summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/CMakeLists.txt272
-rw-r--r--modules/botserv/CMakeLists.txt1
-rw-r--r--modules/botserv/assign.cpp291
-rw-r--r--modules/botserv/autoassign.cpp48
-rw-r--r--modules/botserv/badwords.cpp472
-rw-r--r--modules/botserv/bot.cpp427
-rw-r--r--modules/botserv/botlist.cpp100
-rw-r--r--modules/botserv/control.cpp176
-rw-r--r--modules/botserv/info.cpp118
-rw-r--r--modules/botserv/kick.cpp1743
-rw-r--r--modules/botserv/main/CMakeLists.txt1
-rw-r--r--modules/botserv/main/botserv.cpp (renamed from modules/pseudoclients/botserv.cpp)108
-rw-r--r--modules/botserv/set.cpp (renamed from modules/commands/bs_set.cpp)115
-rw-r--r--modules/bs_autoassign.cpp36
-rw-r--r--modules/chanserv/CMakeLists.txt2
-rw-r--r--modules/chanserv/access.cpp922
-rw-r--r--modules/chanserv/akick.cpp735
-rw-r--r--modules/chanserv/ban.cpp275
-rw-r--r--modules/chanserv/clone.cpp235
-rw-r--r--modules/chanserv/drop.cpp105
-rw-r--r--modules/chanserv/enforce.cpp (renamed from modules/commands/cs_enforce.cpp)129
-rw-r--r--modules/chanserv/entrymsg.cpp285
-rw-r--r--modules/chanserv/flags.cpp541
-rw-r--r--modules/chanserv/getkey.cpp83
-rw-r--r--modules/chanserv/info.cpp101
-rw-r--r--modules/chanserv/invite.cpp117
-rw-r--r--modules/chanserv/kick.cpp160
-rw-r--r--modules/chanserv/list.cpp274
-rw-r--r--modules/chanserv/log.cpp466
-rw-r--r--modules/chanserv/main/CMakeLists.txt1
-rw-r--r--modules/chanserv/main/chanaccess.cpp120
-rw-r--r--modules/chanserv/main/chanaccesstype.h42
-rw-r--r--modules/chanserv/main/channel.cpp401
-rw-r--r--modules/chanserv/main/channel.h96
-rw-r--r--modules/chanserv/main/channeltype.cpp64
-rw-r--r--modules/chanserv/main/channeltype.h53
-rw-r--r--modules/chanserv/main/chanserv.cpp607
-rw-r--r--modules/chanserv/main/level.cpp52
-rw-r--r--modules/chanserv/main/level.h40
-rw-r--r--modules/chanserv/main/leveltype.h35
-rw-r--r--modules/chanserv/main/mode.cpp52
-rw-r--r--modules/chanserv/main/mode.h40
-rw-r--r--modules/chanserv/main/modetype.h35
-rw-r--r--modules/chanserv/mode.cpp1011
-rw-r--r--modules/chanserv/register.cpp174
-rw-r--r--modules/chanserv/seen.cpp (renamed from modules/commands/cs_seen.cpp)170
-rw-r--r--modules/chanserv/set.cpp1291
-rw-r--r--modules/chanserv/set_misc.cpp227
-rw-r--r--modules/chanserv/status.cpp123
-rw-r--r--modules/chanserv/statusupdate.cpp (renamed from modules/cs_statusupdate.cpp)36
-rw-r--r--modules/chanserv/suspend.cpp350
-rw-r--r--modules/chanserv/sync.cpp85
-rw-r--r--modules/chanserv/topic.cpp289
-rw-r--r--modules/chanserv/unban.cpp (renamed from modules/commands/cs_unban.cpp)66
-rw-r--r--modules/chanserv/updown.cpp (renamed from modules/commands/cs_updown.cpp)124
-rw-r--r--modules/chanserv/xop.cpp668
-rw-r--r--modules/commands/bs_assign.cpp256
-rw-r--r--modules/commands/bs_badwords.cpp469
-rw-r--r--modules/commands/bs_bot.cpp385
-rw-r--r--modules/commands/bs_botlist.cpp82
-rw-r--r--modules/commands/bs_control.cpp146
-rw-r--r--modules/commands/bs_info.cpp134
-rw-r--r--modules/commands/bs_kick.cpp1474
-rw-r--r--modules/commands/cs_access.cpp904
-rw-r--r--modules/commands/cs_akick.cpp572
-rw-r--r--modules/commands/cs_ban.cpp248
-rw-r--r--modules/commands/cs_clone.cpp261
-rw-r--r--modules/commands/cs_drop.cpp95
-rw-r--r--modules/commands/cs_entrymsg.cpp289
-rw-r--r--modules/commands/cs_flags.cpp504
-rw-r--r--modules/commands/cs_getkey.cpp73
-rw-r--r--modules/commands/cs_info.cpp98
-rw-r--r--modules/commands/cs_invite.cpp111
-rw-r--r--modules/commands/cs_kick.cpp147
-rw-r--r--modules/commands/cs_list.cpp266
-rw-r--r--modules/commands/cs_log.cpp399
-rw-r--r--modules/commands/cs_mode.cpp1012
-rw-r--r--modules/commands/cs_register.cpp124
-rw-r--r--modules/commands/cs_set.cpp1356
-rw-r--r--modules/commands/cs_set_misc.cpp218
-rw-r--r--modules/commands/cs_status.cpp125
-rw-r--r--modules/commands/cs_suspend.cpp285
-rw-r--r--modules/commands/cs_sync.cpp66
-rw-r--r--modules/commands/cs_topic.cpp270
-rw-r--r--modules/commands/cs_xop.cpp635
-rw-r--r--modules/commands/gl_global.cpp60
-rw-r--r--modules/commands/greet.cpp216
-rw-r--r--modules/commands/hs_del.cpp113
-rw-r--r--modules/commands/hs_group.cpp117
-rw-r--r--modules/commands/hs_off.cpp69
-rw-r--r--modules/commands/hs_on.cpp75
-rw-r--r--modules/commands/hs_request.cpp394
-rw-r--r--modules/commands/hs_set.cpp229
-rw-r--r--modules/commands/ms_cancel.cpp81
-rw-r--r--modules/commands/ms_check.cpp87
-rw-r--r--modules/commands/ms_del.cpp151
-rw-r--r--modules/commands/ms_ignore.cpp132
-rw-r--r--modules/commands/ms_info.cpp231
-rw-r--r--modules/commands/ms_list.cpp164
-rw-r--r--modules/commands/ms_read.cpp216
-rw-r--r--modules/commands/ms_rsend.cpp104
-rw-r--r--modules/commands/ms_send.cpp88
-rw-r--r--modules/commands/ms_sendall.cpp70
-rw-r--r--modules/commands/ms_set.cpp315
-rw-r--r--modules/commands/ms_staff.cpp67
-rw-r--r--modules/commands/ns_access.cpp206
-rw-r--r--modules/commands/ns_ajoin.cpp405
-rw-r--r--modules/commands/ns_alist.cpp149
-rw-r--r--modules/commands/ns_cert.cpp407
-rw-r--r--modules/commands/ns_drop.cpp86
-rw-r--r--modules/commands/ns_getemail.cpp74
-rw-r--r--modules/commands/ns_getpass.cpp74
-rw-r--r--modules/commands/ns_group.cpp384
-rw-r--r--modules/commands/ns_identify.cpp128
-rw-r--r--modules/commands/ns_info.cpp282
-rw-r--r--modules/commands/ns_list.cpp301
-rw-r--r--modules/commands/ns_logout.cpp91
-rw-r--r--modules/commands/ns_recover.cpp295
-rw-r--r--modules/commands/ns_register.cpp429
-rw-r--r--modules/commands/ns_resetpass.cpp143
-rw-r--r--modules/commands/ns_set.cpp1341
-rw-r--r--modules/commands/ns_set_misc.cpp233
-rw-r--r--modules/commands/ns_suspend.cpp288
-rw-r--r--modules/commands/ns_update.cpp62
-rw-r--r--modules/commands/os_akill.cpp483
-rw-r--r--modules/commands/os_chankill.cpp119
-rw-r--r--modules/commands/os_dns.cpp949
-rw-r--r--modules/commands/os_forbid.cpp559
-rw-r--r--modules/commands/os_ignore.cpp416
-rw-r--r--modules/commands/os_info.cpp290
-rw-r--r--modules/commands/os_jupe.cpp80
-rw-r--r--modules/commands/os_kick.cpp84
-rw-r--r--modules/commands/os_login.cpp132
-rw-r--r--modules/commands/os_news.cpp464
-rw-r--r--modules/commands/os_oper.cpp293
-rw-r--r--modules/commands/os_reload.cpp67
-rw-r--r--modules/commands/os_session.cpp737
-rw-r--r--modules/commands/os_stats.cpp276
-rw-r--r--modules/commands/os_sxline.cpp732
-rw-r--r--modules/commands/os_update.cpp52
-rw-r--r--modules/database/CMakeLists.txt2
-rw-r--r--modules/database/db_flatfile.cpp415
-rw-r--r--modules/database/db_redis.cpp644
-rw-r--r--modules/database/db_sql.cpp261
-rw-r--r--modules/database/db_sql_live.cpp265
-rw-r--r--modules/database/flatfile.cpp218
-rw-r--r--modules/database/old.cpp (renamed from modules/database/db_old.cpp)614
-rw-r--r--modules/database/redis.cpp445
-rw-r--r--modules/database/sql.cpp445
-rw-r--r--modules/dns.cpp (renamed from modules/m_dns.cpp)113
-rw-r--r--modules/dnsbl.cpp (renamed from modules/m_dnsbl.cpp)54
-rw-r--r--modules/encryption/CMakeLists.txt1
-rw-r--r--modules/encryption/bcrypt.cpp (renamed from modules/encryption/enc_bcrypt.cpp)59
-rw-r--r--modules/encryption/enc_none.cpp69
-rw-r--r--modules/encryption/md5.cpp (renamed from modules/encryption/enc_md5.cpp)67
-rw-r--r--modules/encryption/none.cpp78
-rw-r--r--modules/encryption/old.cpp (renamed from modules/encryption/enc_old.cpp)65
-rw-r--r--modules/encryption/sha1.cpp (renamed from modules/encryption/enc_sha1.cpp)61
-rw-r--r--modules/encryption/sha256.cpp (renamed from modules/encryption/enc_sha256.cpp)65
-rw-r--r--modules/extra/ldap.cpp (renamed from modules/extra/m_ldap.cpp)45
-rw-r--r--modules/extra/ldap_authentication.cpp (renamed from modules/extra/m_ldap_authentication.cpp)111
-rw-r--r--modules/extra/ldap_oper.cpp (renamed from modules/extra/m_ldap_oper.cpp)55
-rw-r--r--modules/extra/m_regex_pcre.cpp83
-rw-r--r--modules/extra/m_regex_posix.cpp84
-rw-r--r--modules/extra/m_regex_tre.cpp85
-rw-r--r--modules/extra/m_sql_oper.cpp180
-rw-r--r--modules/extra/m_sqlite.cpp326
-rw-r--r--modules/extra/mysql.cpp (renamed from modules/extra/m_mysql.cpp)302
-rw-r--r--modules/extra/sasl_dh-aes.cpp184
-rw-r--r--modules/extra/sasl_dh-blowfish.cpp194
-rw-r--r--modules/extra/sql_authentication.cpp (renamed from modules/extra/m_sql_authentication.cpp)50
-rw-r--r--modules/extra/sql_log.cpp (renamed from modules/extra/m_sql_log.cpp)28
-rw-r--r--modules/extra/sql_oper.cpp194
-rw-r--r--modules/extra/sqlite.cpp399
-rw-r--r--modules/extra/ssl_gnutls.cpp (renamed from modules/extra/m_ssl_gnutls.cpp)50
-rw-r--r--modules/extra/ssl_openssl.cpp (renamed from modules/extra/m_ssl_openssl.cpp)53
-rw-r--r--modules/extra/stats/chanstats.cpp (renamed from modules/extra/stats/m_chanstats.cpp)88
-rw-r--r--modules/extra/stats/cs_fantasy_stats.cpp18
-rw-r--r--modules/extra/stats/cs_fantasy_top.cpp16
-rw-r--r--modules/extra/stats/irc2sql/irc2sql.cpp20
-rw-r--r--modules/extra/stats/irc2sql/irc2sql.h50
-rw-r--r--modules/fantasy.cpp81
-rw-r--r--modules/global/CMakeLists.txt1
-rw-r--r--modules/global/global.cpp68
-rw-r--r--modules/global/main/CMakeLists.txt1
-rw-r--r--modules/global/main/global.cpp119
-rw-r--r--modules/greet.cpp223
-rw-r--r--modules/help.cpp (renamed from modules/commands/help.cpp)72
-rw-r--r--modules/helpchan.cpp45
-rw-r--r--modules/hostserv/CMakeLists.txt1
-rw-r--r--modules/hostserv/del.cpp111
-rw-r--r--modules/hostserv/group.cpp127
-rw-r--r--modules/hostserv/list.cpp (renamed from modules/commands/hs_list.cpp)69
-rw-r--r--modules/hostserv/main/CMakeLists.txt1
-rw-r--r--modules/hostserv/main/hostserv.cpp148
-rw-r--r--modules/hostserv/off.cpp71
-rw-r--r--modules/hostserv/on.cpp81
-rw-r--r--modules/hostserv/request.cpp432
-rw-r--r--modules/hostserv/set.cpp224
-rw-r--r--modules/httpd.cpp (renamed from modules/m_httpd.cpp)61
-rw-r--r--modules/m_helpchan.cpp32
-rw-r--r--modules/m_proxyscan.cpp379
-rw-r--r--modules/m_sasl.cpp329
-rw-r--r--modules/memoserv/CMakeLists.txt1
-rw-r--r--modules/memoserv/cancel.cpp98
-rw-r--r--modules/memoserv/check.cpp88
-rw-r--r--modules/memoserv/del.cpp153
-rw-r--r--modules/memoserv/ignore.cpp155
-rw-r--r--modules/memoserv/info.cpp227
-rw-r--r--modules/memoserv/list.cpp171
-rw-r--r--modules/memoserv/main/CMakeLists.txt1
-rw-r--r--modules/memoserv/main/ignore.cpp46
-rw-r--r--modules/memoserv/main/ignore.h39
-rw-r--r--modules/memoserv/main/ignoretype.cpp29
-rw-r--r--modules/memoserv/main/ignoretype.h29
-rw-r--r--modules/memoserv/main/memo.cpp85
-rw-r--r--modules/memoserv/main/memo.h53
-rw-r--r--modules/memoserv/main/memoinfo.cpp82
-rw-r--r--modules/memoserv/main/memoinfo.h47
-rw-r--r--modules/memoserv/main/memoinfotype.cpp29
-rw-r--r--modules/memoserv/main/memoinfotype.h29
-rw-r--r--modules/memoserv/main/memoserv.cpp305
-rw-r--r--modules/memoserv/main/memotype.cpp33
-rw-r--r--modules/memoserv/main/memotype.h32
-rw-r--r--modules/memoserv/read.cpp217
-rw-r--r--modules/memoserv/rsend.cpp114
-rw-r--r--modules/memoserv/send.cpp89
-rw-r--r--modules/memoserv/sendall.cpp68
-rw-r--r--modules/memoserv/set.cpp322
-rw-r--r--modules/memoserv/staff.cpp65
-rw-r--r--modules/nickserv/CMakeLists.txt1
-rw-r--r--modules/nickserv/access.cpp272
-rw-r--r--modules/nickserv/ajoin.cpp396
-rw-r--r--modules/nickserv/alist.cpp142
-rw-r--r--modules/nickserv/cert.cpp391
-rw-r--r--modules/nickserv/drop.cpp96
-rw-r--r--modules/nickserv/getemail.cpp70
-rw-r--r--modules/nickserv/group.cpp389
-rw-r--r--modules/nickserv/identify.cpp133
-rw-r--r--modules/nickserv/info.cpp284
-rw-r--r--modules/nickserv/list.cpp299
-rw-r--r--modules/nickserv/logout.cpp109
-rw-r--r--modules/nickserv/main/CMakeLists.txt1
-rw-r--r--modules/nickserv/main/account.cpp136
-rw-r--r--modules/nickserv/main/account.h58
-rw-r--r--modules/nickserv/main/accounttype.cpp60
-rw-r--r--modules/nickserv/main/accounttype.h42
-rw-r--r--modules/nickserv/main/identifyrequest.cpp81
-rw-r--r--modules/nickserv/main/identifyrequest.h33
-rw-r--r--modules/nickserv/main/mode.cpp42
-rw-r--r--modules/nickserv/main/mode.h37
-rw-r--r--modules/nickserv/main/modetype.h33
-rw-r--r--modules/nickserv/main/nick.cpp199
-rw-r--r--modules/nickserv/main/nick.h72
-rw-r--r--modules/nickserv/main/nickserv.cpp693
-rw-r--r--modules/nickserv/main/nicktype.cpp64
-rw-r--r--modules/nickserv/main/nicktype.h51
-rw-r--r--modules/nickserv/maxemail.cpp (renamed from modules/ns_maxemail.cpp)52
-rw-r--r--modules/nickserv/recover.cpp307
-rw-r--r--modules/nickserv/register.cpp445
-rw-r--r--modules/nickserv/resetpass.cpp150
-rw-r--r--modules/nickserv/set.cpp1261
-rw-r--r--modules/nickserv/set_misc.cpp239
-rw-r--r--modules/nickserv/status.cpp (renamed from modules/commands/ns_status.cpp)43
-rw-r--r--modules/nickserv/suspend.cpp348
-rw-r--r--modules/nickserv/update.cpp71
-rw-r--r--modules/operserv/CMakeLists.txt1
-rw-r--r--modules/operserv/akill.cpp455
-rw-r--r--modules/operserv/chankill.cpp134
-rw-r--r--modules/operserv/config.cpp (renamed from modules/commands/os_config.cpp)53
-rw-r--r--modules/operserv/defcon.cpp (renamed from modules/commands/os_defcon.cpp)166
-rw-r--r--modules/operserv/dns.cpp1051
-rw-r--r--modules/operserv/forbid.cpp602
-rw-r--r--modules/operserv/ignore.cpp410
-rw-r--r--modules/operserv/info.cpp280
-rw-r--r--modules/operserv/jupe.cpp80
-rw-r--r--modules/operserv/kick.cpp89
-rw-r--r--modules/operserv/kill.cpp (renamed from modules/commands/os_kill.cpp)38
-rw-r--r--modules/operserv/list.cpp (renamed from modules/commands/os_list.cpp)72
-rw-r--r--modules/operserv/login.cpp156
-rw-r--r--modules/operserv/logsearch.cpp (renamed from modules/commands/os_logsearch.cpp)65
-rw-r--r--modules/operserv/main/CMakeLists.txt1
-rw-r--r--modules/operserv/main/operserv.cpp329
-rw-r--r--modules/operserv/mode.cpp (renamed from modules/commands/os_mode.cpp)55
-rw-r--r--modules/operserv/modinfo.cpp (renamed from modules/commands/os_modinfo.cpp)71
-rw-r--r--modules/operserv/module.cpp (renamed from modules/commands/os_module.cpp)81
-rw-r--r--modules/operserv/news.cpp478
-rw-r--r--modules/operserv/noop.cpp (renamed from modules/commands/os_noop.cpp)53
-rw-r--r--modules/operserv/oline.cpp (renamed from modules/commands/os_oline.cpp)42
-rw-r--r--modules/operserv/oper.cpp257
-rw-r--r--modules/operserv/reload.cpp70
-rw-r--r--modules/operserv/session.cpp785
-rw-r--r--modules/operserv/set.cpp (renamed from modules/commands/os_set.cpp)108
-rw-r--r--modules/operserv/shutdown.cpp (renamed from modules/commands/os_shutdown.cpp)51
-rw-r--r--modules/operserv/stats.cpp327
-rw-r--r--modules/operserv/svs.cpp (renamed from modules/commands/os_svs.cpp)77
-rw-r--r--modules/operserv/sxline.cpp701
-rw-r--r--modules/operserv/update.cpp57
-rw-r--r--modules/protocol/CMakeLists.txt2
-rw-r--r--modules/protocol/bahamut.cpp272
-rw-r--r--modules/protocol/charybdis.cpp439
-rw-r--r--modules/protocol/hybrid.cpp662
-rw-r--r--modules/protocol/inspircd12.cpp1383
-rw-r--r--modules/protocol/inspircd20.cpp1581
-rw-r--r--modules/protocol/ngircd.cpp239
-rw-r--r--modules/protocol/plexus.cpp432
-rw-r--r--modules/protocol/ratbox.cpp364
-rw-r--r--modules/protocol/unreal.cpp824
-rw-r--r--modules/protocol/unreal4.cpp1468
-rw-r--r--modules/pseudoclients/chanserv.cpp479
-rw-r--r--modules/pseudoclients/global.cpp96
-rw-r--r--modules/pseudoclients/hostserv.cpp122
-rw-r--r--modules/pseudoclients/memoserv.cpp223
-rw-r--r--modules/pseudoclients/nickserv.cpp575
-rw-r--r--modules/pseudoclients/operserv.cpp299
-rw-r--r--modules/redis.cpp (renamed from modules/m_redis.cpp)88
-rw-r--r--modules/rewrite.cpp (renamed from modules/m_rewrite.cpp)42
-rw-r--r--modules/sasl.cpp391
-rw-r--r--modules/third/language/CMakeLists.txt10
-rw-r--r--modules/webcpanel/pages/chanserv/access.cpp54
-rw-r--r--modules/webcpanel/pages/chanserv/access.h22
-rw-r--r--modules/webcpanel/pages/chanserv/akick.cpp47
-rw-r--r--modules/webcpanel/pages/chanserv/akick.h22
-rw-r--r--modules/webcpanel/pages/chanserv/drop.cpp35
-rw-r--r--modules/webcpanel/pages/chanserv/drop.h20
-rw-r--r--modules/webcpanel/pages/chanserv/info.cpp20
-rw-r--r--modules/webcpanel/pages/chanserv/info.h20
-rw-r--r--modules/webcpanel/pages/chanserv/modes.cpp36
-rw-r--r--modules/webcpanel/pages/chanserv/modes.h22
-rw-r--r--modules/webcpanel/pages/chanserv/set.cpp112
-rw-r--r--modules/webcpanel/pages/chanserv/set.h22
-rw-r--r--modules/webcpanel/pages/chanserv/utils.cpp39
-rw-r--r--modules/webcpanel/pages/chanserv/utils.h20
-rw-r--r--modules/webcpanel/pages/confirm.cpp18
-rw-r--r--modules/webcpanel/pages/confirm.h20
-rw-r--r--modules/webcpanel/pages/hostserv/request.cpp26
-rw-r--r--modules/webcpanel/pages/hostserv/request.h20
-rw-r--r--modules/webcpanel/pages/index.cpp47
-rw-r--r--modules/webcpanel/pages/index.h20
-rw-r--r--modules/webcpanel/pages/logout.cpp20
-rw-r--r--modules/webcpanel/pages/logout.h20
-rw-r--r--modules/webcpanel/pages/memoserv/memos.cpp92
-rw-r--r--modules/webcpanel/pages/memoserv/memos.h20
-rw-r--r--modules/webcpanel/pages/nickserv/access.cpp29
-rw-r--r--modules/webcpanel/pages/nickserv/access.h20
-rw-r--r--modules/webcpanel/pages/nickserv/alist.cpp50
-rw-r--r--modules/webcpanel/pages/nickserv/alist.h20
-rw-r--r--modules/webcpanel/pages/nickserv/cert.cpp33
-rw-r--r--modules/webcpanel/pages/nickserv/cert.h20
-rw-r--r--modules/webcpanel/pages/nickserv/info.cpp96
-rw-r--r--modules/webcpanel/pages/nickserv/info.h20
-rw-r--r--modules/webcpanel/pages/operserv/akill.cpp50
-rw-r--r--modules/webcpanel/pages/operserv/akill.h22
-rw-r--r--modules/webcpanel/pages/register.cpp18
-rw-r--r--modules/webcpanel/pages/register.h20
-rw-r--r--modules/webcpanel/static_fileserver.cpp20
-rw-r--r--modules/webcpanel/static_fileserver.h20
-rw-r--r--modules/webcpanel/template_fileserver.cpp26
-rw-r--r--modules/webcpanel/template_fileserver.h18
-rw-r--r--modules/webcpanel/templates/default/confirm.html2
-rw-r--r--modules/webcpanel/templates/default/footer.html2
-rw-r--r--modules/webcpanel/templates/default/login.html2
-rw-r--r--modules/webcpanel/templates/default/register.html2
-rw-r--r--modules/webcpanel/webcpanel.cpp97
-rw-r--r--modules/webcpanel/webcpanel.h46
-rw-r--r--modules/xmlrpc.cpp (renamed from modules/m_xmlrpc.cpp)39
-rw-r--r--modules/xmlrpc_main.cpp (renamed from modules/m_xmlrpc_main.cpp)58
367 files changed, 39755 insertions, 37578 deletions
diff --git a/modules/CMakeLists.txt b/modules/CMakeLists.txt
index 02139a0e5..d48a57234 100644
--- a/modules/CMakeLists.txt
+++ b/modules/CMakeLists.txt
@@ -1,17 +1,17 @@
# If using Windows, add the MODULE_COMPILE define
if(WIN32)
add_definitions(-DMODULE_COMPILE)
-endif(WIN32)
+endif()
macro(build_modules SRC)
if(NOT ${SRC} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR} AND EXISTS "${SRC}/CMakeLists.txt")
add_subdirectory("${SRC}")
- else(NOT ${SRC} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR} AND EXISTS "${SRC}/CMakeLists.txt")
+ else()
file(GLOB MODULES_SRCS "${SRC}/*")
foreach(MODULE_SRC ${MODULES_SRCS})
if(IS_DIRECTORY "${MODULE_SRC}")
build_modules("${MODULE_SRC}")
- else(IS_DIRECTORY "${MODULE_SRC}")
+ else()
string(REGEX MATCH "\\.c$" ANOPE18MODULE ${MODULE_SRC})
if(ANOPE18MODULE)
message(FATAL_ERROR "Anope 1 modules are not compatible with Anope 2!\nOffending module: ${MODULE_SRC}")
@@ -22,15 +22,16 @@ macro(build_modules SRC)
file(RELATIVE_PATH FNAME ${SRC} ${MODULE_SRC})
# Convert the real source file extension to have a .so extension
- string(REGEX REPLACE "\\.cpp$" ".so" SO ${FNAME})
- # Temporary variable for the current source's include directories
- set(TEMP_INCLUDES)
- # Calculate the header file dependencies for the given source file
- calculate_depends(${MODULE_SRC} TEMP_INCLUDES)
- # If there were some extra include directories, add them to the list
- if(TEMP_INCLUDES)
- append_to_list(EXTRA_INCLUDES ${TEMP_INCLUDES})
- endif(TEMP_INCLUDES)
+ string(REGEX REPLACE "\\.cpp$" "" SO ${FNAME})
+
+ file(RELATIVE_PATH DEST "${Anope_SOURCE_DIR}/modules" ${SRC})
+ if(DEST)
+ set(TARG "anope_${DEST}.${SO}")
+ set(OUT_NAME "${DEST}_${SO}")
+ else()
+ set(TARG "anope_${SO}")
+ set(OUT_NAME "${SO}")
+ endif()
# Reset linker flags
set(TEMP_LDFLAGS)
@@ -38,133 +39,146 @@ macro(build_modules SRC)
set(TEMP_DEPENDENCIES)
# Calculate the library dependencies for the given source file
calculate_libraries(${MODULE_SRC} TEMP_LDFLAGS TEMP_DEPENDENCIES)
- # Reset has_function
- set(HAS_FUNCTION)
- # Check the function dependencies for the given source file
- check_functions(${MODULE_SRC} HAS_FUNCTION)
- # Only continue if this module has all of the required functions
- if(HAS_FUNCTION)
- # For Visual Studio only, include win32_memory static library, required to override Visual Studio's overrides of the new/delete operators
- if(MSVC)
- set(WIN32_MEMORY win32_memory)
- else(MSVC)
- set(WIN32_MEMORY)
- endif(MSVC)
- # Generate the module and set its linker flags, also set it to depend on the main Anope executable to be built beforehand
- add_library(${SO} MODULE ${MODULE_SRC})
+ # For Visual Studio only, include win32_memory static library, required to override Visual Studio's overrides of the new/delete operators
+ if(MSVC)
+ set(WIN32_MEMORY win32_memory)
+ else()
+ set(WIN32_MEMORY)
+ endif()
+ # Generate the module and set its linker flags, also set it to depend on the main Anope executable to be built beforehand
+ add_library(${TARG} SHARED ${MODULE_SRC})
+ if(WIN32)
# Windows requires this because it's weird
- if(WIN32)
- set(WIN32_NO_LIBS "/nodefaultlib:\"libcmt.lib\" /OPT:NOREF")
- else(WIN32)
- set(WIN32_NO_LIBS)
- endif(WIN32)
- set_target_properties(${SO} PROPERTIES LINKER_LANGUAGE CXX PREFIX "" SUFFIX "" LINK_FLAGS "${TEMP_LDFLAGS} ${WIN32_NO_LIBS}" INSTALL_RPATH_USE_LINK_PATH ON BUILD_WITH_INSTALL_RPATH ON)
- add_dependencies(${SO} ${PROGRAM_NAME})
- if(GETTEXT_FOUND)
- add_dependencies(${SO} module_language)
- endif(GETTEXT_FOUND)
- target_link_libraries(${SO} ${TEMP_DEPENDENCIES})
- # For Windows only, have the module link to the export library of Anope as well as wsock32 and Ws2_32 libraries (most of the modules probably don't need this, but this is to be on the safe side), also set its version
- if(WIN32)
- target_link_libraries(${SO} ${PROGRAM_NAME} wsock32 Ws2_32 ${WIN32_MEMORY})
- set_target_properties(${PROGRAM_NAME} PROPERTIES VERSION "${VERSION_DOTTED}")
- else(WIN32)
- if(APPLE)
- target_link_libraries(${SO} ${PROGRAM_NAME})
- endif(APPLE)
- endif(WIN32)
- # Set the module to be installed to the module directory under the data directory
- install(TARGETS ${SO} DESTINATION ${LIB_DIR}/modules)
- endif(HAS_FUNCTION)
- endif(CPP)
- endif(IS_DIRECTORY "${MODULE_SRC}")
- endforeach(MODULE_SRC ${MODULES_SRCS})
- endif(NOT ${SRC} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR} AND EXISTS "${SRC}/CMakeLists.txt")
-endmacro(build_modules)
+ set(MOD_EXTRA_LDFLAGS "/nodefaultlib:\"libcmt.lib\" /OPT:NOREF")
+ elseif(APPLE)
+ # Mark undefined symbols as having to be looked up at runtime
+ set(MOD_EXTRA_LDFLAGS "-undefined dynamic_lookup")
+ endif()
+ set_target_properties(${TARG} PROPERTIES LINKER_LANGUAGE CXX PREFIX "" OUTPUT_NAME "${OUT_NAME}" LINK_FLAGS "${TEMP_LDFLAGS} ${MOD_EXTRA_LDFLAGS}" INSTALL_RPATH_USE_LINK_PATH ON BUILD_WITH_INSTALL_RPATH ON)
+ add_dependencies(${TARG} ${PROGRAM_NAME})
+ if(GETTEXT_FOUND)
+ add_dependencies(${TARG} module_language)
+ endif()
+ target_link_libraries(${TARG} ${TEMP_DEPENDENCIES})
+ # For Windows only, have the module link to the export library of Anope as well as wsock32 and Ws2_32 libraries (most of the modules probably don't need this, but this is to be on the safe side), also set its version
+ if(WIN32)
+ target_link_libraries(${TARG} ${PROGRAM_NAME} wsock32 Ws2_32 ${WIN32_MEMORY})
+ set_target_properties(${PROGRAM_NAME} PROPERTIES VERSION "${VERSION_DOTTED}")
+ elseif(APPLE)
+ target_link_libraries(${TARG} ${PROGRAM_NAME})
+ endif()
+ # Set the module to be installed to the module directory under the data directory
+ install(TARGETS ${TARG} DESTINATION ${LIB_DIR}/modules)
+ endif()
+ endif()
+ endforeach()
+ endif()
+endmacro()
+
+macro(build_modules_dependencies SRC)
+ file(GLOB MODULES_SRCS "${SRC}/*")
+ foreach(MODULE_SRC ${MODULES_SRCS})
+ if(NOT IS_DIRECTORY "${MODULE_SRC}")
+ string(REGEX MATCH "\\.cpp$" CPP ${MODULE_SRC})
+ if(CPP)
+ file(RELATIVE_PATH FNAME ${SRC} ${MODULE_SRC})
+ # Convert the real source file extension to have a .so extension
+ string(REGEX REPLACE "\\.cpp$" "" SO ${FNAME})
+
+ file(RELATIVE_PATH DEST "${Anope_SOURCE_DIR}/modules" ${SRC})
+ if(DEST)
+ set(TARG "anope_${DEST}.${SO}")
+ else()
+ set(TARG "anope_${SO}")
+ endif()
+
+ set(DEPS)
+ calculate_dependencies(${MODULE_SRC} DEPS)
+ foreach(DEP ${DEPS})
+ target_link_libraries(${TARG} ${DEP})
+ endforeach()
+ endif()
+ endif()
+ endforeach()
+endmacro()
macro(build_subdir)
file(GLOB_RECURSE MODULES_SUBDIR_SRCS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.cpp")
- sort_list(MODULES_SUBDIR_SRCS)
+ list(SORT MODULES_SUBDIR_SRCS)
- GET_FILENAME_COMPONENT(FOLDER_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME)
- set(SO "${FOLDER_NAME}.so")
+ file(RELATIVE_PATH DEST "${Anope_SOURCE_DIR}/modules" ${CMAKE_CURRENT_SOURCE_DIR})
+ get_filename_component(DIR_NAME ${DEST} DIRECTORY)
+ get_filename_component(FOLDER_NAME ${DEST} NAME)
+ if(DIR_NAME)
+ set(SO "anope_${DIR_NAME}.${FOLDER_NAME}")
+ else()
+ set(SO "anope_${FOLDER_NAME}")
+ endif()
# Set all the files to use C++ as well as set their compile flags (use the module-specific compile flags, though)
set_source_files_properties(${MODULES_SUBDIR_SRCS} PROPERTIES LANGUAGE CXX COMPILE_FLAGS "${CXXFLAGS}")
- set(HAS_FUNCTION TRUE)
-
+ set(DEPS)
# Iterate through the source files in the subdirectory
foreach(SRC ${MODULES_SUBDIR_SRCS})
- if(HAS_FUNCTION)
- # Temporary variable for the current source's include directories
- set(TEMP_INCLUDES)
- # Calculate the header file dependencies for the given source file
- calculate_depends(${SRC} TEMP_INCLUDES)
- # If there were some extra include directories, add them to the list
- if(TEMP_INCLUDES)
- include_directories(${TEMP_INCLUDES})
- endif(TEMP_INCLUDES)
-
- # Reset linker flags
- set(TEMP_LDFLAGS)
- # Reset extra dependencies
- set(TEMP_DEPENDENCIES)
- # Calculate the library dependencies for the given source file
- calculate_libraries(${SRC} SKIP_LIBRARIES MODULE TEMP_LDFLAGS TEMP_DEPENDENCIES)
- # Check the function dependencies for the given source file
- check_functions(${SRC} HAS_FUNCTION)
-
- # Append this source file's linker flags to the subdirectoy's linker flags, if there are any to append
- if(TEMP_DEPENDENCIES)
- append_to_list(SUBDIR_EXTRA_DEPENDS ${TEMP_DEPDENCIES})
- endif(TEMP_DEPENDENCIES)
- endif(HAS_FUNCTION)
- endforeach(SRC ${MODULES_SUBDIR_SRCS})
-
- # Continue if library and function requirements are met
- if(HAS_FUNCTION)
- # Remove duplicates from the linker flags
- if(SUBDIR_LDFLAGS)
- remove_list_duplicates(SUBDIR_LDFLAGS)
- endif(SUBDIR_LDFLAGS)
-
- # Remove duplicates from the extra dependencies
- if(SUBDIR_EXTRA_DEPENDS)
- remove_list_duplicates(SUBDIR_EXTRA_DEPENDS)
- endif(SUBDIR_EXTRA_DEPENDS)
-
- # For Visual Studio only, include win32_memory static library, required to override Visual Studio's overrides of the new/delete operators
- if(MSVC)
- set(WIN32_MEMORY win32_memory)
- else(MSVC)
- set(WIN32_MEMORY)
- endif(MSVC)
-
- # Generate the module and set it's linker flags, also set it to depend on the main Anope executable to be built beforehand
- add_library(${SO} MODULE ${MODULES_SUBDIR_SRCS})
- set_target_properties(${SO} PROPERTIES LINKER_LANGUAGE CXX PREFIX "" SUFFIX "" LINK_FLAGS "${SUBDIR_LDFLAGS}" INSTALL_RPATH_USE_LINK_PATH ON BUILD_WITH_INSTALL_RPATH ON)
- add_dependencies(${SO} ${PROGRAM_NAME})
- if(GETTEXT_FOUND)
- add_dependencies(${SO} module_language)
- endif(GETTEXT_FOUND)
- target_link_libraries(${SO} ${SUBDIR_EXTRA_DEPENDS})
- # For Windows only, have the module link to the export library of Anope as well as wsock32 and Ws2_32 libraries (most of the modules probably don't need this, but this is to be on the safe side), also set it's version
- if(WIN32)
- target_link_libraries(${SO} ${PROGRAM_NAME} wsock32 Ws2_32 ${WIN32_MEMORY})
- set_target_properties(${PROGRAM_NAME} PROPERTIES VERSION "${VERSION_DOTTED}")
- else(WIN32)
- if(APPLE)
- target_link_libraries(${SO} ${PROGRAM_NAME})
- endif(APPLE)
- endif(WIN32)
-
- # Set the module to be installed to the module directory under the data directory
- install(TARGETS ${SO} DESTINATION ${LIB_DIR}/modules)
-
- endif(HAS_FUNCTION)
-endmacro(build_subdir)
-
-include_directories(${CMAKE_CURRENT_SOURCE_DIR})
+ # Reset linker flags
+ set(TEMP_LDFLAGS)
+ # Reset extra dependencies
+ set(TEMP_DEPENDENCIES)
+ # Calculate the library dependencies for the given source file
+ calculate_libraries(${SRC} SKIP_LIBRARIES MODULE TEMP_LDFLAGS TEMP_DEPENDENCIES)
+ calculate_dependencies(${SRC} DEPS)
+
+ # Append this source file's linker flags to the subdirectoy's linker flags, if there are any to append
+ if(TEMP_DEPENDENCIES)
+ append_to_list(SUBDIR_EXTRA_DEPENDS ${TEMP_DEPDENCIES})
+ endif()
+ endforeach()
+
+ # Remove duplicates from the linker flags
+ if(SUBDIR_LDFLAGS)
+ list(REMOVE_DUPLICATES SUBDIR_LDFLAGS)
+ endif()
+
+ # Remove duplicates from the extra dependencies
+ if(SUBDIR_EXTRA_DEPENDS)
+ list(REMOVE_DUPLICATES SUBDIR_EXTRA_DEPENDS)
+ endif()
+
+ # For Visual Studio only, include win32_memory static library, required to override Visual Studio's overrides of the new/delete operators
+ if(MSVC)
+ set(WIN32_MEMORY win32_memory)
+ else()
+ set(WIN32_MEMORY)
+ endif()
+
+ if(APPLE)
+ # Mark undefined symbols as having to be looked up at runtime
+ set(MOD_EXTRA_LDFLAGS "-undefined dynamic_lookup")
+ endif(APPLE)
+
+ # Generate the module and set its linker flags, also set it to depend on the main Anope executable to be built beforehand
+ add_library(${SO} SHARED ${MODULES_SUBDIR_SRCS})
+ set_target_properties(${SO} PROPERTIES LINKER_LANGUAGE CXX PREFIX "" LINK_FLAGS "${SUBDIR_LDFLAGS} ${MOD_EXTRA_LDFLAGS}" INSTALL_RPATH_USE_LINK_PATH ON BUILD_WITH_INSTALL_RPATH ON OUTPUT_NAME "${DIR_NAME}_${FOLDER_NAME}")
+ add_dependencies(${SO} ${PROGRAM_NAME})
+ if(GETTEXT_FOUND)
+ add_dependencies(${SO} module_language)
+ endif()
+ target_link_libraries(${SO} ${SUBDIR_EXTRA_DEPENDS})
+ foreach(DEP ${DEPS})
+ target_link_libraries(${SO} ${DEP})
+ endforeach()
+ # For Windows only, have the module link to the export library of Anope as well as wsock32 and Ws2_32 libraries (most of the modules probably don't need this, but this is to be on the safe side), also set its version
+ if(WIN32)
+ target_link_libraries(${SO} ${PROGRAM_NAME} wsock32 Ws2_32 ${WIN32_MEMORY})
+ set_target_properties(${PROGRAM_NAME} PROPERTIES VERSION "${VERSION_DOTTED}")
+ elseif(APPLE)
+ target_link_libraries(${SO} ${PROGRAM_NAME})
+ endif()
+
+ install(TARGETS ${SO} DESTINATION ${LIB_DIR}/modules)
+endmacro()
+
build_modules(${CMAKE_CURRENT_SOURCE_DIR})
+build_modules_dependencies(${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/modules/botserv/CMakeLists.txt b/modules/botserv/CMakeLists.txt
new file mode 100644
index 000000000..cd225a94d
--- /dev/null
+++ b/modules/botserv/CMakeLists.txt
@@ -0,0 +1 @@
+build_modules(${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/modules/botserv/assign.cpp b/modules/botserv/assign.cpp
new file mode 100644
index 000000000..eb6b8da10
--- /dev/null
+++ b/modules/botserv/assign.cpp
@@ -0,0 +1,291 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/botserv/info.h"
+
+class CommandBSAssign : public Command
+{
+ public:
+ CommandBSAssign(Module *creator) : Command(creator, "botserv/assign", 2, 2)
+ {
+ this->SetDesc(_("Assigns a bot to a channel"));
+ this->SetSyntax(_("\037channel\037 \037nickname\037"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &chan = params[0];
+ const Anope::string &nick = params[1];
+
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Sorry, bot assignment is temporarily disabled."));
+ return;
+ }
+
+ ChanServ::Channel *ci = ChanServ::Find(chan);
+ if (ci == NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
+ return;
+ }
+
+ ServiceBot *bi = ServiceBot::Find(nick, true);
+ if (!bi)
+ {
+ source.Reply(_("Bot \002{0}\002 does not exist."), nick);
+ return;
+ }
+
+ ChanServ::AccessGroup access = source.AccessFor(ci);
+ if (!access.HasPriv("ASSIGN") && !source.HasPriv("botserv/administration"))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "ASSIGN", ci->GetName());
+ return;
+ }
+
+ if (ci->HasFieldS("BS_NOBOT"))
+ {
+ source.Reply(_("Access denied. \002{0}\002 may not have a bot assigned to it because a Services Operator has disallowed it."), ci->GetName());
+ return;
+ }
+
+ if (bi->bi->GetOperOnly() && !source.HasPriv("botserv/administration"))
+ {
+ source.Reply(_("Access denied. Bot \002{0}\002 is for operators only."), bi->nick);
+ return;
+ }
+
+ if (ci->GetBot() == bi)
+ {
+ source.Reply(_("Bot \002{0}\002 is already assigned to \002{1}\002."), ci->GetBot()->nick, ci->GetName());
+ return;
+ }
+
+ bool override = !access.HasPriv("ASSIGN");
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "for " << bi->nick;
+
+ bi->Assign(source.GetUser(), ci);
+ source.Reply(_("Bot \002{0}\002 has been assigned to \002{1}\002."), bi->nick, ci->GetName());
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Assigns the bot \037nickname\037 to \037channel\037."
+ " You can then configure the bot for the channel so it fits your needs.\n"
+ "\n"
+ "Use of this command requires the \002{0}\002 privilege on \037channel\037."
+ "\n"
+ "Example:\n"
+ " {command} #anope Botox\n"
+ " Assigns the bot Botox to #anope.\n"),
+ "ASSIGN", "command"_kw = source.command);
+ return true;
+ }
+};
+
+class CommandBSUnassign : public Command
+{
+ public:
+ CommandBSUnassign(Module *creator) : Command(creator, "botserv/unassign", 1, 1)
+ {
+ this->SetDesc(_("Unassigns a bot from a channel"));
+ this->SetSyntax(_("\037channel\037"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &chan = params[0];
+
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Sorry, bot assignment is temporarily disabled."));
+ return;
+ }
+
+ ChanServ::Channel *ci = ChanServ::Find(chan);
+ if (ci == NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
+ return;
+ }
+
+ ChanServ::AccessGroup access = source.AccessFor(ci);
+ if (!source.HasPriv("botserv/administration") && !access.HasPriv("ASSIGN"))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "ASSIGN", ci->GetName());
+ return;
+ }
+
+ if (!ci->GetBot())
+ {
+ source.Reply(_("There is no bot assigned to \002{0}\002."), ci->GetName());
+ return;
+ }
+
+ if (ci->HasFieldS("PERSIST") && !ModeManager::FindChannelModeByName("PERM"))
+ {
+ source.Reply(_("You cannot unassign bots while persist is set on the channel."));
+ return;
+ }
+
+ bool override = !access.HasPriv("ASSIGN");
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "for " << ci->GetBot()->nick;
+
+ ServiceBot *bi = ci->GetBot();
+ bi->UnAssign(source.GetUser(), ci);
+ source.Reply(_("Bot \002{0}\002 has been unassigned from \002{1}\002."), bi->nick, ci->GetName());
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Unassigns a bot from \037channel\037."
+ "Bot configuration is kept, so you will always be able to reassign a bot later without losing your settings.\n"
+ "\n"
+ "Use of this command requires the \002{0}\002 privilege on \037channel\037."
+ "\n"
+ "Example:\n"
+ " {command} #anope\n"
+ " Unassigns the current bot from #anope.\n"),
+ "ASSIGN", "command"_kw = source.command);
+ return true;
+ }
+};
+
+class CommandBSSetNoBot : public Command
+{
+ public:
+ CommandBSSetNoBot(Module *creator, const Anope::string &sname = "botserv/set/nobot") : Command(creator, sname, 2, 2)
+ {
+ this->SetDesc(_("Prevent a bot from being assigned to a channel"));
+ this->SetSyntax(_("\037channel\037 {\037ON|OFF\037}"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &chan = params[0];
+ const Anope::string &value = params[1];
+
+ ChanServ::Channel *ci = ChanServ::Find(chan);
+ if (ci == NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
+ return;
+ }
+
+ if (value.equals_ci("ON"))
+ {
+ Log(LOG_ADMIN, source, this, ci) << "to enable nobot";
+
+ ci->SetS<bool>("BS_NOBOT", true);
+ if (ci->GetBot())
+ ci->GetBot()->UnAssign(source.GetUser(), ci);
+ source.Reply(_("No-bot mode is now \002on\002 for \002{0}\002."), ci->GetName());
+ }
+ else if (value.equals_ci("OFF"))
+ {
+ Log(LOG_ADMIN, source, this, ci) << "to disable nobot";
+
+ ci->UnsetS<bool>("BS_NOBOT");
+ source.Reply(_("No-bot mode is now \002off\002 for \002{0}\002."), ci->GetName());
+ }
+ else
+ this->OnSyntaxError(source, source.command);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
+ {
+ source.Reply(_("If no-bot is set on a channel, then the channel will be unabled to have a bot assigned to it."
+ " If a bot is already assigned to the channel, it is unassigned automatically when no-bot is enabled."
+ "\n"
+ "Example:\n"
+ " {command} #anope on\n"
+ " Prevents a service bot from being assigned to #anope.\n"),
+ "command"_kw = source.command);
+ return true;
+ }
+};
+
+class BSAssign : public Module
+ , public EventHook<Event::Invite>
+ , public EventHook<Event::ServiceBotEvent>
+{
+ Serialize::Field<BotInfo, bool> nobot;
+
+ CommandBSAssign commandbsassign;
+ CommandBSUnassign commandbsunassign;
+ CommandBSSetNoBot commandbssetnobot;
+
+ public:
+ BSAssign(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , EventHook<Event::Invite>(this)
+ , EventHook<Event::ServiceBotEvent>(this)
+
+ , nobot(this, "BS_NOBOT")
+
+ , commandbsassign(this)
+ , commandbsunassign(this)
+ , commandbssetnobot(this)
+ {
+ }
+
+ void OnInvite(User *source, Channel *c, User *targ) override
+ {
+ ServiceBot *bi;
+ if (Anope::ReadOnly || !c->ci || targ->server != Me || !(bi = ServiceBot::Find(targ->nick, true)))
+ return;
+
+ ChanServ::AccessGroup access = c->ci->AccessFor(source);
+ if (!access.HasPriv("ASSIGN") && !source->HasPriv("botserv/administration"))
+ {
+ targ->SendMessage(bi, _("Access denied. You do not have privilege \002ASSIGN\002 on \002{0}\002."), c->ci->GetName());
+ return;
+ }
+
+ if (nobot.HasExt(c->ci))
+ {
+ targ->SendMessage(bi, _("Access denied. \002{0}\002 may not have a bot assigned to it because a Services Operator has disallowed it."), c->ci->GetName());
+ return;
+ }
+
+ if (bi->bi->GetOperOnly() && !source->HasPriv("botserv/administration"))
+ {
+ targ->SendMessage(bi, _("Access denied. Bot \002{0}\002 is for operators only."), bi->nick);
+ return;
+ }
+
+ if (c->ci->GetBot() == bi)
+ {
+ targ->SendMessage(bi, _("Bot \002{0}\002 is already assigned to \002{1}\002."), bi->nick, c->ci->GetName());
+ return;
+ }
+
+ bi->Assign(source, c->ci);
+ targ->SendMessage(bi, _("Bot \002{0}\002 has been assigned to \002{1}\002."), bi->nick, c->ci->GetName());
+ }
+
+ void OnServiceBot(CommandSource &source, ServiceBot *bi, ChanServ::Channel *ci, InfoFormatter &info) override
+ {
+ if (nobot.HasExt(ci))
+ info.AddOption(_("No bot"));
+ }
+};
+
+MODULE_INIT(BSAssign)
diff --git a/modules/botserv/autoassign.cpp b/modules/botserv/autoassign.cpp
new file mode 100644
index 000000000..3bb6dce64
--- /dev/null
+++ b/modules/botserv/autoassign.cpp
@@ -0,0 +1,48 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2011-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+
+class BSAutoAssign : public Module
+ , public EventHook<Event::ChanRegistered>
+{
+ public:
+ BSAutoAssign(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , EventHook<Event::ChanRegistered>(this)
+ {
+ }
+
+ void OnChanRegistered(ChanServ::Channel *ci) override
+ {
+ const Anope::string &bot = Config->GetModule(this)->Get<Anope::string>("bot");
+ if (bot.empty())
+ return;
+
+ ServiceBot *bi = ServiceBot::Find(bot, true);
+ if (bi == NULL)
+ {
+ Log(this) << "bs_autoassign is configured to assign bot " << bot << ", but it does not exist?";
+ return;
+ }
+
+ bi->Assign(NULL, ci);
+ }
+};
+
+MODULE_INIT(BSAutoAssign)
diff --git a/modules/botserv/badwords.cpp b/modules/botserv/badwords.cpp
new file mode 100644
index 000000000..998ccc7f8
--- /dev/null
+++ b/modules/botserv/badwords.cpp
@@ -0,0 +1,472 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/botserv/badwords.h"
+
+class BadWordImpl : public BadWord
+{
+ friend class BadWordsType;
+
+ ChanServ::Channel *channel = nullptr;
+ Anope::string word;
+ BadWordType type;
+
+ public:
+ BadWordImpl(Serialize::TypeBase *type) : BadWord(type) { }
+ BadWordImpl(Serialize::TypeBase *type, Serialize::ID id) : BadWord(type, id) { }
+
+ ChanServ::Channel *GetChannel() override;
+ void SetChannel(ChanServ::Channel *c) override;
+
+ Anope::string GetWord() override;
+ void SetWord(const Anope::string &w) override;
+
+ BadWordType GetType() override;
+ void SetType(const BadWordType &t) override;
+};
+
+class BadWordsType : public Serialize::Type<BadWordImpl>
+{
+ public:
+ Serialize::ObjectField<BadWordImpl, ChanServ::Channel *> channel;
+ Serialize::Field<BadWordImpl, Anope::string> word;
+ Serialize::Field<BadWordImpl, BadWordType> type;
+
+ BadWordsType(Module *me) : Serialize::Type<BadWordImpl>(me)
+ , channel(this, "ci", &BadWordImpl::channel, true)
+ , word(this, "word", &BadWordImpl::word)
+ , type(this, "type", &BadWordImpl::type)
+ {
+ }
+};
+
+ChanServ::Channel *BadWordImpl::GetChannel()
+{
+ return Get(&BadWordsType::channel);
+}
+
+void BadWordImpl::SetChannel(ChanServ::Channel *c)
+{
+ Set(&BadWordsType::channel, c);
+}
+
+Anope::string BadWordImpl::GetWord()
+{
+ return Get(&BadWordsType::word);
+}
+
+void BadWordImpl::SetWord(const Anope::string &w)
+{
+ Set(&BadWordsType::word, w);
+}
+
+BadWordType BadWordImpl::GetType()
+{
+ return Get(&BadWordsType::type);
+}
+
+void BadWordImpl::SetType(const BadWordType &t)
+{
+ Set(&BadWordsType::type, t);
+}
+
+struct BadWordsImpl : BadWords
+{
+ BadWordsImpl(Module *me) : BadWords(me) { }
+
+ BadWord* AddBadWord(ChanServ::Channel *ci, const Anope::string &word, BadWordType type) override
+ {
+ BadWord *bw = Serialize::New<BadWord *>();
+ bw->SetChannel(ci);
+ bw->SetWord(word);
+ bw->SetType(type);
+
+ EventManager::Get()->Dispatch(&Event::BadWordEvents::OnBadWordAdd, ci, bw);
+
+ return bw;
+ }
+
+ std::vector<BadWord *> GetBadWords(ChanServ::Channel *ci) override
+ {
+ return ci->GetRefs<BadWord *>();
+ }
+
+ BadWord* GetBadWord(ChanServ::Channel *ci, unsigned index) override
+ {
+ auto bw = GetBadWords(ci);
+ return index < bw.size() ? bw[index] : nullptr;
+ }
+
+ unsigned GetBadWordCount(ChanServ::Channel *ci) override
+ {
+ return GetBadWords(ci).size();
+ }
+
+ void EraseBadWord(ChanServ::Channel *ci, unsigned index) override
+ {
+ auto bws = GetBadWords(ci);
+ if (index >= bws.size())
+ return;
+
+ BadWord *bw = bws[index];
+ EventManager::Get()->Dispatch(&Event::BadWordEvents::OnBadWordDel, ci, bw);
+
+ delete bw;
+ }
+
+ void ClearBadWords(ChanServ::Channel *ci) override
+ {
+ for (BadWord *bw : GetBadWords(ci))
+ delete bw;
+ }
+};
+
+class CommandBSBadwords : public Command
+{
+ ServiceReference<BadWords> badwords;
+
+ void DoList(CommandSource &source, ChanServ::Channel *ci, const Anope::string &word)
+ {
+ bool override = !source.AccessFor(ci).HasPriv("BADWORDS");
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "LIST";
+ ListFormatter list(source.GetAccount());
+
+ list.AddColumn(_("Number")).AddColumn(_("Word")).AddColumn(_("Type"));
+
+ if (!badwords->GetBadWordCount(ci))
+ {
+ source.Reply(_("The bad word list of \002{0}\002 is empty."), ci->GetName());
+ return;
+ }
+
+ if (!word.empty() && word.find_first_not_of("1234567890,-") == Anope::string::npos)
+ {
+ NumberList(word, false,
+ [&](unsigned int num)
+ {
+ if (!num || num > badwords->GetBadWordCount(ci))
+ return;
+
+ BadWord *b = badwords->GetBadWord(ci, num - 1);
+ ListFormatter::ListEntry entry;
+ entry["Number"] = stringify(num);
+ entry["Word"] = b->GetWord();
+ entry["Type"] = b->GetType() == BW_SINGLE ? "(SINGLE)" : (b->GetType() == BW_START ? "(START)" : (b->GetType() == BW_END ? "(END)" : ""));
+ list.AddEntry(entry);
+ },
+ [](){});
+ }
+ else
+ {
+ for (unsigned i = 0, end = badwords->GetBadWordCount(ci); i < end; ++i)
+ {
+ BadWord *b = badwords->GetBadWord(ci, i);
+
+ if (!word.empty() && !Anope::Match(b->GetWord(), word))
+ continue;
+
+ ListFormatter::ListEntry entry;
+ entry["Number"] = stringify(i + 1);
+ entry["Word"] = b->GetWord();
+ entry["Type"] = b->GetType() == BW_SINGLE ? "(SINGLE)" : (b->GetType() == BW_START ? "(START)" : (b->GetType() == BW_END ? "(END)" : ""));
+ list.AddEntry(entry);
+ }
+ }
+
+ if (list.IsEmpty())
+ {
+ source.Reply(_("No matching entries on the bad word list of \002{0}\002."), ci->GetName());
+ return;
+ }
+
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+ source.Reply(_("Bad words list for \002{0}\002:"), ci->GetName());
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+
+ source.Reply(_("End of bad words list."));
+ }
+
+ void DoAdd(CommandSource &source, ChanServ::Channel *ci, const Anope::string &word)
+ {
+ size_t pos = word.rfind(' ');
+ BadWordType bwtype = BW_ANY;
+ Anope::string realword = word;
+
+ if (pos != Anope::string::npos)
+ {
+ Anope::string opt = word.substr(pos + 1);
+ if (!opt.empty())
+ {
+ if (opt.equals_ci("SINGLE"))
+ bwtype = BW_SINGLE;
+ else if (opt.equals_ci("START"))
+ bwtype = BW_START;
+ else if (opt.equals_ci("END"))
+ bwtype = BW_END;
+ }
+ realword = word.substr(0, pos);
+ }
+
+ unsigned badwordsmax = Config->GetModule(this->module)->Get<unsigned>("badwordsmax");
+ if (badwords->GetBadWordCount(ci) >= badwordsmax)
+ {
+ source.Reply(_("Sorry, you can only have \002{0}\002 bad words entries on a channel."), badwordsmax);
+ return;
+ }
+
+ bool casesensitive = Config->GetModule(this->module)->Get<bool>("casesensitive");
+
+ for (BadWord *bw : badwords->GetBadWords(ci))
+ if ((casesensitive && realword.equals_cs(bw->GetWord())) || (!casesensitive && realword.equals_ci(bw->GetWord())))
+ {
+ source.Reply(_("\002{0}\002 already exists in \002{1}\002 bad words list."), bw->GetWord(), ci->GetName());
+ return;
+ }
+
+ bool override = !source.AccessFor(ci).HasPriv("BADWORDS");
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "ADD " << realword;
+ badwords->AddBadWord(ci, realword, bwtype);
+
+ source.Reply(_("\002{0}\002 added to \002{1}\002 bad words list."), realword, ci->GetName());
+ }
+
+ void DoDelete(CommandSource &source, ChanServ::Channel *ci, const Anope::string &word)
+ {
+ if (!badwords->GetBadWordCount(ci))
+ {
+ source.Reply(_("Bad word list for \002{0}\002 is empty."), ci->GetName());
+ return;
+ }
+
+ bool override = !source.AccessFor(ci).HasPriv("BADWORDS");
+
+ /* Special case: is it a number/list? Only do search if it isn't. */
+ if (!word.empty() && isdigit(word[0]) && word.find_first_not_of("1234567890,-") == Anope::string::npos)
+ {
+ unsigned int deleted = 0;
+
+ NumberList(word, true,
+ [&](unsigned int num)
+ {
+ if (!num || num > badwords->GetBadWordCount(ci))
+ return;
+
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "DEL " << badwords->GetBadWord(ci, num - 1)->GetWord();
+ ++deleted;
+ badwords->EraseBadWord(ci, num - 1);
+ },
+ [&]()
+ {
+ if (!deleted)
+ source.Reply(_("No matching entries on the bad word list of \002{0}\002."), ci->GetName());
+ else if (deleted == 1)
+ source.Reply(_("Deleted \0021\002 entry from bad word list of \002{0}\002."), ci->GetName());
+ else
+ source.Reply(_("Deleted \002{0}\002 entries from the bad word list of \002{1}\002."), deleted, ci->GetName());
+ });
+ }
+ else
+ {
+ unsigned i, end;
+ BadWord *bw;
+
+ for (i = 0, end = badwords->GetBadWordCount(ci); i < end; ++i)
+ {
+ bw = badwords->GetBadWord(ci, i);
+
+ if (word.equals_ci(bw->GetWord()))
+ break;
+ }
+
+ if (i == end)
+ {
+ source.Reply(_("\002{0}\002 was not found on the bad word list of \002{1}\002."), word, ci->GetName());
+ return;
+ }
+
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "DEL " << bw->GetWord();
+
+ source.Reply(_("\002{0}\002 deleted from \002{1}\002 bad words list."), bw->GetWord(), ci->GetName());
+
+ bw->Delete();
+ }
+ }
+
+ void DoClear(CommandSource &source, ChanServ::Channel *ci)
+ {
+ bool override = !source.AccessFor(ci).HasPriv("BADWORDS");
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "CLEAR";
+
+ badwords->ClearBadWords(ci);
+ source.Reply(_("Bad words list is now empty."));
+ }
+
+ public:
+ CommandBSBadwords(Module *creator) : Command(creator, "botserv/badwords", 2, 3)
+ {
+ this->SetDesc(_("Maintains the bad words list"));
+ this->SetSyntax(_("\037channel\037 ADD \037word\037 [\037SINGLE\037 | \037START\037 | \037END\037]"));
+ this->SetSyntax(_("\037channel\037 DEL {\037word\037 | \037entry-num\037 | \037list\037}"));
+ this->SetSyntax(_("\037channel\037 LIST [\037mask\037 | \037list\037]"));
+ this->SetSyntax(_("\037channel\037 CLEAR"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &chan = params[0];
+ const Anope::string &cmd = params[1];
+ const Anope::string &word = params.size() > 2 ? params[2] : "";
+ bool need_args = cmd.equals_ci("LIST") || cmd.equals_ci("CLEAR");
+
+ if (!need_args && word.empty())
+ {
+ this->OnSyntaxError(source, cmd);
+ return;
+ }
+
+ ChanServ::Channel *ci = ChanServ::Find(chan);
+ if (ci == NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
+ return;
+ }
+
+ if (!source.AccessFor(ci).HasPriv("BADWORDS") && (!need_args || !source.HasPriv("botserv/administration")))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "BADWORDS", ci->GetName());
+ return;
+ }
+
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Sorry, bad words list modification is temporarily disabled."));
+ return;
+ }
+
+ if (cmd.equals_ci("ADD"))
+ this->DoAdd(source, ci, word);
+ else if (cmd.equals_ci("DEL"))
+ this->DoDelete(source, ci, word);
+ else if (cmd.equals_ci("LIST"))
+ this->DoList(source, ci, word);
+ else if (cmd.equals_ci("CLEAR"))
+ this->DoClear(source, ci);
+ else
+ this->OnSyntaxError(source, "");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ if (subcommand.equals_ci("ADD"))
+ {
+ source.Reply(_("Adds \037word\037 to the bad words list for \037channel\037. If \002SINGLE\002 is specified then the user must say the enire word for it to be considered a match."
+ " If \002START\002 is specified then the user only must say a word that starts with \037word\037."
+ " Likewise if \002END\002 is specified then the user must only say a word that ends with \037word\037."
+ " If no argument is specified, then any word which contains \037word\037 will be considered a match.\n"
+ "\n"
+ "Examples:\n"
+ " {command} #anope ADD raw SINGLE\n"
+ " Adds the bad word \"raw\" to the bad word list of #anope."),
+ "command"_kw = source.command);
+ }
+ else if (subcommand.equals_ci("DEL"))
+ {
+ source.Reply(_("Removes \037word\037 from the bad words list for \037channel\037. If a number or list of numbers is given, those entries are deleted.\n"
+ "\n"
+ "Examples:\n"
+ " {command} #anope DEL raw\n"
+ " Removes the bad word \"raw\" from the bad word list of #anope.\n"
+ "\n"
+ " {command} #anope DEL 2-5,7-9\n"
+ " Removes bad words entries numbered 2 through 5 and 7 through 9 on #anope."),
+ "command"_kw = source.command);
+
+ }
+ else if (subcommand.equals_ci("LIST"))
+ {
+ source.Reply(_("Lists the badwords for \037channel\037."
+ " If a wildcard mask is given, only those entries matching the mask are displayed."
+ " If a list of entry numbers is given, only those entries are shown.\n"
+ "\n"
+ "Examples:\n"
+ " {command} #anope LIST\n"
+ " Lists the bad words for #anope.\n"
+ "\n"
+ " {command} #anope LIST 2-5,7-9\n"
+ " Lists bad words entries numbered 2 thorough 5 and 7 through 9 on #anope."),
+ "command"_kw = source.command);
+ }
+ else if (subcommand.equals_ci("CLEAR"))
+ {
+ source.Reply(_("Clears the bad words for \037channel\037."
+ "\n"
+ "\n"
+ "Example:\n"
+ " {command} #anope CLEAR\n"
+ " Clears the bad word list for #anope."),
+ "command"_kw = source.command);
+ }
+ else
+ {
+ source.Reply(_("Maintains the bad words list for a channel."
+ " When a word on the bad words list is said, action may be taken against the offending user.\n"
+ "\n"
+ "Use of this command requires the \002{0}\002 privilege on \037channel\037."),
+ "BADWORDS");
+
+ CommandInfo *help = source.service->FindCommand("generic/help");
+ if (help)
+ source.Reply(_("\n"
+ "For help configuring the bad word kicker use \002{msg}{service} HELP KICK BADWORDS\002.\n"//XXX
+ "\n"
+ "The \002ADD\002 command adds \037word\037 to the badwords list.\n"
+ "\002{msg}{service} {help} {command} ADD\002 for more information.\n"
+ "\n"
+ "The \002DEL\002 command removes \037word\037 from the badwords list.\n"
+ "\002{msg}{service} {help} {command} DEL\002 for more information.\n"
+ "\n"
+ "The \002LIST\002 and \002CLEAR\002 commands show and clear the bad words list, respectively.\n"
+ "\002{msg}{service} {help} {command} LIST\002 and \002{msg}{service} {help} {command} CLEAR\002 for more information.\n"),
+ "msg"_kw = Config->StrictPrivmsg, "service"_kw = source.service->nick, "command"_kw = source.command, "help"_kw = help->cname);
+ }
+ return true;
+ }
+};
+
+class BSBadwords : public Module
+{
+ CommandBSBadwords commandbsbadwords;
+ BadWordsImpl badwords;
+ BadWordsType bwtype;
+
+ public:
+ BSBadwords(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandbsbadwords(this)
+ , badwords(this)
+ , bwtype(this)
+ {
+ }
+};
+
+MODULE_INIT(BSBadwords)
diff --git a/modules/botserv/bot.cpp b/modules/botserv/bot.cpp
new file mode 100644
index 000000000..c93c4cf50
--- /dev/null
+++ b/modules/botserv/bot.cpp
@@ -0,0 +1,427 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/botserv/bot.h"
+
+class CommandBSBot : public Command
+{
+ void DoAdd(CommandSource &source, const std::vector<Anope::string> &params)
+ {
+ const Anope::string &nick = params[1];
+ const Anope::string &user = params[2];
+ const Anope::string &host = params[3];
+ const Anope::string &real = params[4];
+
+ if (ServiceBot::Find(nick, true))
+ {
+ source.Reply(_("Bot \002{0}\002 already exists."), nick);
+ return;
+ }
+
+ Configuration::Block *networkinfo = Config->GetBlock("networkinfo");
+
+ if (nick.length() > networkinfo->Get<unsigned>("nicklen"))
+ {
+ source.Reply(_("Bot nicks may only be \002{0}\002 characters long."), networkinfo->Get<unsigned>("nicklen"));
+ return;
+ }
+
+ if (user.length() > networkinfo->Get<unsigned>("userlen"))
+ {
+ source.Reply(_("Bot idents may only be \002{0}\002 characters long."), networkinfo->Get<unsigned>("userlen"));
+ return;
+ }
+
+ if (host.length() > networkinfo->Get<unsigned>("hostlen"))
+ {
+ source.Reply(_("Bot hosts may only be \002{0}\002 characters long."), networkinfo->Get<unsigned>("hostlen"));
+ return;
+ }
+
+ if (!IRCD->IsNickValid(nick))
+ {
+ source.Reply(_("Bot nicks may only contain valid nick characters."));
+ return;
+ }
+
+ if (!IRCD->IsIdentValid(user))
+ {
+ source.Reply(_("Bot idents may only contain valid ident characters."));
+ return;
+ }
+
+ if (!IRCD->IsHostValid(host))
+ {
+ source.Reply(_("Bot hosts may only contain valid host characters."));
+ return;
+ }
+
+ /* We check whether the nick is registered, and inform the user
+ * if so. You need to drop the nick manually before you can use
+ * it as a bot nick from now on -GD
+ */
+ NickServ::Nick *na = NickServ::FindNick(nick);
+ if (na)
+ {
+ source.Reply(_("\002{0}\002 is already registered!"), na->GetNick());
+ return;
+ }
+
+ User *targ = User::Find(nick, true);
+ if (targ)
+ {
+ source.Reply(_("\002{0}\002 is currently in use."), targ->nick);
+ return;
+ }
+
+ ServiceBot *bi = new ServiceBot(nick, user, host, real);
+
+ Log(LOG_ADMIN, source, this) << "ADD " << bi->GetMask() << " " << bi->realname;
+
+ source.Reply(_("\002{0}!{1}@{2}\002 (\002{3}\002) added to the bot list."), bi->nick, bi->GetIdent(), bi->host, bi->realname);
+
+ EventManager::Get()->Dispatch(&Event::BotCreate::OnBotCreate, bi);
+ }
+
+ void DoChange(CommandSource &source, const std::vector<Anope::string> &params)
+ {
+ const Anope::string &oldnick = params[1];
+ const Anope::string &nick = params.size() > 2 ? params[2] : "";
+ const Anope::string &user = params.size() > 3 ? params[3] : "";
+ const Anope::string &host = params.size() > 4 ? params[4] : "";
+ const Anope::string &real = params.size() > 5 ? params[5] : "";
+
+ if (oldnick.empty() || nick.empty())
+ {
+ this->OnSyntaxError(source, "CHANGE");
+ return;
+ }
+
+ ServiceBot *bi = ServiceBot::Find(oldnick, true);
+ if (!bi)
+ {
+ source.Reply(_("Bot \002{0}\002 does not exist."), oldnick);
+ return;
+ }
+
+ if (bi->bi->conf)
+ {
+ source.Reply(_("Bot \002{0}\002 is not changeable because it is configured in services configuration."), bi->nick.c_str());
+ return;
+ }
+
+ Configuration::Block *networkinfo = Config->GetBlock("networkinfo");
+
+ if (nick.length() > networkinfo->Get<unsigned>("nicklen"))
+ {
+ source.Reply(_("Bot nicknames may only be \002{0}\002 characters long."), networkinfo->Get<unsigned>("nicklen"));
+ return;
+ }
+
+ if (user.length() > networkinfo->Get<unsigned>("userlen"))
+ {
+ source.Reply(_("Bot usernames may only be \002{0}\002 characters long."), networkinfo->Get<unsigned>("userlen"));
+ return;
+ }
+
+ if (host.length() > networkinfo->Get<unsigned>("hostlen"))
+ {
+ source.Reply(_("Bot hostnames may only be \002{0}\002 characters long."), networkinfo->Get<unsigned>("hostlen"));
+ return;
+ }
+
+ /* Checks whether there *are* changes.
+ * Case sensitive because we may want to change just the case.
+ * And we must finally check that the nick is not already
+ * taken by another bot.
+ */
+ if (nick.equals_cs(bi->nick)
+ && (user.empty() || user.equals_cs(bi->GetIdent()))
+ && (host.empty() || host.equals_cs(bi->host))
+ && (real.empty() || real.equals_cs(bi->realname)))
+ {
+ source.Reply(_("There is no difference between the current settings and the new settings."));
+ return;
+ }
+
+ if (!IRCD->IsNickValid(nick))
+ {
+ source.Reply(_("Bot nicknames may only contain valid nickname characters."));
+ return;
+ }
+
+ if (!user.empty() && !IRCD->IsIdentValid(user))
+ {
+ source.Reply(_("Bot uesrnames may only contain valid username characters."));
+ return;
+ }
+
+ if (!host.empty() && !IRCD->IsHostValid(host))
+ {
+ source.Reply(_("Bot hostnames may only contain valid hostname characters."));
+ return;
+ }
+
+ ServiceBot *newbi = ServiceBot::Find(nick, true);
+ if (newbi && bi != newbi)
+ {
+ source.Reply(_("Bot \002{0}\002 already exists."), newbi->nick);
+ return;
+ }
+
+ User *target = User::Find(nick, true);
+ if (target)
+ {
+ source.Reply(_("\002{0}\002 is currently in use."), target->nick);
+ return;
+ }
+
+ if (!nick.equals_ci(bi->nick))
+ {
+ /* We check whether the nick is registered, and inform the user
+ * if so. You need to drop the nick manually before you can use
+ * it as a bot nick from now on -GD
+ */
+ NickServ::Nick *na = NickServ::FindNick(nick);
+ if (na)
+ {
+ source.Reply(_("\002{0}\002 is already registered."), na->GetNick());
+ return;
+ }
+
+ /* The new nick is really different, so we remove the Q line for the old nick. */
+ //XLine x_del(bi->nick);
+ //IRCD->SendSQLineDel(&x_del);
+
+ /* Add a Q line for the new nick */
+ //XLine x(nick, "Reserved for services");
+ //IRCD->SendSQLine(NULL, &x);
+ }
+
+ if (!user.empty())
+ {
+ IRCD->SendQuit(bi, "Quit: Be right back");
+ bi->introduced = false;
+ }
+ else
+ IRCD->SendNickChange(bi, nick);
+
+ if (!nick.equals_cs(bi->nick))
+ {
+ bi->SetNewNick(nick);
+ bi->bi->SetNick(nick);
+ }
+
+ if (!user.equals_cs(bi->GetIdent()))
+ {
+ bi->SetIdent(user);
+ bi->bi->SetUser(user);
+ }
+ if (!host.equals_cs(bi->host))
+ {
+ bi->host = host;
+ bi->bi->SetHost(host);
+ }
+ if (real.equals_cs(bi->realname))
+ {
+ bi->realname = real;
+ bi->bi->SetRealName(real);
+ }
+
+ if (!user.empty())
+ bi->OnKill();
+
+ source.Reply(_("Bot \002{0}\002 has been changed to \002{1}!{2}@{3}\002 (\002{4}\002)."), oldnick, bi->nick, bi->GetIdent(), bi->host, bi->realname);
+ Log(LOG_ADMIN, source, this) << "CHANGE " << oldnick << " to " << bi->GetMask() << " " << bi->realname;
+
+ EventManager::Get()->Dispatch(&Event::BotChange::OnBotChange, bi);
+ }
+
+ void DoDel(CommandSource &source, const std::vector<Anope::string> &params)
+ {
+ const Anope::string &nick = params[1];
+
+ if (nick.empty())
+ {
+ this->OnSyntaxError(source, "DEL");
+ return;
+ }
+
+ ServiceBot *bi = ServiceBot::Find(nick, true);
+ if (!bi)
+ {
+ source.Reply(_("Bot \002{0}\002 does not exist."), nick);
+ return;
+ }
+
+ if (bi->bi->conf)
+ {
+ source.Reply(_("Bot \002{0}\002 is can not be deleted because it is configured in services configuration."), bi->nick);
+ return;
+ }
+
+ EventManager::Get()->Dispatch(&Event::BotDelete::OnBotDelete, bi);
+
+ Log(LOG_ADMIN, source, this) << "DEL " << bi->nick;
+
+ source.Reply(_("Bot \002{0}\002 has been deleted."), bi->nick);
+ delete bi;
+ }
+
+ public:
+ CommandBSBot(Module *creator) : Command(creator, "botserv/bot", 1, 6)
+ {
+ this->SetDesc(_("Maintains network bot list"));
+ this->SetSyntax(_("\002ADD \037nicknae\037 \037username\037 \037hostname\037 \037realname\037\002"));
+ this->SetSyntax(_("\002CHANGE \037oldnickname\037 \037newnickname\037 [\037username\037 [\037hostname\037 [\037realname\037]]]\002"));
+ this->SetSyntax(_("\002DEL \037nickname\037\002"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &cmd = params[0];
+
+ if (Anope::ReadOnly)
+ source.Reply(_("Services are in read-only mode. Any changes made may not persist."));
+
+ if (cmd.equals_ci("ADD"))
+ {
+ if (!source.HasCommand("botserv/bot/add"))
+ {
+ source.Reply(_("Access denied. You do not have access to the operator command \002{0}\002."), "botserv/bot/add");
+ return;
+ }
+
+ // ADD nick user host real - 5
+ if (params.size() < 5)
+ {
+ this->OnSyntaxError(source, "ADD");
+ return;
+ }
+
+ std::vector<Anope::string> tempparams = params;
+ // ADD takes less params than CHANGE, so we need to take 6 if given and append it with a space to 5.
+ if (tempparams.size() >= 6)
+ tempparams[4] = tempparams[4] + " " + tempparams[5];
+
+ this->DoAdd(source, tempparams);
+ }
+ else if (cmd.equals_ci("CHANGE"))
+ {
+ // CHANGE oldn newn user host real - 6
+ // but only oldn and newn are required
+ if (!source.HasCommand("botserv/bot/change"))
+ {
+ source.Reply(_("Access denied. You do not have access to the operator command \002{0}\002."), "botserv/bot/change");
+ return;
+ }
+
+ if (params.size() < 3)
+ {
+ this->OnSyntaxError(source, "CHANGE");
+ return;
+ }
+
+ this->DoChange(source, params);
+ }
+ else if (cmd.equals_ci("DEL"))
+ {
+ // DEL nick
+ if (!source.HasCommand("botserv/bot/del"))
+ {
+ source.Reply(_("Access denied. You do not have access to the operator command \002{0}\002."), "botserv/bot/del");
+ return;
+ }
+
+ if (params.size() < 1)
+ {
+ this->OnSyntaxError(source, "DEL");
+ return;
+ }
+
+ this->DoDel(source, params);
+ }
+ else
+ this->OnSyntaxError(source, "");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ if (subcommand.equals_ci("ADD"))
+ source.Reply(_("\002{command} ADD\002 adds a bot with the given \037nickname\037, \037username\037, \037hostname\037 and \037realname\037."
+ " You can not create a bot with a nickname that is currently registered. If an unregistered user is currently using the nick, they will be killed.\n"
+ " Once a bot is created, users will be able to assign the bot to their channels. This command requires the opererator privilege for command \002{0}\002."
+ "\n"
+ "Example:\n"
+ " {command} ADD Botox Botox services.anope.org Botox\n"
+ " Adds a service bot with nickname \"Botox\", username \"Botox\", hostname \"services.anope.org\", and realname \"Botox\" to the bot list."),
+ "botserv/bot/add", "command"_kw = source.command);
+ else if (subcommand.equals_ci("CHANGE"))
+ source.Reply(_("\002{command} CHANGE\002 allows changing the \037nickname\037, \037username\037, \037hostname\037 and \037realname\037 of bot \037oldnickname\037."
+ " If a new username, hostname, or realname is specified, then the bot \037nickname\037 will quit and rejoin all of its channels using the new mask."
+ " Otherwise, the bot simply change nick to \037newnickname\037. All settings on the bot, such as channels and no-bot, are retained."
+ " This command requires the operator privilege for command \002{0}\002."
+ "\n"
+ "Example:\n"
+ " {command} CHANGE Botox peer connection reset.by peer\n"
+ " Renames the bot \"Botox\" to \"peer\" with the given username, hostname, and realname."),
+ "botserv/bot/change", "command"_kw = source.command);
+ else if (subcommand.equals_ci("DEL"))
+ source.Reply(_("\002{command} DEL\002 removes the bot \037nickname\037 from the bot list. The bot will quit from any channels it is in, and will not be replaced."
+ " This command requires the operator privilege for command \002{0}\002.\n"
+ "\n"
+ "Example:\n"
+ " {command} DEL peer\n"
+ " Removes the bot \"peer\" from the bot list."),
+ "botserv/bot/del", "command"_kw = source.command);
+ else
+ {
+ source.Reply(_("Allows Services Operators to create, modify, and delete bots that users will be able to use on their channels."));
+
+ CommandInfo *help = source.service->FindCommand("generic/help");
+ if (help)
+ source.Reply(_("\n"
+ "The \002ADD\002 command adds a bot with the given \037nickname\037, \037username\037, \037hostname\037 and \037realname\037 to the bot list.\n"
+ "\002{msg}{service} {help} {command} ADD\002 for more information.\n"
+ "\n"
+ "The \002CHANGE\002 command allows changing the \037nickname\037, \037username\037, \037hostname\037 and \037realname\037 of bot \037oldnickname\037.\n"
+ "\002{msg}{service} {help} {command} CHANGE\002 for more information.\n"
+ "\n"
+ "The \002{command} DEL\002 removes the bot \037nickname\037 from the bot list.\n"
+ "\002{msg}{service} {help} {command} DEL\002 for more information."),
+ "msg"_kw = Config->StrictPrivmsg, "help"_kw = help->cname, "command"_kw = source.command);
+ }
+ return true;
+ }
+};
+
+class BSBot : public Module
+{
+ CommandBSBot commandbsbot;
+
+ public:
+ BSBot(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandbsbot(this)
+ {
+
+ }
+};
+
+MODULE_INIT(BSBot)
diff --git a/modules/botserv/botlist.cpp b/modules/botserv/botlist.cpp
new file mode 100644
index 000000000..6c12a2c59
--- /dev/null
+++ b/modules/botserv/botlist.cpp
@@ -0,0 +1,100 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+
+class CommandBSBotList : public Command
+{
+ public:
+ CommandBSBotList(Module *creator) : Command(creator, "botserv/botlist", 0, 0)
+ {
+ this->SetDesc(_("Lists available bots"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ unsigned count = 0;
+ ListFormatter list(source.GetAccount());
+
+ list.AddColumn(_("Nick")).AddColumn(_("Mask"));
+
+ for (BotInfo *bi : Serialize::GetObjects<BotInfo *>())
+ {
+ if (source.HasPriv("botserv/administration") || !bi->GetOperOnly())
+ {
+ ++count;
+ ListFormatter::ListEntry entry;
+ entry["Nick"] = (bi->GetOperOnly() ? "* " : "") + bi->GetNick();
+ entry["Mask"] = bi->GetUser() + "@" + bi->GetHost();
+ list.AddEntry(entry);
+ }
+ }
+
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+ if (!count)
+ {
+ source.Reply(_("There are no bots available"));
+ return;
+ }
+
+ source.Reply(_("Bot list:"));
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+
+ source.Reply(_("{0} bots available."), count);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ ServiceBot *bi;
+ Anope::string name;
+ Command::FindCommandFromService("botserv/assign", bi, name);
+ if (!bi)
+ return false;
+
+ source.Reply(_("Lists all available bots. You may use the \002{msg}{service} {assign}\002 command to assign a bot to your channel."
+ "The bot names are vanity; they all proviate the same commands and features."),
+ "msg"_kw = Config->StrictPrivmsg, "service"_kw = bi->nick, "assign"_kw = name);
+ if (source.HasPriv("botserv/administration"))
+ source.Reply(_("Bots prefixed by a * are reserved for Services Operators with the privilege \002{0}\002."),
+ "botserv/administration");
+ source.Reply(_("\n"
+ "Example:\n"
+ " {command} BOTLIST"),
+ "command"_kw = source.command);
+ return true;
+ }
+};
+
+class BSBotList : public Module
+{
+ CommandBSBotList commandbsbotlist;
+
+ public:
+ BSBotList(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandbsbotlist(this)
+ {
+
+ }
+};
+
+MODULE_INIT(BSBotList)
diff --git a/modules/botserv/control.cpp b/modules/botserv/control.cpp
new file mode 100644
index 000000000..86b4ed829
--- /dev/null
+++ b/modules/botserv/control.cpp
@@ -0,0 +1,176 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+
+class CommandBSSay : public Command
+{
+ public:
+ CommandBSSay(Module *creator) : Command(creator, "botserv/say", 2, 2)
+ {
+ this->SetDesc(_("Makes the bot say the specified text on the specified channel"));
+ this->SetSyntax(_("\037channel\037 \037text\037"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &chan = params[0];
+ const Anope::string &text = params[1];
+
+ ChanServ::Channel *ci = ChanServ::Find(chan);
+ if (ci == NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
+ return;
+ }
+
+ if (!source.AccessFor(ci).HasPriv("SAY") && !source.HasPriv("botserv/administration"))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "SAY", ci->GetName());
+ return;
+ }
+
+ if (!ci->GetBot())
+ {
+ source.Reply(_("There is no bot assigned to \002{0}\002. One must be assigned to the channel before this command can be used."), ci->GetName());
+ ServiceBot *bi;
+ Anope::string name;
+ Command::FindCommandFromService("botserv/assign", bi, name);
+ CommandInfo *help = source.service->FindCommand("generic/help");
+ if (bi && help)
+ source.Reply(_("See \002{msg}{service} {help} {command}\002 for information on assigning bots."),
+ "msg"_kw = Config->StrictPrivmsg, "service"_kw = bi->nick, "help"_kw = help->cname, "command"_kw = name);
+ return;
+ }
+
+ if (!ci->c || !ci->c->FindUser(ci->GetBot()))
+ {
+ source.Reply(_("Bot \002{0}\002 is not on channel \002{1}\002."), ci->GetBot()->nick, ci->GetName());
+ return;
+ }
+
+ if (text[0] == '\001')
+ {
+ this->OnSyntaxError(source, "");
+ return;
+ }
+
+ IRCD->SendPrivmsg(ci->GetBot(), ci->GetName(), "%s", text.c_str());
+ ci->GetBot()->lastmsg = Anope::CurTime;
+
+ bool override = !source.AccessFor(ci).HasPriv("SAY");
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to say: " << text;
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ this->SendSyntax(source);
+ source.Reply(" ");
+ source.Reply(_("Makes the bot say the given \037text\037 on \037channel\037.\n"
+ "\n"
+ "Example:\n"
+ " {command} #anope mmm pie\n"
+ " Makes the assigned service bot say \"mmm pie\"."));
+ return true;
+ }
+};
+
+class CommandBSAct : public Command
+{
+ public:
+ CommandBSAct(Module *creator) : Command(creator, "botserv/act", 2, 2)
+ {
+ this->SetDesc(_("Makes the bot do the equivalent of a \"/me\" command"));
+ this->SetSyntax(_("\037channel\037 \037text\037"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &chan = params[0];
+ Anope::string message = params[1];
+
+ ChanServ::Channel *ci = ChanServ::Find(chan);
+ if (ci == NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
+ return;
+ }
+
+ if (!source.AccessFor(ci).HasPriv("SAY") && !source.HasPriv("botserv/administration"))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "SAY", ci->GetName());
+ return;
+ }
+
+ if (!ci->GetBot())
+ {
+ source.Reply(_("There is no bot assigned to \002{0}\002. One must be assigned to the channel before this command can be used."), ci->GetName());
+ ServiceBot *bi;
+ Anope::string name;
+ Command::FindCommandFromService("botserv/assign", bi, name);
+ CommandInfo *help = source.service->FindCommand("generic/help");
+ if (bi && help)
+ source.Reply(_("See \002{msg}{service} {help} {command}\002 for information on assigning bots."),
+ "msg"_kw = Config->StrictPrivmsg, "service"_kw = bi->nick, "help"_kw = help->cname, "command"_kw = name);
+ return;
+ }
+
+ if (!ci->c || !ci->c->FindUser(ci->GetBot()))
+ {
+ source.Reply(_("Bot \002{0}\002 is not on channel \002{1}\002."), ci->GetBot()->nick, ci->GetName());
+ return;
+ }
+
+ message = message.replace_all_cs("\1", "");
+ if (message.empty())
+ return;
+
+ IRCD->SendAction(ci->GetBot(), ci->GetName(), "%s", message.c_str());
+ ci->GetBot()->lastmsg = Anope::CurTime;
+
+ bool override = !source.AccessFor(ci).HasPriv("SAY");
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to say: " << message;
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Makes the assigned bot do the equivalent of a \"/me\" command on \037channel\037 using the given \037text\037.\n"
+ "\n"
+ "Example:\n"
+ " {command} #anope slaps Cronus\n"
+ " Shows the assigned service bot \"slapping\" Cronus."),
+ "command"_kw = source.command);
+ return true;
+ }
+};
+
+class BSControl : public Module
+{
+ CommandBSSay commandbssay;
+ CommandBSAct commandbsact;
+
+ public:
+ BSControl(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandbssay(this), commandbsact(this)
+ {
+
+ }
+};
+
+MODULE_INIT(BSControl)
diff --git a/modules/botserv/info.cpp b/modules/botserv/info.cpp
new file mode 100644
index 000000000..94b1bbbd7
--- /dev/null
+++ b/modules/botserv/info.cpp
@@ -0,0 +1,118 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/botserv/info.h"
+
+class CommandBSInfo : public Command
+{
+ public:
+ CommandBSInfo(Module *creator) : Command(creator, "botserv/info", 1, 1)
+ {
+ this->SetSyntax(_("{\037channel\037 | \037nickname\037}"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &query = params[0];
+
+ ServiceBot *bi = ServiceBot::Find(query, true);
+ ChanServ::Channel *ci = ChanServ::Find(query);
+ InfoFormatter info(source.nc);
+
+ if (bi)
+ {
+ source.Reply(_("Information for bot \002%s\002:"), bi->nick.c_str());
+ info[_("Mask")] = bi->GetIdent() + "@" + bi->host;
+ info[_("Real name")] = bi->realname;
+ info[_("Created")] = Anope::strftime(bi->bi->GetCreated(), source.GetAccount());
+ info[_("Options")] = bi->bi->GetOperOnly() ? _("Private") : _("None");
+ info[_("Used on")] = stringify(bi->GetChannelCount()) + " channel(s)";
+
+ EventManager::Get()->Dispatch(&Event::ServiceBotEvent::OnServiceBot, source, bi, ci, info);
+
+ std::vector<Anope::string> replies;
+ info.Process(replies);
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+
+ if (source.HasPriv("botserv/administration"))
+ {
+ Anope::string buf;
+ for (ChanServ::Channel *ci2 : bi->GetChannels())
+ buf += " " + ci2->GetName();
+ source.Reply(buf);
+ }
+
+ }
+ else if (ci)
+ {
+ if (!source.AccessFor(ci).HasPriv("INFO") && !source.HasPriv("botserv/administration"))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "INFO", ci->GetName());
+ return;
+ }
+
+ source.Reply(_("Information for channel \002{0}\002:"), ci->GetName());
+ info[_("Bot nick")] = ci->GetBot() ? ci->GetBot()->nick : _("not assigned yet");
+
+ Anope::string enabled = Language::Translate(source.nc, _("Enabled"));
+ Anope::string disabled = Language::Translate(source.nc, _("Disabled"));
+
+ EventManager::Get()->Dispatch(&Event::ServiceBotEvent::OnServiceBot, source, bi, ci, info);
+
+ std::vector<Anope::string> replies;
+ info.Process(replies);
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+ }
+ else
+ source.Reply(_("\002{0}\002 is not a valid bot or registered channel."), query);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Allows you to see {0} information about a channel or a bot."
+ " If the parameter is a channel, then you'll get information such as enabled kickers."
+ " If the parameter is a bot nickname, you'll get information about a bot, such as creation time and number of channels it is on."),
+ source.service->nick);
+ return true;
+ }
+
+ const Anope::string GetDesc(CommandSource &source) const override
+ {
+ return Anope::printf(Language::Translate(source.GetAccount(), _("Allows you to see %s information about a channel or a bot")), source.service->nick.c_str());
+ }
+};
+
+class BSInfo : public Module
+{
+ CommandBSInfo commandbsinfo;
+
+ public:
+ BSInfo(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandbsinfo(this)
+ {
+
+ }
+};
+
+MODULE_INIT(BSInfo)
diff --git a/modules/botserv/kick.cpp b/modules/botserv/kick.cpp
new file mode 100644
index 000000000..3d56ddd21
--- /dev/null
+++ b/modules/botserv/kick.cpp
@@ -0,0 +1,1743 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/botserv/kick.h"
+#include "modules/botserv/badwords.h"
+#include "modules/botserv/info.h"
+
+static Module *me;
+
+enum TTBType
+{
+ TTB_BOLDS,
+ TTB_COLORS,
+ TTB_REVERSES,
+ TTB_UNDERLINES,
+ TTB_BADWORDS,
+ TTB_CAPS,
+ TTB_FLOOD,
+ TTB_REPEAT,
+ TTB_ITALICS,
+ TTB_AMSGS,
+ TTB_SIZE
+};
+
+class KickerDataImpl : public KickerData
+{
+ friend class KickerDataType;
+
+ ChanServ::Channel *channel = nullptr;
+
+ bool amsgs = false,
+ badwords = false,
+ bolds = false,
+ caps = false,
+ colors = false,
+ flood = false,
+ italics = false,
+ repeat = false,
+ reverses = false,
+ underlines = false;
+
+ int16_t ttb_bolds = 0,
+ ttb_colors = 0,
+ ttb_reverses = 0,
+ ttb_underlines = 0,
+ ttb_badwords = 0,
+ ttb_caps = 0,
+ ttb_flood = 0,
+ ttb_repeat = 0,
+ ttb_italics = 0,
+ ttb_amsgs = 0;
+
+ int16_t capsmin = 0,
+ capspercent = 0,
+ floodlines = 0,
+ floodsecs = 0,
+ repeattimes = 0;
+
+ bool dontkickops = false,
+ dontkickvoices = false;
+
+ public:
+ KickerDataImpl(Serialize::TypeBase *type) : KickerData(type) { }
+ KickerDataImpl(Serialize::TypeBase *type, Serialize::ID id) : KickerData(type, id) { }
+
+ ChanServ::Channel *GetChannel() override;
+ void SetChannel(ChanServ::Channel *) override;
+
+ bool GetAmsgs() override;
+ void SetAmsgs(const bool &) override;
+
+ bool GetBadwords() override;
+ void SetBadwords(const bool &) override;
+
+ bool GetBolds() override;
+ void SetBolds(const bool &) override;
+
+ bool GetCaps() override;
+ void SetCaps(const bool &) override;
+
+ bool GetColors() override;
+ void SetColors(const bool &) override;
+
+ bool GetFlood() override;
+ void SetFlood(const bool &) override;
+
+ bool GetItalics() override;
+ void SetItalics(const bool &) override;
+
+ bool GetRepeat() override;
+ void SetRepeat(const bool &) override;
+
+ bool GetReverses() override;
+ void SetReverses(const bool &) override;
+
+ bool GetUnderlines() override;
+ void SetUnderlines(const bool &) override;
+
+ int16_t GetTTBBolds() override;
+ void SetTTBBolds(const int16_t &) override;
+
+ int16_t GetTTBColors() override;
+ void SetTTBColors(const int16_t &) override;
+
+ int16_t GetTTBReverses() override;
+ void SetTTBReverses(const int16_t &) override;
+
+ int16_t GetTTBUnderlines() override;
+ void SetTTBUnderlines(const int16_t &) override;
+
+ int16_t GetTTBBadwords() override;
+ void SetTTBBadwords(const int16_t &) override;
+
+ int16_t GetTTBCaps() override;
+ void SetTTBCaps(const int16_t &) override;
+
+ int16_t GetTTBFlood() override;
+ void SetTTBFlood(const int16_t &) override;
+
+ int16_t GetTTBRepeat() override;
+ void SetTTBRepeat(const int16_t &) override;
+
+ int16_t GetTTBItalics() override;
+ void SetTTBItalics(const int16_t &) override;
+
+ int16_t GetTTBAmsgs() override;
+ void SetTTBAmsgs(const int16_t &) override;
+
+ int16_t GetCapsMin() override;
+ void SetCapsMin(const int16_t &) override;
+
+ int16_t GetCapsPercent() override;
+ void SetCapsPercent(const int16_t &) override;
+
+ int16_t GetFloodLines() override;
+ void SetFloodLines(const int16_t &) override;
+
+ int16_t GetFloodSecs() override;
+ void SetFloodSecs(const int16_t &) override;
+
+ int16_t GetRepeatTimes() override;
+ void SetRepeatTimes(const int16_t &) override;
+
+ bool GetDontKickOps() override;
+ void SetDontKickOps(const bool &) override;
+
+ bool GetDontKickVoices() override;
+ void SetDontKickVoices(const bool &) override;
+};
+
+class KickerDataType : public Serialize::Type<KickerDataImpl>
+{
+ public:
+ Serialize::ObjectField<KickerDataImpl, ChanServ::Channel *> channel;
+
+ Serialize::Field<KickerDataImpl, bool> amsgs,
+ badwords,
+ bolds,
+ caps,
+ colors,
+ flood,
+ italics,
+ repeat,
+ reverses,
+ underlines;
+
+ Serialize::Field<KickerDataImpl, int16_t> ttb_bolds,
+ ttb_colors,
+ ttb_reverses,
+ ttb_underlines,
+ ttb_badwords,
+ ttb_caps,
+ ttb_flood,
+ ttb_repeat,
+ ttb_italics,
+ ttb_amsgs,
+ capsmin,
+ capspercent,
+ floodlines,
+ floodsecs,
+ repeattimes;
+
+ Serialize::Field<KickerDataImpl, bool> dontkickops,
+ dontkickvoices;
+
+ KickerDataType(Module *owner) : Serialize::Type<KickerDataImpl>(owner)
+ , channel(this, "channel", &KickerDataImpl::channel, true)
+ , amsgs(this, "amsgs", &KickerDataImpl::amsgs)
+ , badwords(this, "badwords", &KickerDataImpl::badwords)
+ , bolds(this, "bolds", &KickerDataImpl::bolds)
+ , caps(this, "caps", &KickerDataImpl::caps)
+ , colors(this, "colors", &KickerDataImpl::colors)
+ , flood(this, "flood", &KickerDataImpl::flood)
+ , italics(this, "italics", &KickerDataImpl::italics)
+ , repeat(this, "repeat", &KickerDataImpl::repeat)
+ , reverses(this, "reverses", &KickerDataImpl::reverses)
+ , underlines(this, "underlines", &KickerDataImpl::underlines)
+ , ttb_bolds(this, "ttb_bolds", &KickerDataImpl::ttb_bolds)
+ , ttb_colors(this, "ttb_colors", &KickerDataImpl::ttb_colors)
+ , ttb_reverses(this, "ttb_reverses", &KickerDataImpl::ttb_reverses)
+ , ttb_underlines(this, "ttb_underlines", &KickerDataImpl::ttb_underlines)
+ , ttb_badwords(this, "ttb_badwords", &KickerDataImpl::ttb_badwords)
+ , ttb_caps(this, "ttb_caps", &KickerDataImpl::ttb_caps)
+ , ttb_flood(this, "ttb_flood", &KickerDataImpl::ttb_flood)
+ , ttb_repeat(this, "ttb_repeat", &KickerDataImpl::ttb_repeat)
+ , ttb_italics(this, "ttb_italics", &KickerDataImpl::ttb_italics)
+ , ttb_amsgs(this, "ttb_amsgs", &KickerDataImpl::ttb_amsgs)
+ , capsmin(this, "capsmin", &KickerDataImpl::capsmin)
+ , capspercent(this, "capspercent", &KickerDataImpl::capspercent)
+ , floodlines(this, "floodlines", &KickerDataImpl::floodlines)
+ , floodsecs(this, "floodsecs", &KickerDataImpl::floodsecs)
+ , repeattimes(this, "repeattimes", &KickerDataImpl::repeattimes)
+ , dontkickops(this, "dontkickops", &KickerDataImpl::dontkickops)
+ , dontkickvoices(this, "dontkickvoices", &KickerDataImpl::dontkickvoices)
+ {
+ }
+};
+
+ChanServ::Channel *KickerDataImpl::GetChannel()
+{
+ return Get(&KickerDataType::channel);
+}
+
+void KickerDataImpl::SetChannel(ChanServ::Channel *channel)
+{
+ Set(&KickerDataType::amsgs, channel);
+}
+
+bool KickerDataImpl::GetAmsgs()
+{
+ return Get(&KickerDataType::amsgs);
+}
+
+void KickerDataImpl::SetAmsgs(const bool &b)
+{
+ Set(&KickerDataType::amsgs, b);
+}
+
+bool KickerDataImpl::GetBadwords()
+{
+ return Get(&KickerDataType::badwords);
+}
+
+void KickerDataImpl::SetBadwords(const bool &b)
+{
+ Set(&KickerDataType::badwords, b);
+}
+
+bool KickerDataImpl::GetBolds()
+{
+ return Get(&KickerDataType::bolds);
+}
+
+void KickerDataImpl::SetBolds(const bool &b)
+{
+ Set(&KickerDataType::bolds, b);
+}
+
+bool KickerDataImpl::GetCaps()
+{
+ return Get(&KickerDataType::caps);
+}
+
+void KickerDataImpl::SetCaps(const bool &b)
+{
+ Set(&KickerDataType::caps, b);
+}
+
+bool KickerDataImpl::GetColors()
+{
+ return Get(&KickerDataType::colors);
+}
+
+void KickerDataImpl::SetColors(const bool &b)
+{
+ Set(&KickerDataType::colors, b);
+}
+
+bool KickerDataImpl::GetFlood()
+{
+ return Get(&KickerDataType::flood);
+}
+
+void KickerDataImpl::SetFlood(const bool &b)
+{
+ Set(&KickerDataType::flood, b);
+}
+
+bool KickerDataImpl::GetItalics()
+{
+ return Get(&KickerDataType::italics);
+}
+
+void KickerDataImpl::SetItalics(const bool &b)
+{
+ Set(&KickerDataType::italics, b);
+}
+
+bool KickerDataImpl::GetRepeat()
+{
+ return Get(&KickerDataType::repeat);
+}
+
+void KickerDataImpl::SetRepeat(const bool &b)
+{
+ Set(&KickerDataType::repeat, b);
+}
+
+bool KickerDataImpl::GetReverses()
+{
+ return Get(&KickerDataType::reverses);
+}
+
+void KickerDataImpl::SetReverses(const bool &b)
+{
+ Set(&KickerDataType::reverses, b);
+}
+
+bool KickerDataImpl::GetUnderlines()
+{
+ return Get(&KickerDataType::underlines);
+}
+
+void KickerDataImpl::SetUnderlines(const bool &b)
+{
+ Set(&KickerDataType::underlines, b);
+}
+
+int16_t KickerDataImpl::GetTTBBolds()
+{
+ return Get(&KickerDataType::ttb_bolds);
+}
+
+void KickerDataImpl::SetTTBBolds(const int16_t &i)
+{
+ Set(&KickerDataType::ttb_bolds, i);
+}
+
+int16_t KickerDataImpl::GetTTBColors()
+{
+ return Get(&KickerDataType::ttb_colors);
+}
+
+void KickerDataImpl::SetTTBColors(const int16_t &i)
+{
+ Set(&KickerDataType::ttb_colors, i);
+}
+
+int16_t KickerDataImpl::GetTTBReverses()
+{
+ return Get(&KickerDataType::ttb_reverses);
+}
+
+void KickerDataImpl::SetTTBReverses(const int16_t &i)
+{
+ Set(&KickerDataType::ttb_reverses, i);
+}
+
+int16_t KickerDataImpl::GetTTBUnderlines()
+{
+ return Get(&KickerDataType::ttb_underlines);
+}
+
+void KickerDataImpl::SetTTBUnderlines(const int16_t &i)
+{
+ Set(&KickerDataType::ttb_underlines, i);
+}
+
+int16_t KickerDataImpl::GetTTBBadwords()
+{
+ return Get(&KickerDataType::ttb_badwords);
+}
+
+void KickerDataImpl::SetTTBBadwords(const int16_t &i)
+{
+ Set(&KickerDataType::ttb_badwords, i);
+}
+
+int16_t KickerDataImpl::GetTTBCaps()
+{
+ return Get(&KickerDataType::ttb_caps);
+}
+
+void KickerDataImpl::SetTTBCaps(const int16_t &i)
+{
+ Set(&KickerDataType::ttb_caps, i);
+}
+
+int16_t KickerDataImpl::GetTTBFlood()
+{
+ return Get(&KickerDataType::ttb_flood);
+}
+
+void KickerDataImpl::SetTTBFlood(const int16_t &i)
+{
+ Set(&KickerDataType::ttb_flood, i);
+}
+
+int16_t KickerDataImpl::GetTTBRepeat()
+{
+ return Get(&KickerDataType::ttb_repeat);
+}
+
+void KickerDataImpl::SetTTBRepeat(const int16_t &i)
+{
+ Set(&KickerDataType::ttb_repeat, i);
+}
+
+int16_t KickerDataImpl::GetTTBItalics()
+{
+ return Get(&KickerDataType::ttb_italics);
+}
+
+void KickerDataImpl::SetTTBItalics(const int16_t &i)
+{
+ Set(&KickerDataType::ttb_italics, i);
+}
+
+int16_t KickerDataImpl::GetTTBAmsgs()
+{
+ return Get(&KickerDataType::ttb_amsgs);
+}
+
+void KickerDataImpl::SetTTBAmsgs(const int16_t &i)
+{
+ Set(&KickerDataType::ttb_amsgs, i);
+}
+
+int16_t KickerDataImpl::GetCapsMin()
+{
+ return Get(&KickerDataType::capsmin);
+}
+
+void KickerDataImpl::SetCapsMin(const int16_t &i)
+{
+ Set(&KickerDataType::capsmin, i);
+}
+
+int16_t KickerDataImpl::GetCapsPercent()
+{
+ return Get(&KickerDataType::capspercent);
+}
+
+void KickerDataImpl::SetCapsPercent(const int16_t &i)
+{
+ Set(&KickerDataType::capspercent, i);
+}
+
+int16_t KickerDataImpl::GetFloodLines()
+{
+ return Get(&KickerDataType::floodlines);
+}
+
+void KickerDataImpl::SetFloodLines(const int16_t &i)
+{
+ Set(&KickerDataType::floodlines, i);
+}
+
+int16_t KickerDataImpl::GetFloodSecs()
+{
+ return Get(&KickerDataType::floodsecs);
+}
+
+void KickerDataImpl::SetFloodSecs(const int16_t &i)
+{
+ Set(&KickerDataType::floodsecs, i);
+}
+
+int16_t KickerDataImpl::GetRepeatTimes()
+{
+ return Get(&KickerDataType::repeattimes);
+}
+
+void KickerDataImpl::SetRepeatTimes(const int16_t &i)
+{
+ Set(&KickerDataType::repeattimes, i);
+}
+
+bool KickerDataImpl::GetDontKickOps()
+{
+ return Get(&KickerDataType::dontkickops);
+}
+
+void KickerDataImpl::SetDontKickOps(const bool &b)
+{
+ Set(&KickerDataType::dontkickops, b);
+}
+
+bool KickerDataImpl::GetDontKickVoices()
+{
+ return Get(&KickerDataType::dontkickvoices);
+}
+
+void KickerDataImpl::SetDontKickVoices(const bool &b)
+{
+ Set(&KickerDataType::dontkickvoices, b);
+}
+
+class CommandBSKick : public Command
+{
+ public:
+ CommandBSKick(Module *creator) : Command(creator, "botserv/kick", 0)
+ {
+ this->SetDesc(_("Configures kickers"));
+ this->SetSyntax(_("\037option\037 \037channel\037 {\037ON|OFF\037} [\037settings\037]"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ this->OnSyntaxError(source, "");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Configures bot kickers."
+ " Use of this command requires the \002SET\002 privilege on \037channel\037.\n"
+ "\n"
+ "Available kickers:"));
+
+ Anope::string this_name = source.command;
+ for (CommandInfo::map::const_iterator it = source.service->commands.begin(), it_end = source.service->commands.end(); it != it_end; ++it)
+ {
+ const Anope::string &c_name = it->first;
+ const CommandInfo &info = it->second;
+
+ if (c_name.find_ci(this_name + " ") == 0)
+ {
+ ServiceReference<Command> command(info.name);
+ if (command)
+ {
+ source.command = c_name;
+ command->OnServHelp(source);
+ }
+ }
+ }
+
+ CommandInfo *help = source.service->FindCommand("generic/help");
+ if (help)
+ source.Reply(_("See \002{0}{1} {2} {3} \037option\037\002 for more information on a specific option."),
+ Config->StrictPrivmsg, source.service->nick, help->cname, this_name);
+
+ return true;
+ }
+};
+
+class CommandBSKickBase : public Command
+{
+ public:
+ CommandBSKickBase(Module *creator, const Anope::string &cname, int minarg, int maxarg) : Command(creator, cname, minarg, maxarg)
+ {
+ }
+
+ virtual void Execute(CommandSource &source, const std::vector<Anope::string> &params) override anope_abstract;
+
+ virtual bool OnHelp(CommandSource &source, const Anope::string &subcommand) override anope_abstract;
+
+ protected:
+ bool CheckArguments(CommandSource &source, const std::vector<Anope::string> &params, ChanServ::Channel* &ci)
+ {
+ const Anope::string &chan = params[0];
+ const Anope::string &option = params[1];
+
+ ci = ChanServ::Find(chan);
+
+ if (Anope::ReadOnly)
+ source.Reply(_("Sorry, kicker configuration is temporarily disabled."));
+ else if (ci == NULL)
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
+ else if (option.empty())
+ this->OnSyntaxError(source, "");
+ else if (!option.equals_ci("ON") && !option.equals_ci("OFF"))
+ this->OnSyntaxError(source, "");
+ else if (!source.AccessFor(ci).HasPriv("SET") && !source.HasPriv("botserv/administration"))
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "SET", ci->GetName());
+ else if (!ci->GetBot())
+ source.Reply(_("There is no bot assigned to \002{0}\002."), ci->GetName());
+ else
+ return true;
+
+ return false;
+ }
+
+ bool CheckTTB(CommandSource &source, const Anope::string &ttb, int16_t &i)
+ {
+ i = 0;
+ if (ttb.empty())
+ return true;
+
+ try
+ {
+ i = convertTo<int16_t>(ttb);
+ if (i < 0)
+ throw ConvertException();
+ }
+ catch (const ConvertException &)
+ {
+ i = 0;
+ source.Reply(_("\002{0}\002 can not be taken as times to ban. Times to ban must be a positive integer."), ttb);
+ return false;
+ }
+
+ return true;
+ }
+
+ void Process(CommandSource &source, ChanServ::Channel *ci, const Anope::string &param, const Anope::string &ttb, void (KickerData::*setter)(const bool &), void (KickerData::*ttbsetter)(const int16_t &), const Anope::string &optname)
+ {
+ KickerData *kd = ci->GetRef<KickerData *>();
+
+ if (param.equals_ci("ON"))
+ {
+ int16_t i;
+ if (!CheckTTB(source, ttb, i))
+ return;
+
+ (kd->*setter)(true);
+ (kd->*ttbsetter)(i);
+
+ if (i)
+ source.Reply(_("Bot will now kick for \002{0}\002, and will place a ban after \002{1}\002 kicks for the same user."), optname, i);
+ else
+ source.Reply(_("Bot will now kick for \002{0}\002."), optname);
+
+ bool override = !source.AccessFor(ci).HasPriv("SET");
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to enable the " << optname << " kicker";
+ }
+ else if (param.equals_ci("OFF"))
+ {
+ bool override = !source.AccessFor(ci).HasPriv("SET");
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to disable the " << optname << " kicker";
+
+ (kd->*setter)(false);
+ (kd->*ttbsetter)(0);
+
+ source.Reply(_("Bot won't kick for \002{0}\002 anymore."), optname);
+ }
+ else
+ this->OnSyntaxError(source, "");
+ }
+};
+
+class CommandBSKickAMSG : public CommandBSKickBase
+{
+ public:
+ CommandBSKickAMSG(Module *creator) : CommandBSKickBase(creator, "botserv/kick/amsg", 2, 3)
+ {
+ this->SetDesc(_("Configures AMSG kicker"));
+ this->SetSyntax(_("\037channel\037 {\037ON|OFF\037} [\037ttb\037]"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ ChanServ::Channel *ci;
+ if (CheckArguments(source, params, ci))
+ Process(source, ci, params[1], params.size() > 2 ? params[2] : "", &KickerData::SetAmsgs, &KickerData::SetTTBAmsgs, "amsgs");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Sets the AMSG kicker on or off on \037channel\037. When enabled, the bot will kick users who send the same message to multiple channels where {0} bots are.\n"
+ "\n"
+ "\037ttb\037 is the number of times a user can be kicked before they get banned. Don't give ttb to disable the ban system."),
+ source.service->nick);
+ return true;
+ }
+};
+
+class CommandBSKickBadwords : public CommandBSKickBase
+{
+ public:
+ CommandBSKickBadwords(Module *creator) : CommandBSKickBase(creator, "botserv/kick/badwords", 2, 3)
+ {
+ this->SetDesc(_("Configures badwords kicker"));
+ this->SetSyntax(_("\037channel\037 {\037ON|OFF\037} [\037ttb\037]"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ ChanServ::Channel *ci;
+ if (CheckArguments(source, params, ci))
+ Process(source, ci, params[1], params.size() > 2 ? params[2] : "", &KickerData::SetBadwords, &KickerData::SetTTBBadwords, "badwords");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ ServiceBot *bi;
+ Anope::string name;
+ CommandInfo *help;
+ source.Reply(_("Sets the bad words kicker on or off on \037channel\037. When enabled, the bot will kick users who say certain words on the channel."));
+ if (Command::FindCommandFromService("botserv/badwords", bi, name) && (help = bi->FindCommand("generic/help")))
+ source.Reply(_("You can define bad words for your channel using the \002{0}\002 command. See \002{1}{2} {3} {4}\002 for more information."),
+ name, Config->StrictPrivmsg, bi->nick, help->cname, name);
+ source.Reply(_("\n"
+ "\037ttb\037 is the number of times a user can be kicked before they get banned. Don't give ttb to disable the ban system."));
+ return true;
+ }
+};
+
+class CommandBSKickBolds : public CommandBSKickBase
+{
+ public:
+ CommandBSKickBolds(Module *creator) : CommandBSKickBase(creator, "botserv/kick/bolds", 2, 3)
+ {
+ this->SetDesc(_("Configures bolds kicker"));
+ this->SetSyntax(_("\037channel\037 {\037ON|OFF\037} [\037ttb\037]"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ ChanServ::Channel *ci;
+ if (CheckArguments(source, params, ci))
+ Process(source, ci, params[1], params.size() > 2 ? params[2] : "", &KickerData::SetBolds, &KickerData::SetTTBBolds, "bolds");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Sets the bolds kicker on or off on \037channel\037. When enabled, the bot will kick users who use \002bolds\002.\n"
+ "\n"
+ "\037ttb\037 is the number of times a user can be kicked before they get banned. Don't give ttb to disable the ban system."));
+ return true;
+ }
+};
+
+class CommandBSKickCaps : public CommandBSKickBase
+{
+ public:
+ CommandBSKickCaps(Module *creator) : CommandBSKickBase(creator, "botserv/kick/caps", 2, 5)
+ {
+ this->SetDesc(_("Configures caps kicker"));
+ this->SetSyntax(_("\037channel\037 {\037ON|OFF\037} [\037ttb\037 [\037min\037 [\037percent\037]]]\002"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ ChanServ::Channel *ci;
+ if (!CheckArguments(source, params, ci))
+ return;
+
+ KickerData *kd = GetKickerData(ci);
+
+ if (params[1].equals_ci("ON"))
+ {
+ const Anope::string &ttb = params.size() > 2 ? params[2] : "",
+ &min = params.size() > 3 ? params[3] : "",
+ &percent = params.size() > 4 ? params[4] : "";
+
+ int16_t i;
+ if (!CheckTTB(source, ttb, i))
+ return;
+
+ kd->SetCaps(true);
+ kd->SetTTBCaps(i);
+
+ kd->SetCapsMin(10);
+ try
+ {
+ kd->SetCapsMin(convertTo<int16_t>(min));
+ }
+ catch (const ConvertException &) { }
+ if (kd->GetCapsMin() < 1)
+ kd->SetCapsMin(10);
+
+ try
+ {
+ kd->SetCapsPercent(convertTo<int16_t>(percent));
+ }
+ catch (const ConvertException &) { }
+ if (kd->GetCapsPercent() < 1 || kd->GetCapsPercent() > 100)
+ kd->SetCapsPercent(25);
+
+ if (i)
+ source.Reply(_("Bot will now kick for \002caps\002 if they constitute at least {0} characters and {1}% of the entire message, and will place a ban after {2} kicks for the same user."), kd->GetCapsMin(), kd->GetCapsPercent(), i);
+ else
+ source.Reply(_("Bot will now kick for \002caps\002 if they constitute at least {0} characters and {1}% of the entire message."), kd->GetCapsMin(), kd->GetCapsPercent());
+ }
+ else
+ {
+ kd->SetCaps(false);
+ kd->SetTTBCaps(0);
+ kd->SetCapsMin(0);
+ kd->SetCapsPercent(0);
+ source.Reply(_("Bot won't kick for \002caps\002 anymore."));
+ }
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Sets the caps kicker on or off on \037channel\037. When enabled, the bot will kick users who talk in CAPS."
+ " The bot kicks only if there are at least \002min\002 caps and they constitute at least \002percent\002% of the total text line."
+ " (if not given, it defaults to 10 characters and 25%).\n"
+ "\n"
+ "\037ttb\037 is the number of times a user can be kicked before they get banned. Don't give ttb to disable the ban system."));
+ return true;
+ }
+};
+
+class CommandBSKickColors : public CommandBSKickBase
+{
+ public:
+ CommandBSKickColors(Module *creator) : CommandBSKickBase(creator, "botserv/kick/colors", 2, 3)
+ {
+ this->SetDesc(_("Configures color kicker"));
+ this->SetSyntax(_("\037channel\037 {\037ON|OFF\037} [\037ttb\037]"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ ChanServ::Channel *ci;
+ if (CheckArguments(source, params, ci))
+ Process(source, ci, params[1], params.size() > 2 ? params[2] : "", &KickerData::SetColors, &KickerData::SetTTBColors, "colors");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Sets the colors kicker on or off on \037channel\037. When enabled, the bot will kick users who use \00304c\00307o\00308l\00303o\00306r\00312s\017.\n"
+ "\n"
+ "\037ttb\037 is the number of times a user can be kicked before they get banned. Don't give ttb to disable the ban system."));
+ return true;
+ }
+};
+
+class CommandBSKickFlood : public CommandBSKickBase
+{
+ public:
+ CommandBSKickFlood(Module *creator) : CommandBSKickBase(creator, "botserv/kick/flood", 2, 5)
+ {
+ this->SetDesc(_("Configures flood kicker"));
+ this->SetSyntax(_("\037channel\037 {\037ON|OFF\037} [\037ttb\037 [\037lines\037 [\037seconds\037]]]\002"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ ChanServ::Channel *ci;
+ if (!CheckArguments(source, params, ci))
+ return;
+
+ KickerData *kd = GetKickerData(ci);
+
+ if (params[1].equals_ci("ON"))
+ {
+ const Anope::string &ttb = params.size() > 2 ? params[2] : "",
+ &lines = params.size() > 3 ? params[3] : "",
+ &secs = params.size() > 4 ? params[4] : "";
+
+ int16_t i;
+ if (!CheckTTB(source, ttb, i))
+ return;
+
+ kd->SetFlood(true);
+ kd->SetTTBFlood(i);
+
+ kd->SetFloodLines(6);
+ try
+ {
+ kd->SetFloodLines(convertTo<int16_t>(lines));
+ }
+ catch (const ConvertException &) { }
+ if (kd->GetFloodLines() < 2)
+ kd->SetFloodLines(6);
+
+ kd->SetFloodSecs(10);
+ try
+ {
+ kd->SetFloodSecs(convertTo<int16_t>(secs));
+ }
+ catch (const ConvertException &) { }
+ if (kd->GetFloodSecs() < 1)
+ kd->SetFloodSecs(10);
+ if (kd->GetFloodSecs() > Config->GetModule(me)->Get<time_t>("keepdata"))
+ kd->SetFloodSecs(Config->GetModule(me)->Get<time_t>("keepdata"));
+
+ if (i)
+ source.Reply(_("Bot will now kick for \002flood\002 ({0} lines in {1} seconds and will place a ban after {2} kicks for the same user."), kd->GetFloodLines(), kd->GetFloodSecs(), i);
+ else
+ source.Reply(_("Bot will now kick for \002flood\002 ({0} lines in {1} seconds)."), kd->GetFloodLines(), kd->GetFloodSecs());
+ }
+ else if (params[1].equals_ci("OFF"))
+ {
+ kd->SetFlood(false);
+ kd->SetFloodLines(0);
+ kd->SetFloodSecs(0);
+ source.Reply(_("Bot won't kick for \002flood\002 anymore."));
+ }
+ else
+ this->OnSyntaxError(source, params[1]);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Sets the flood kicker on or off on \037channel\037. When enabled, the bot to kick users who are flooding the channel with at least \037lines\037 lines in \037seconds\037 seconds. If not given, it defaults to 6 lines in 10 seconds.\n"
+ "\n"
+ "\037ttb\037 is the number of times a user can be kicked before they get banned. Don't give ttb to disable the ban system."));
+ return true;
+ }
+};
+
+class CommandBSKickItalics : public CommandBSKickBase
+{
+ public:
+ CommandBSKickItalics(Module *creator) : CommandBSKickBase(creator, "botserv/kick/italics", 2, 3)
+ {
+ this->SetDesc(_("Configures italics kicker"));
+ this->SetSyntax(_("\037channel\037 {\037ON|OFF\037} [\037ttb\037]"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ ChanServ::Channel *ci;
+ if (CheckArguments(source, params, ci))
+ Process(source, ci, params[1], params.size() > 2 ? params[2] : "", &KickerData::SetItalics, &KickerData::SetTTBItalics, "itlaics");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Sets the italics kicker on or off on \037channel\037. When enabled, the bot will kick users who use \035italics\035."
+ "\n"
+ "\037ttb\037 is the number of times a user can be kicked before they get banned. Don't give ttb to disable the ban system."));
+ return true;
+ }
+};
+
+class CommandBSKickRepeat : public CommandBSKickBase
+{
+ public:
+ CommandBSKickRepeat(Module *creator) : CommandBSKickBase(creator, "botserv/kick/repeat", 2, 4)
+ {
+ this->SetDesc(_("Configures repeat kicker"));
+ this->SetSyntax(_("\037channel\037 {\037ON|OFF\037} [\037ttb\037 [\037num\037]]\002"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ ChanServ::Channel *ci;
+ if (!CheckArguments(source, params, ci))
+ return;
+
+ KickerData *kd = GetKickerData(ci);
+
+ if (params[1].equals_ci("ON"))
+ {
+ const Anope::string &ttb = params.size() > 2 ? params[2] : "",
+ &times = params.size() > 3 ? params[3] : "";
+
+ int16_t i;
+ if (!CheckTTB(source, ttb, i))
+ return;
+
+ kd->SetRepeat(true);
+ kd->SetTTBRepeat(i);
+ kd->SetRepeatTimes(3);
+ try
+ {
+ kd->SetRepeatTimes(convertTo<int16_t>(times));
+ }
+ catch (const ConvertException &) { }
+ if (kd->GetRepeatTimes() < 1)
+ kd->SetRepeatTimes(3);
+
+ if (i)
+ {
+ if (kd->GetRepeatTimes() != 1)
+ source.Reply(_("Bot will now kick for \002repeats\002 (users that say the same thing {0} times),"
+ " and will place a ban after {1} kicks for the same user."), kd->GetRepeatTimes(), i);
+ else
+ source.Reply(_("Bot will now kick for \002repeats\002 (users that say the same thing {0} time),"
+ " and will place a ban after {1} kicks for the same user."), kd->GetRepeatTimes(), i);
+ }
+ else
+ {
+ if (kd->GetRepeatTimes() != 1)
+ source.Reply(_("Bot will now kick for \002repeats\002 (users that say the same thing {0} times)."), kd->GetRepeatTimes());
+ else
+ source.Reply(_("Bot will now kick for \002repeats\002 (users that say the same thing {0} time)."), kd->GetRepeatTimes());
+
+ }
+ }
+ else if (params[1].equals_ci("OFF"))
+ {
+ kd->SetRepeat(false);
+ kd->SetTTBRepeat(0);
+ kd->SetRepeatTimes(0);
+ source.Reply(_("Bot won't kick for \002repeats\002 anymore."));
+ }
+ else
+ this->OnSyntaxError(source, params[1]);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Sets the repeat kicker on or off. When enabled, the bot will kick users who repeat themselves \037num\037 times. If \037num\037 is not given, it defaults to 3.\n"
+ "\n"
+ "\037ttb\037 is the number of times a user can be kicked before they get banned. Don't give ttb to disable the ban system."));
+ return true;
+ }
+};
+
+class CommandBSKickReverses : public CommandBSKickBase
+{
+ public:
+ CommandBSKickReverses(Module *creator) : CommandBSKickBase(creator, "botserv/kick/reverses", 2, 3)
+ {
+ this->SetDesc(_("Configures reverses kicker"));
+ this->SetSyntax(_("\037channel\037 {\037ON|OFF\037} [\037ttb\037]"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ ChanServ::Channel *ci;
+ if (CheckArguments(source, params, ci))
+ Process(source, ci, params[1], params.size() > 2 ? params[2] : "", &KickerData::SetReverses, &KickerData::SetTTBReverses, "reverses");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Sets the reverses kicker on or off. When enabled, the bot will kick users who use \026reverses\026.\n"
+ "\n"
+ "\037ttb\037 is the number of times a user can be kicked before they get banned. Don't give ttb to disable the ban system."));
+ return true;
+ }
+};
+
+class CommandBSKickUnderlines : public CommandBSKickBase
+{
+ public:
+ CommandBSKickUnderlines(Module *creator) : CommandBSKickBase(creator, "botserv/kick/underlines", 2, 3)
+ {
+ this->SetDesc(_("Configures underlines kicker"));
+ this->SetSyntax(_("\037channel\037 {\037ON|OFF\037} [\037ttb\037]"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ ChanServ::Channel *ci;
+ if (CheckArguments(source, params, ci))
+ Process(source, ci, params[1], params.size() > 2 ? params[2] : "", &KickerData::SetUnderlines, &KickerData::SetTTBUnderlines, "underlines");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Sets the underlines kicker on or off. When enabled, the bot will kick users who use \037underlines\037\n"
+ "\n"
+ "\037ttb\037 is the number of times a user can be kicked before they get banned. Don't give ttb to disable the ban system."));
+ return true;
+ }
+};
+
+class CommandBSSetDontKickOps : public Command
+{
+ public:
+ CommandBSSetDontKickOps(Module *creator, const Anope::string &sname = "botserv/set/dontkickops") : Command(creator, sname, 2, 2)
+ {
+ this->SetDesc(_("To protect ops against bot kicks"));
+ this->SetSyntax(_("\037channel\037 {ON | OFF}"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ ChanServ::Channel *ci = ChanServ::Find(params[0]);
+ if (ci == NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), params[0]);
+ return;
+ }
+
+ ChanServ::AccessGroup access = source.AccessFor(ci);
+ if (!source.HasPriv("botserv/administration") && !access.HasPriv("SET"))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "SET", ci->GetName());
+ return;
+ }
+
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Sorry, bot option setting is temporarily disabled."));
+ return;
+ }
+
+ KickerData *kd = GetKickerData(ci);
+
+ if (params[1].equals_ci("ON"))
+ {
+ bool override = !access.HasPriv("SET");
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to enable dontkickops";
+
+ kd->SetDontKickOps(true);
+ source.Reply(_("Bot \002won't kick ops\002 on channel \002{0}\002."), ci->GetName());
+ }
+ else if (params[1].equals_ci("OFF"))
+ {
+ bool override = !access.HasPriv("SET");
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to disable dontkickops";
+
+ kd->SetDontKickOps(false);
+ source.Reply(_("Bot \002will kick ops\002 on channel \002{0}\002."), ci->GetName());
+ }
+ else
+ this->OnSyntaxError(source, source.command);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
+ {
+ source.Reply(_("Enables or disables \002op protection\002 mode on a channel."
+ " When it is enabled, ops won't be kicked by the bot, even if they don't have the \002{0}\002 privilege.\n"
+ "\n"
+ "Use of this command requires the \002{1}\002 privilege on \037channel\037."),
+ "NOKICK", "SET");
+ return true;
+ }
+};
+
+class CommandBSSetDontKickVoices : public Command
+{
+ public:
+ CommandBSSetDontKickVoices(Module *creator, const Anope::string &sname = "botserv/set/dontkickvoices") : Command(creator, sname, 2, 2)
+ {
+ this->SetDesc(_("To protect voices against bot kicks"));
+ this->SetSyntax(_("\037channel\037 {ON | OFF}"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ ChanServ::Channel *ci = ChanServ::Find(params[0]);
+ if (ci == NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), params[0]);
+ return;
+ }
+
+ ChanServ::AccessGroup access = source.AccessFor(ci);
+ if (!source.HasPriv("botserv/administration") && !access.HasPriv("SET"))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "SET", ci->GetName());
+ return;
+ }
+
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Sorry, bot option setting is temporarily disabled."));
+ return;
+ }
+
+ KickerData *kd = GetKickerData(ci);
+
+ if (params[1].equals_ci("ON"))
+ {
+ bool override = !access.HasPriv("SET");
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to enable dontkickvoices";
+
+ kd->SetDontKickVoices(true);
+ source.Reply(_("Bot \002won't kick voices\002 on channel %s."), ci->GetName().c_str());
+ }
+ else if (params[1].equals_ci("OFF"))
+ {
+ bool override = !access.HasPriv("SET");
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to disable dontkickvoices";
+
+ kd->SetDontKickVoices(false);
+ source.Reply(_("Bot \002will kick voices\002 on channel %s."), ci->GetName().c_str());
+ }
+ else
+ this->OnSyntaxError(source, source.command);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
+ {
+ source.Reply(_("Enables or disables \002voice protection\002 mode on a channel."
+ " When it is enabled, ops won't be kicked by the bot, even if they don't have the \002{0}\002 privilege.\n"
+ "\n"
+ "Use of this command requires the \002{1}\002 privilege on \037channel\037."),
+ "NOKICK", "SET");
+ return true;
+ }
+};
+
+struct BanData
+{
+ struct Data
+ {
+ Anope::string mask;
+ time_t last_use;
+ int16_t ttb[TTB_SIZE];
+
+ Data()
+ {
+ last_use = 0;
+ for (int i = 0; i < TTB_SIZE; ++i)
+ this->ttb[i] = 0;
+ }
+ };
+
+ private:
+ typedef Anope::map<Data> data_type;
+ data_type data_map;
+
+ public:
+ Data &get(const Anope::string &key)
+ {
+ return this->data_map[key];
+ }
+
+ bool empty() const
+ {
+ return this->data_map.empty();
+ }
+
+ void purge()
+ {
+ time_t keepdata = Config->GetModule(me)->Get<time_t>("keepdata");
+ for (data_type::iterator it = data_map.begin(), it_end = data_map.end(); it != it_end;)
+ {
+ const Anope::string &user = it->first;
+ Data &bd = it->second;
+ ++it;
+
+ if (Anope::CurTime - bd.last_use > keepdata)
+ data_map.erase(user);
+ }
+ }
+};
+
+struct UserData
+{
+ UserData()
+ {
+ last_use = last_start = Anope::CurTime;
+ }
+
+ /* Data validity */
+ time_t last_use;
+
+ /* for flood kicker */
+ int16_t lines = 0;
+ time_t last_start;
+
+ /* for repeat kicker */
+ Anope::string lasttarget;
+ int16_t times = 0;
+
+ Anope::string lastline;
+};
+
+class BanDataPurger : public Timer
+{
+ public:
+ BanDataPurger(Module *o) : Timer(o, 300, Anope::CurTime, true) { }
+
+ void Tick(time_t) override
+ {
+ Log(LOG_DEBUG) << "bs_main: Running bandata purger";
+
+ for (channel_map::iterator it = ChannelList.begin(), it_end = ChannelList.end(); it != it_end; ++it)
+ {
+ Channel *c = it->second;
+
+ BanData *bd = c->GetExt<BanData>("bandata");
+ if (bd != NULL)
+ {
+ bd->purge();
+ if (bd->empty())
+ c->Shrink<BanData>("bandata");
+ }
+ }
+ }
+};
+
+class BSKick : public Module
+ , public EventHook<Event::ServiceBotEvent>
+ , public EventHook<Event::Privmsg>
+{
+ ExtensibleItem<BanData> bandata;
+ ExtensibleItem<UserData> userdata;
+
+ KickerDataType kdtype;
+
+ CommandBSKick commandbskick;
+ CommandBSKickAMSG commandbskickamsg;
+ CommandBSKickBadwords commandbskickbadwords;
+ CommandBSKickBolds commandbskickbolds;
+ CommandBSKickCaps commandbskickcaps;
+ CommandBSKickColors commandbskickcolors;
+ CommandBSKickFlood commandbskickflood;
+ CommandBSKickItalics commandbskickitalics;
+ CommandBSKickRepeat commandbskickrepeat;
+ CommandBSKickReverses commandbskickreverse;
+ CommandBSKickUnderlines commandbskickunderlines;
+
+ CommandBSSetDontKickOps commandbssetdontkickops;
+ CommandBSSetDontKickVoices commandbssetdontkickvoices;
+
+ BanDataPurger purger;
+
+ ServiceReference<BadWords> badwords;
+
+ BanData::Data &GetBanData(User *u, Channel *c)
+ {
+ BanData *bd = bandata.Require(c);
+ return bd->get(u->GetMask());
+ }
+
+ UserData *GetUserData(User *u, Channel *c)
+ {
+ ChanUserContainer *uc = c->FindUser(u);
+ if (uc == nullptr)
+ return nullptr;
+
+ return userdata.Require(uc);
+ }
+
+ void TakeAction(ChanServ::Channel *ci, User *u, int ttb, TTBType ttbtype, const char *message, ...)
+ {
+ /* Don't ban ulines or protected users */
+ if (u->IsProtected())
+ return;
+
+ BanData::Data &bd = this->GetBanData(u, ci->c);
+
+ ++bd.ttb[ttbtype];
+ if (ttb && bd.ttb[ttbtype] >= ttb)
+ {
+ bd.ttb[ttbtype] = 0;
+
+ Anope::string mask = ci->GetIdealBan(u);
+
+ ci->c->SetMode(NULL, "BAN", mask);
+ EventManager::Get()->Dispatch(&Event::BotBan::OnBotBan, u, ci, mask);
+ }
+
+ if (!ci->c->FindUser(u))
+ return;
+
+ va_list args;
+ char buf[1024];
+
+ Anope::string fmt = Language::Translate(u, message);
+ va_start(args, message);
+ vsnprintf(buf, sizeof(buf), fmt.c_str(), args);
+ va_end(args);
+
+ ci->c->Kick(ci->GetBot(), u, "%s", buf);
+ }
+
+ public:
+ BSKick(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , EventHook<Event::ServiceBotEvent>(this)
+ , EventHook<Event::Privmsg>(this)
+
+ , bandata(this, "bandata")
+ , userdata(this, "userdata")
+
+ , kdtype(this)
+
+ , commandbskick(this)
+ , commandbskickamsg(this)
+ , commandbskickbadwords(this)
+ , commandbskickbolds(this)
+ , commandbskickcaps(this)
+ , commandbskickcolors(this)
+ , commandbskickflood(this)
+ , commandbskickitalics(this)
+ , commandbskickrepeat(this)
+ , commandbskickreverse(this)
+ , commandbskickunderlines(this)
+
+ , commandbssetdontkickops(this)
+ , commandbssetdontkickvoices(this)
+
+ , purger(this)
+ {
+ me = this;
+ }
+
+ void OnServiceBot(CommandSource &source, ServiceBot *bi, ChanServ::Channel *ci, InfoFormatter &info) override
+ {
+ if (!ci)
+ return;
+
+ Anope::string enabled = Language::Translate(source.nc, _("Enabled"));
+ Anope::string disabled = Language::Translate(source.nc, _("Disabled"));
+ KickerData *kd = ci->GetRef<KickerData *>();
+
+ if (kd && kd->GetBadwords())
+ {
+ if (kd->GetTTBBadwords())
+ info[_("Bad words kicker")] = Anope::printf("%s (%d kick(s) to ban)", enabled.c_str(), kd->GetBadwords());
+ else
+ info[_("Bad words kicker")] = enabled;
+ }
+ else
+ info[_("Bad words kicker")] = disabled;
+
+ if (kd && kd->GetBolds())
+ {
+ if (kd->GetTTBBolds())
+ info[_("Bolds kicker")] = Anope::printf("%s (%d kick(s) to ban)", enabled.c_str(), kd->GetTTBBolds());
+ else
+ info[_("Bolds kicker")] = enabled;
+ }
+ else
+ info[_("Bolds kicker")] = disabled;
+
+ if (kd && kd->GetCaps())
+ {
+ if (kd->GetTTBCaps())
+ info[_("Caps kicker")] = Anope::printf(_("%s (%d kick(s) to ban; minimum %d/%d%%)"), enabled.c_str(), kd->GetTTBCaps(), kd->GetCapsMin(), kd->GetCapsPercent());
+ else
+ info[_("Caps kicker")] = Anope::printf(_("%s (minimum %d/%d%%)"), enabled.c_str(), kd->GetCapsMin(), kd->GetCapsPercent());
+ }
+ else
+ info[_("Caps kicker")] = disabled;
+
+ if (kd && kd->GetColors())
+ {
+ if (kd->GetTTBColors())
+ info[_("Colors kicker")] = Anope::printf(_("%s (%d kick(s) to ban)"), enabled.c_str(), kd->GetTTBColors());
+ else
+ info[_("Colors kicker")] = enabled;
+ }
+ else
+ info[_("Colors kicker")] = disabled;
+
+ if (kd && kd->GetFlood())
+ {
+ if (kd->GetTTBFlood())
+ info[_("Flood kicker")] = Anope::printf(_("%s (%d kick(s) to ban; %d lines in %ds)"), enabled.c_str(), kd->GetTTBFlood(), kd->GetFloodLines(), kd->GetFloodSecs());
+ else
+ info[_("Flood kicker")] = Anope::printf(_("%s (%d lines in %ds)"), enabled.c_str(), kd->GetFloodLines(), kd->GetFloodSecs());
+ }
+ else
+ info[_("Flood kicker")] = disabled;
+
+ if (kd && kd->GetRepeat())
+ {
+ if (kd->GetTTBRepeat())
+ info[_("Repeat kicker")] = Anope::printf(_("%s (%d kick(s) to ban; %d times)"), enabled.c_str(), kd->GetTTBRepeat(), kd->GetRepeatTimes());
+ else
+ info[_("Repeat kicker")] = Anope::printf(_("%s (%d times)"), enabled.c_str(), kd->GetRepeatTimes());
+ }
+ else
+ info[_("Repeat kicker")] = disabled;
+
+ if (kd && kd->GetReverses())
+ {
+ if (kd->GetTTBReverses())
+ info[_("Reverses kicker")] = Anope::printf(_("%s (%d kick(s) to ban)"), enabled.c_str(), kd->GetTTBReverses());
+ else
+ info[_("Reverses kicker")] = enabled;
+ }
+ else
+ info[_("Reverses kicker")] = disabled;
+
+ if (kd && kd->GetUnderlines())
+ {
+ if (kd->GetTTBUnderlines())
+ info[_("Underlines kicker")] = Anope::printf(_("%s (%d kick(s) to ban)"), enabled.c_str(), kd->GetTTBUnderlines());
+ else
+ info[_("Underlines kicker")] = enabled;
+ }
+ else
+ info[_("Underlines kicker")] = disabled;
+
+ if (kd && kd->GetItalics())
+ {
+ if (kd->GetTTBItalics())
+ info[_("Italics kicker")] = Anope::printf(_("%s (%d kick(s) to ban)"), enabled.c_str(), kd->GetTTBItalics());
+ else
+ info[_("Italics kicker")] = enabled;
+ }
+ else
+ info[_("Italics kicker")] = disabled;
+
+ if (kd && kd->GetAmsgs())
+ {
+ if (kd->GetTTBAmsgs())
+ info[_("AMSG kicker")] = Anope::printf(_("%s (%d kick(s) to ban)"), enabled.c_str(), kd->GetTTBAmsgs());
+ else
+ info[_("AMSG kicker")] = enabled;
+ }
+ else
+ info[_("AMSG kicker")] = disabled;
+
+ if (kd && kd->GetDontKickOps())
+ info.AddOption(_("Ops protection"));
+ if (kd && kd->GetDontKickVoices())
+ info.AddOption(_("Voices protection"));
+ }
+
+ void OnPrivmsg(User *u, Channel *c, Anope::string &msg) override
+ {
+ /* Now we can make kicker stuff. We try to order the checks
+ * from the fastest one to the slowest one, since there's
+ * no need to process other kickers if a user is kicked before
+ * the last kicker check.
+ *
+ * But FIRST we check whether the user is protected in any
+ * way.
+ */
+ ChanServ::Channel *ci = c->ci;
+ if (ci == NULL)
+ return;
+ KickerData *kd = c->ci->GetRef<KickerData *>();
+ if (kd == NULL)
+ return;
+
+ if (ci->AccessFor(u).HasPriv("NOKICK"))
+ return;
+ if (kd->GetDontKickOps() && (c->HasUserStatus(u, "HALFOP") || c->HasUserStatus(u, "OP") || c->HasUserStatus(u, "PROTECT") || c->HasUserStatus(u, "OWNER")))
+ return;
+ if (kd->GetDontKickVoices() && c->HasUserStatus(u, "VOICE"))
+ return;
+
+ Anope::string realbuf = msg;
+
+ /* If it's a /me, cut the CTCP part because the ACTION will cause
+ * problems with the caps or badwords kicker
+ */
+ if (realbuf.substr(0, 8).equals_ci("\1ACTION ") && realbuf[realbuf.length() - 1] == '\1')
+ {
+ realbuf.erase(0, 8);
+ realbuf.erase(realbuf.length() - 1);
+ }
+
+ if (realbuf.empty())
+ return;
+
+ /* Bolds kicker */
+ if (kd->GetBolds() && realbuf.find(2) != Anope::string::npos)
+ {
+ TakeAction(ci, u, kd->GetTTBBolds(), TTB_BOLDS, _("Don't use bolds on this channel!"));
+ return;
+ }
+
+ /* Color kicker */
+ if (kd->GetColors() && realbuf.find(3) != Anope::string::npos)
+ {
+ TakeAction(ci, u, kd->GetTTBColors(), TTB_COLORS, _("Don't use colors on this channel!"));
+ return;
+ }
+
+ /* Reverses kicker */
+ if (kd->GetReverses() && realbuf.find(22) != Anope::string::npos)
+ {
+ TakeAction(ci, u, kd->GetTTBReverses(), TTB_REVERSES, _("Don't use reverses on this channel!"));
+ return;
+ }
+
+ /* Italics kicker */
+ if (kd->GetItalics() && realbuf.find(29) != Anope::string::npos)
+ {
+ TakeAction(ci, u, kd->GetTTBItalics(), TTB_ITALICS, _("Don't use italics on this channel!"));
+ return;
+ }
+
+ /* Underlines kicker */
+ if (kd->GetUnderlines() && realbuf.find(31) != Anope::string::npos)
+ {
+ TakeAction(ci, u, kd->GetTTBUnderlines(), TTB_UNDERLINES, _("Don't use underlines on this channel!"));
+ return;
+ }
+
+ /* Caps kicker */
+ if (kd->GetCaps() && realbuf.length() >= static_cast<unsigned>(kd->GetCapsMin()))
+ {
+ int i = 0, l = 0;
+
+ for (unsigned j = 0, end = realbuf.length(); j < end; ++j)
+ {
+ if (isupper(realbuf[j]))
+ ++i;
+ else if (islower(realbuf[j]))
+ ++l;
+ }
+
+ /* i counts uppercase chars, l counts lowercase chars. Only
+ * alphabetic chars (so islower || isupper) qualify for the
+ * percentage of caps to kick for; the rest is ignored. -GD
+ */
+
+ if ((i || l) && i >= kd->GetCapsMin() && i * 100 / (i + l) >= kd->GetCapsPercent())
+ {
+ TakeAction(ci, u, kd->GetTTBCaps(), TTB_CAPS, _("Turn caps lock OFF!"));
+ return;
+ }
+ }
+
+ /* Bad words kicker */
+ if (kd->GetBadwords())
+ {
+ bool mustkick = false;
+
+ /* Normalize the buffer */
+ Anope::string nbuf = Anope::NormalizeBuffer(realbuf);
+ bool casesensitive = Config->GetModule("botserv")->Get<bool>("casesensitive");
+
+ /* Normalize can return an empty string if this only conains control codes etc */
+ if (badwords && !nbuf.empty())
+ for (unsigned i = 0; i < badwords->GetBadWordCount(ci); ++i)
+ {
+ BadWord *bw = badwords->GetBadWord(ci, i);
+
+ if (bw->GetWord().empty())
+ continue; // Shouldn't happen
+
+ if (bw->GetWord().length() > nbuf.length())
+ continue; // This can't ever match
+
+ if (bw->GetType() == BW_ANY && ((casesensitive && nbuf.find(bw->GetWord()) != Anope::string::npos) || (!casesensitive && nbuf.find_ci(bw->GetWord()) != Anope::string::npos)))
+ mustkick = true;
+ else if (bw->GetType() == BW_SINGLE)
+ {
+ size_t len = bw->GetWord().length();
+
+ if ((casesensitive && bw->GetWord().equals_cs(nbuf)) || (!casesensitive && bw->GetWord().equals_ci(nbuf)))
+ mustkick = true;
+ else if (nbuf.find(' ') == len && ((casesensitive && bw->GetWord().equals_cs(nbuf.substr(0, len))) || (!casesensitive && bw->GetWord().equals_ci(nbuf.substr(0, len)))))
+ mustkick = true;
+ else
+ {
+ if (len < nbuf.length() && nbuf.rfind(' ') == nbuf.length() - len - 1 && ((casesensitive && nbuf.find(bw->GetWord()) == nbuf.length() - len) || (!casesensitive && nbuf.find_ci(bw->GetWord()) == nbuf.length() - len)))
+ mustkick = true;
+ else
+ {
+ Anope::string wordbuf = " " + bw->GetWord() + " ";
+
+ if ((casesensitive && nbuf.find(wordbuf) != Anope::string::npos) || (!casesensitive && nbuf.find_ci(wordbuf) != Anope::string::npos))
+ mustkick = true;
+ }
+ }
+ }
+ else if (bw->GetType() == BW_START)
+ {
+ size_t len = bw->GetWord().length();
+
+ if ((casesensitive && nbuf.substr(0, len).equals_cs(bw->GetWord())) || (!casesensitive && nbuf.substr(0, len).equals_ci(bw->GetWord())))
+ mustkick = true;
+ else
+ {
+ Anope::string wordbuf = " " + bw->GetWord();
+
+ if ((casesensitive && nbuf.find(wordbuf) != Anope::string::npos) || (!casesensitive && nbuf.find_ci(wordbuf) != Anope::string::npos))
+ mustkick = true;
+ }
+ }
+ else if (bw->GetType() == BW_END)
+ {
+ size_t len = bw->GetWord().length();
+
+ if ((casesensitive && nbuf.substr(nbuf.length() - len).equals_cs(bw->GetWord())) || (!casesensitive && nbuf.substr(nbuf.length() - len).equals_ci(bw->GetWord())))
+ mustkick = true;
+ else
+ {
+ Anope::string wordbuf = bw->GetWord() + " ";
+
+ if ((casesensitive && nbuf.find(wordbuf) != Anope::string::npos) || (!casesensitive && nbuf.find_ci(wordbuf) != Anope::string::npos))
+ mustkick = true;
+ }
+ }
+
+ if (mustkick)
+ {
+ if (Config->GetModule(me)->Get<bool>("gentlebadwordreason"))
+ TakeAction(ci, u, kd->GetTTBBadwords(), TTB_BADWORDS, _("Watch your language!"));
+ else
+ TakeAction(ci, u, kd->GetTTBBadwords(), TTB_BADWORDS, _("Don't use the word \"%s\" on this channel!"), bw->GetWord().c_str());
+
+ return;
+ }
+ } /* for */
+ } /* if badwords */
+
+ UserData *ud = GetUserData(u, c);
+
+ if (ud)
+ {
+ /* Flood kicker */
+ if (kd->GetFlood())
+ {
+ if (Anope::CurTime - ud->last_start > kd->GetFloodSecs())
+ {
+ ud->last_start = Anope::CurTime;
+ ud->lines = 0;
+ }
+
+ ++ud->lines;
+ if (ud->lines >= kd->GetFloodLines())
+ {
+ TakeAction(ci, u, kd->GetTTBFlood(), TTB_FLOOD, _("Stop flooding!"));
+ return;
+ }
+ }
+
+ /* Repeat kicker */
+ if (kd->GetRepeat())
+ {
+ if (!ud->lastline.equals_ci(realbuf))
+ ud->times = 0;
+ else
+ ++ud->times;
+
+ if (ud->times >= kd->GetRepeatTimes())
+ {
+ TakeAction(ci, u, kd->GetTTBRepeat(), TTB_REPEAT, _("Stop repeating yourself!"));
+ return;
+ }
+ }
+
+ if (ud->lastline.equals_ci(realbuf) && !ud->lasttarget.empty() && !ud->lasttarget.equals_ci(ci->GetName()))
+ {
+ for (User::ChanUserList::iterator it = u->chans.begin(); it != u->chans.end();)
+ {
+ Channel *chan = it->second->chan;
+ ++it;
+
+ if (chan->ci && kd->GetAmsgs() && !chan->ci->AccessFor(u).HasPriv("NOKICK"))
+ {
+ TakeAction(ci, u, kd->GetTTBAmsgs(), TTB_AMSGS, _("Don't use AMSGs!"));
+ return;
+ }
+ }
+ }
+
+ ud->lasttarget = ci->GetName();
+ ud->lastline = realbuf;
+ }
+ }
+};
+
+MODULE_INIT(BSKick)
diff --git a/modules/botserv/main/CMakeLists.txt b/modules/botserv/main/CMakeLists.txt
new file mode 100644
index 000000000..781f0ef1f
--- /dev/null
+++ b/modules/botserv/main/CMakeLists.txt
@@ -0,0 +1 @@
+build_subdir(${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/modules/pseudoclients/botserv.cpp b/modules/botserv/main/botserv.cpp
index ac73e257a..a297db153 100644
--- a/modules/pseudoclients/botserv.cpp
+++ b/modules/botserv/main/botserv.cpp
@@ -1,6 +1,6 @@
/* BotServ core functions
*
- * (C) 2003-2016 Anope Team
+ * (C) 2003-2014 Anope Team
* Contact us at team@anope.org
*
* Please read COPYING and README for further details.
@@ -10,50 +10,71 @@
*/
#include "module.h"
-
-class BotServCore : public Module
+#include "modules/botserv.h"
+#include "modules/help.h"
+
+class BotServCore : public Module, public BotServ::BotServService
+ , public EventHook<Event::SetCorrectModes>
+ , public EventHook<Event::BotAssign>
+ , public EventHook<Event::JoinChannel>
+ , public EventHook<Event::LeaveChannel>
+ , public EventHook<Event::Help>
+ , public EventHook<Event::ChannelModeSet>
+ , public EventHook<Event::ChanRegistered>
+ , public EventHook<Event::UserKicked>
+ , public EventHook<Event::CreateBot>
{
- Reference<BotInfo> BotServ;
- ExtensibleRef<bool> persist, inhabit;
+ Reference<ServiceBot> BotServ;
+ ExtensibleRef<bool> inhabit;
public:
- BotServCore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, PSEUDOCLIENT | VENDOR),
- persist("PERSIST"), inhabit("inhabit")
+ BotServCore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, PSEUDOCLIENT | VENDOR)
+ , BotServ::BotServService(this)
+ , EventHook<Event::SetCorrectModes>(this)
+ , EventHook<Event::BotAssign>(this)
+ , EventHook<Event::JoinChannel>(this)
+ , EventHook<Event::LeaveChannel>(this)
+ , EventHook<Event::Help>(this)
+ , EventHook<Event::ChannelModeSet>(this)
+ , EventHook<Event::ChanRegistered>(this)
+ , EventHook<Event::UserKicked>(this)
+ , EventHook<Event::CreateBot>(this)
+ , inhabit("inhabit")
{
}
- void OnReload(Configuration::Conf *conf) anope_override
+ void OnReload(Configuration::Conf *conf) override
{
- const Anope::string &bsnick = conf->GetModule(this)->Get<const Anope::string>("client");
- BotServ = BotInfo::Find(bsnick, true);
+ const Anope::string &bsnick = conf->GetModule(this)->Get<Anope::string>("client");
+ BotServ = ServiceBot::Find(bsnick, true);
}
- void OnSetCorrectModes(User *user, Channel *chan, AccessGroup &access, bool &give_modes, bool &take_modes) anope_override
+ void OnSetCorrectModes(User *user, Channel *chan, ChanServ::AccessGroup &access, bool &give_modes, bool &take_modes) override
{
/* Do not allow removing bot modes on our service bots */
- if (chan->ci && chan->ci->bi == user)
+ if (chan->ci && chan->ci->GetBot() == user)
{
- const Anope::string &botmodes = Config->GetModule(this)->Get<const Anope::string>("botmodes");
+ const Anope::string &botmodes = Config->GetModule(this)->Get<Anope::string>("botmodes");
for (unsigned i = 0; i < botmodes.length(); ++i)
- chan->SetMode(chan->ci->bi, ModeManager::FindChannelModeByChar(botmodes[i]), chan->ci->bi->GetUID());
+ chan->SetMode(chan->ci->GetBot(), ModeManager::FindChannelModeByChar(botmodes[i]), chan->ci->GetBot()->GetUID());
}
}
- void OnBotAssign(User *sender, ChannelInfo *ci, BotInfo *bi) anope_override
+ void OnBotAssign(User *sender, ChanServ::Channel *ci, ServiceBot *bi) override
{
if (ci->c && ci->c->users.size() >= Config->GetModule(this)->Get<unsigned>("minusers"))
{
- ChannelStatus status(Config->GetModule(this)->Get<const Anope::string>("botmodes"));
+ ChannelStatus status(Config->GetModule(this)->Get<Anope::string>("botmodes"));
bi->Join(ci->c, &status);
}
}
- void OnJoinChannel(User *user, Channel *c) anope_override
+ void OnJoinChannel(User *user, Channel *c) override
{
if (!Config || !IRCD)
return;
- BotInfo *bi = user->server == Me ? dynamic_cast<BotInfo *>(user) : NULL;
+ ServiceBot *bi = user->server == Me ? dynamic_cast<ServiceBot *>(user) : NULL;
if (bi && Config->GetModule(this)->Get<bool>("smartjoin"))
{
std::vector<Anope::string> bans = c->GetModeList("BAN");
@@ -86,7 +107,7 @@ class BotServCore : public Module
ModeManager::ProcessModes();
}
- if (user->server != Me && c->ci && c->ci->bi)
+ if (user->server != Me && c->ci && c->ci->GetBot())
{
/**
* We let the bot join even if it was an ignored user, as if we don't,
@@ -95,18 +116,18 @@ class BotServCore : public Module
* legit users - Rob
**/
/* This is before the user has joined the channel, so check usercount + 1 */
- if (c->users.size() + 1 >= Config->GetModule(this)->Get<unsigned>("minusers") && !c->FindUser(c->ci->bi))
+ if (c->users.size() + 1 >= Config->GetModule(this)->Get<unsigned>("minusers") && !c->FindUser(c->ci->GetBot()))
{
- ChannelStatus status(Config->GetModule(this)->Get<const Anope::string>("botmodes"));
- c->ci->bi->Join(c, &status);
+ ChannelStatus status(Config->GetModule(this)->Get<Anope::string>("botmodes"));
+ c->ci->GetBot()->Join(c, &status);
}
}
}
- void OnLeaveChannel(User *u, Channel *c) anope_override
+ void OnLeaveChannel(User *u, Channel *c) override
{
/* Channel is persistent, it shouldn't be deleted and the service bot should stay */
- if (c->ci && persist && persist->HasExt(c->ci))
+ if (c->ci && c->ci->HasFieldS("PERSIST"))
return;
/* Channel is syncing from a netburst, don't destroy it as more users are probably wanting to join immediately
@@ -119,12 +140,17 @@ class BotServCore : public Module
if (inhabit && inhabit->HasExt(c))
return;
- /* This is called prior to removing the user from the channnel, so c->users.size() - 1 should be safe */
- if (c->ci && c->ci->bi && u != *c->ci->bi && c->users.size() - 1 <= Config->GetModule(this)->Get<unsigned>("minusers") && c->FindUser(c->ci->bi))
- c->ci->bi->Part(c->ci->c);
+ if (c->ci)
+ {
+ ServiceBot *bot = c->ci->GetBot();
+
+ /* This is called prior to removing the user from the channnel, so c->users.size() - 1 should be safe */
+ if (bot && u != bot && c->users.size() - 1 <= Config->GetModule(this)->Get<unsigned>("minusers") && c->FindUser(bot))
+ bot->Part(c);
+ }
}
- EventReturn OnPreHelp(CommandSource &source, const std::vector<Anope::string> &params) anope_override
+ EventReturn OnPreHelp(CommandSource &source, const std::vector<Anope::string> &params) override
{
if (!params.empty())
return EVENT_CONTINUE;
@@ -136,7 +162,7 @@ class BotServCore : public Module
"channel, and provide a more convenient way to execute commands. Commands that\n"
"require a channel as a parameter will automatically have that parameter\n"
"given.\n"), source.service->nick.c_str());
- const Anope::string &fantasycharacters = Config->GetModule("fantasy")->Get<const Anope::string>("fantasycharacter", "!");
+ const Anope::string &fantasycharacters = Config->GetModule("fantasy")->Get<Anope::string>("fantasycharacter", "!");
if (!fantasycharacters.empty())
source.Reply(_(" \n"
"Fantasy commands may be prefixed with one of the following characters: %s\n"), fantasycharacters.c_str());
@@ -159,7 +185,7 @@ class BotServCore : public Module
return EVENT_CONTINUE;
}
- void OnPostHelp(CommandSource &source, const std::vector<Anope::string> &params) anope_override
+ void OnPostHelp(CommandSource &source, const std::vector<Anope::string> &params) override
{
if (!params.empty() || source.c || source.service != *BotServ)
return;
@@ -167,18 +193,18 @@ class BotServCore : public Module
source.Reply(_(" \n"
"Bot will join a channel whenever there is at least\n"
"\002%d\002 user(s) on it."), Config->GetModule(this)->Get<unsigned>("minusers"));
- const Anope::string &fantasycharacters = Config->GetModule("fantasy")->Get<const Anope::string>("fantasycharacter", "!");
+ const Anope::string &fantasycharacters = Config->GetModule("fantasy")->Get<Anope::string>("fantasycharacter", "!");
if (!fantasycharacters.empty())
source.Reply(_("Additionally, if fantasy is enabled fantasy commands\n"
"can be executed by prefixing the command name with\n"
"one of the following characters: %s"), fantasycharacters.c_str());
}
- EventReturn OnChannelModeSet(Channel *c, MessageSource &source, ChannelMode *mode, const Anope::string &param) anope_override
+ EventReturn OnChannelModeSet(Channel *c, const MessageSource &source, ChannelMode *mode, const Anope::string &param) override
{
- if (source.GetUser() && !source.GetBot() && Config->GetModule(this)->Get<bool>("smartjoin") && mode->name == "BAN" && c->ci && c->ci->bi && c->FindUser(c->ci->bi))
+ if (source.GetUser() && !source.GetBot() && Config->GetModule(this)->Get<bool>("smartjoin") && mode->name == "BAN" && c->ci && c->ci->GetBot() && c->FindUser(c->ci->GetBot()))
{
- BotInfo *bi = c->ci->bi;
+ ServiceBot *bi = c->ci->GetBot();
Entry ban("BAN", param);
if (ban.Matches(bi))
@@ -188,26 +214,26 @@ class BotServCore : public Module
return EVENT_CONTINUE;
}
- void OnCreateChan(ChannelInfo *ci) anope_override
+ void OnChanRegistered(ChanServ::Channel *ci) override
{
/* Set default bot flags */
- spacesepstream sep(Config->GetModule(this)->Get<const Anope::string>("defaults", "greet fantasy"));
+ spacesepstream sep(Config->GetModule(this)->Get<Anope::string>("defaults", "greet fantasy"));
for (Anope::string token; sep.GetToken(token);)
- ci->Extend<bool>("BS_" + token.upper());
+ ci->SetS<bool>("BS_" + token.upper(), true);
}
- void OnUserKicked(const MessageSource &source, User *target, const Anope::string &channel, ChannelStatus &status, const Anope::string &kickmsg) anope_override
+ void OnUserKicked(const MessageSource &source, User *target, const Anope::string &channel, ChannelStatus &status, const Anope::string &kickmsg) override
{
- BotInfo *bi = BotInfo::Find(target->GetUID());
+ ServiceBot *bi = ServiceBot::Find(target->GetUID());
if (bi)
/* Bots get rejoined */
bi->Join(channel, &status);
}
- void OnCreateBot(BotInfo *bi) anope_override
+ void OnCreateBot(ServiceBot *bi) override
{
if (bi->botmodes.empty())
- bi->botmodes = Config->GetModule(this)->Get<const Anope::string>("botumodes");
+ bi->botmodes = Config->GetModule(this)->Get<Anope::string>("botumodes");
}
};
diff --git a/modules/commands/bs_set.cpp b/modules/botserv/set.cpp
index 9e0bac56a..47c5337d2 100644
--- a/modules/commands/bs_set.cpp
+++ b/modules/botserv/set.cpp
@@ -1,15 +1,24 @@
-/* BotServ core functions
+/*
+ * Anope IRC Services
*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
*
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "module.h"
+#include "modules/botserv/kick.h"
class CommandBSSet : public Command
{
@@ -20,17 +29,15 @@ class CommandBSSet : public Command
this->SetSyntax(_("\037option\037 \037(channel | bot)\037 \037settings\037"));
}
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
{
this->OnSyntaxError(source, "");
}
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
{
- this->SendSyntax(source);
- source.Reply(" ");
source.Reply(_("Configures bot options.\n"
- " \n"
+ "\n"
"Available options:"));
bool hide_privileged_commands = Config->GetBlock("options")->Get<bool>("hideprivilegedcommands"),
hide_registered_commands = Config->GetBlock("options")->Get<bool>("hideregisteredcommands");
@@ -41,7 +48,7 @@ class CommandBSSet : public Command
const CommandInfo &info = it->second;
if (c_name.find_ci(this_name + " ") == 0)
{
- ServiceReference<Command> command("Command", info.name);
+ ServiceReference<Command> command(info.name);
if (command)
{
// XXX dup
@@ -56,8 +63,11 @@ class CommandBSSet : public Command
}
}
}
- source.Reply(_("Type \002%s%s HELP %s \037option\037\002 for more information on a\n"
- "particular option."), Config->StrictPrivmsg.c_str(), source.service->nick.c_str(), this_name.c_str());
+
+ CommandInfo *help = source.service->FindCommand("generic/help");
+ if (help)
+ source.Reply(_("Type \002{0}{1} {2} {3} \037option\037\002 for more information on a particular option."),
+ Config->StrictPrivmsg, source.service->nick, help->cname, this_name);
return true;
}
@@ -74,7 +84,7 @@ class CommandBSSetBanExpire : public Command
public:
UnbanTimer(Module *creator, const Anope::string &ch, const Anope::string &bmask, time_t t) : Timer(creator, t), chname(ch), mask(bmask) { }
- void Tick(time_t) anope_override
+ void Tick(time_t) override
{
Channel *c = Channel::Find(chname);
if (c)
@@ -88,22 +98,22 @@ class CommandBSSetBanExpire : public Command
this->SetSyntax(_("\037channel\037 \037time\037"));
}
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
{
const Anope::string &chan = params[0];
const Anope::string &arg = params[1];
- ChannelInfo *ci = ChannelInfo::Find(chan);
+ ChanServ::Channel *ci = ChanServ::Find(chan);
if (ci == NULL)
{
- source.Reply(CHAN_X_NOT_REGISTERED, chan.c_str());
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
return;
}
- AccessGroup access = source.AccessFor(ci);
+ ChanServ::AccessGroup access = source.AccessFor(ci);
if (!source.HasPriv("botserv/administration") && !access.HasPriv("SET"))
{
- source.Reply(ACCESS_DENIED);
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "SET", ci->GetName());
return;
}
@@ -116,7 +126,7 @@ class CommandBSSetBanExpire : public Command
time_t t = Anope::DoTime(arg);
if (t == -1)
{
- source.Reply(BAD_EXPIRY_TIME);
+ source.Reply(_("Invalid expiry time \002{0}\002."), arg);
return;
}
@@ -127,25 +137,21 @@ class CommandBSSetBanExpire : public Command
return;
}
- ci->banexpire = t;
+ ci->SetBanExpire(t);
bool override = !access.HasPriv("SET");
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to change banexpire to " << ci->banexpire;
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to change banexpire to " << arg;
- if (!ci->banexpire)
+ if (!t)
source.Reply(_("Bot bans will no longer automatically expire."));
else
- source.Reply(_("Bot bans will automatically expire after %s."), Anope::Duration(ci->banexpire, source.GetAccount()).c_str());
+ source.Reply(_("Bot bans will automatically expire after \002{0}\002."), Anope::Duration(t, source.GetAccount()));
}
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
+ bool OnHelp(CommandSource &source, const Anope::string &) override
{
- this->SendSyntax(source);
- source.Reply(_(" \n"
- "Sets the time bot bans expire in. If enabled, any bans placed by\n"
- "bots, such as flood kicker, badwords kicker, etc. will automatically\n"
- "be removed after the given time. Set to 0 to disable bans from\n"
- "automatically expiring."));
+ source.Reply(_("Sets the time bot bans expire in. If enabled, any bans placed by bots, such as by the flood kicker, badwords kicker, etc. will automatically be removed after the given time."
+ " Set to 0 to disable bans from automatically expiring."));
return true;
}
};
@@ -155,70 +161,69 @@ class CommandBSSetPrivate : public Command
public:
CommandBSSetPrivate(Module *creator, const Anope::string &sname = "botserv/set/private") : Command(creator, sname, 2, 2)
{
- this->SetDesc(_("Prevent a bot from being assigned by non IRC operators"));
+ this->SetDesc(_("Prevent a bot from being assigned by non Services Operators"));
this->SetSyntax(_("\037botname\037 {\037ON|OFF\037}"));
}
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
{
- BotInfo *bi = BotInfo::Find(params[0], true);
+ const Anope::string &nick = params[9];
const Anope::string &value = params[1];
if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
+ source.Reply(_("Services are in read-only mode. Any changes made may not persist."));
+ ServiceBot *bi = ServiceBot::Find(nick, true);
if (bi == NULL)
{
- source.Reply(BOT_DOES_NOT_EXIST, params[0].c_str());
+ source.Reply(_("Bot \002{0}\002 does not exist."), nick);
return;
}
if (value.equals_ci("ON"))
{
- bi->oper_only = true;
- source.Reply(_("Private mode of bot %s is now \002on\002."), bi->nick.c_str());
+ bi->bi->SetOperOnly(true);
+ source.Reply(_("Private mode of bot \002{0}\002 is now \002on\002."), bi->nick);
}
else if (value.equals_ci("OFF"))
{
- bi->oper_only = false;
- source.Reply(_("Private mode of bot %s is now \002off\002."), bi->nick.c_str());
+ bi->bi->SetOperOnly(false);
+ source.Reply(_("Private mode of bot \002{0}\002 is now \002off\002."), bi->nick);
}
else
this->OnSyntaxError(source, source.command);
}
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
+ bool OnHelp(CommandSource &source, const Anope::string &) override
{
- this->SendSyntax(source);
- source.Reply(_(" \n"
- "This option prevents a bot from being assigned to a\n"
- "channel by users that aren't IRC Operators."));
+ source.Reply(_("This option prevents a bot from being assigned to channels by users who do not have the \002{0}\002 privilege."),
+ "botserv/administration");
return true;
}
};
class BSSet : public Module
+ , public EventHook<Event::BotBan>
{
CommandBSSet commandbsset;
CommandBSSetBanExpire commandbssetbanexpire;
CommandBSSetPrivate commandbssetprivate;
public:
- BSSet(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandbsset(this), commandbssetbanexpire(this),
- commandbssetprivate(this)
+ BSSet(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , EventHook<Event::BotBan>(this)
+ , commandbsset(this)
+ , commandbssetbanexpire(this)
+ , commandbssetprivate(this)
{
}
- void OnBotBan(User *u, ChannelInfo *ci, const Anope::string &mask) anope_override
+ void OnBotBan(User *u, ChanServ::Channel *ci, const Anope::string &mask) override
{
- if (!ci->banexpire)
+ if (!ci->GetBanExpire())
return;
- new CommandBSSetBanExpire::UnbanTimer(this, ci->name, mask, ci->banexpire);
+ new CommandBSSetBanExpire::UnbanTimer(this, ci->GetName(), mask, ci->GetBanExpire());
}
};
diff --git a/modules/bs_autoassign.cpp b/modules/bs_autoassign.cpp
deleted file mode 100644
index 0462e649d..000000000
--- a/modules/bs_autoassign.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- */
-
-#include "module.h"
-
-class BSAutoAssign : public Module
-{
- public:
- BSAutoAssign(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
- {
- }
-
- void OnChanRegistered(ChannelInfo *ci) anope_override
- {
- const Anope::string &bot = Config->GetModule(this)->Get<const Anope::string>("bot");
- if (bot.empty())
- return;
-
- BotInfo *bi = BotInfo::Find(bot, true);
- if (bi == NULL)
- {
- Log(this) << "bs_autoassign is configured to assign bot " << bot << ", but it does not exist?";
- return;
- }
-
- bi->Assign(NULL, ci);
- }
-};
-
-MODULE_INIT(BSAutoAssign)
diff --git a/modules/chanserv/CMakeLists.txt b/modules/chanserv/CMakeLists.txt
new file mode 100644
index 000000000..9a236d6d0
--- /dev/null
+++ b/modules/chanserv/CMakeLists.txt
@@ -0,0 +1,2 @@
+build_modules(${CMAKE_CURRENT_SOURCE_DIR})
+build_modules_dependencies(${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/modules/chanserv/access.cpp b/modules/chanserv/access.cpp
new file mode 100644
index 000000000..5c9650577
--- /dev/null
+++ b/modules/chanserv/access.cpp
@@ -0,0 +1,922 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+/* Dependencies: anope_chanserv.main */
+
+#include "module.h"
+#include "modules/chanserv.h"
+#include "modules/chanserv/access.h"
+#include "modules/chanserv/main/chanaccess.h"
+#include "main/chanaccesstype.h"
+
+class AccessChanAccessImpl : public AccessChanAccess
+{
+ friend class AccessChanAccessType;
+
+ int level = 0;
+
+ public:
+ static constexpr const char *NAME = "accesschanaccess";
+
+ using AccessChanAccess::AccessChanAccess;
+
+ int GetLevel();
+ void SetLevel(const int &);
+
+ bool HasPriv(const Anope::string &name) override
+ {
+ return this->GetChannel()->GetLevel(name) != ChanServ::ACCESS_INVALID && this->GetLevel() >= this->GetChannel()->GetLevel(name);
+ }
+
+ Anope::string AccessSerialize() override
+ {
+ return stringify(this->GetLevel());
+ }
+
+ void AccessUnserialize(const Anope::string &data) override
+ {
+ try
+ {
+ this->SetLevel(convertTo<int>(data));
+ }
+ catch (const ConvertException &)
+ {
+ }
+ }
+
+ bool operator>(ChanServ::ChanAccess &other) override
+ {
+ if (this->GetSerializableType() != other.GetSerializableType())
+ return ChanServ::ChanAccess::operator>(other);
+ else
+ return this->GetLevel() > anope_dynamic_static_cast<AccessChanAccess *>(&other)->GetLevel();
+ }
+
+ bool operator<(ChanServ::ChanAccess &other) override
+ {
+ if (this->GetSerializableType() != other.GetSerializableType())
+ return ChanAccess::operator<(other);
+ else
+ return this->GetLevel() < anope_dynamic_static_cast<AccessChanAccess *>(&other)->GetLevel();
+ }
+};
+
+class AccessChanAccessType : public ChanAccessType<AccessChanAccessImpl>
+{
+ public:
+ Serialize::Field<AccessChanAccessImpl, int> level;
+
+ AccessChanAccessType(Module *me) : ChanAccessType<AccessChanAccessImpl>(me)
+ , level(this, "level", &AccessChanAccessImpl::level)
+ {
+ Serialize::SetParent(AccessChanAccess::NAME, ChanServ::ChanAccess::NAME);
+ }
+};
+
+int AccessChanAccessImpl::GetLevel()
+{
+ return Get(&AccessChanAccessType::level);
+}
+
+void AccessChanAccessImpl::SetLevel(const int &i)
+{
+ Object::Set(&AccessChanAccessType::level, i);
+}
+
+class CommandCSAccess : public Command
+{
+ void DoAdd(CommandSource &source, ChanServ::Channel *ci, const std::vector<Anope::string> &params)
+ {
+ Anope::string mask = params[2];
+ ChanServ::Privilege *p = NULL;
+ int level = ChanServ::ACCESS_INVALID;
+
+ try
+ {
+ level = convertTo<int>(params[3]);
+ }
+ catch (const ConvertException &)
+ {
+ p = ChanServ::service ? ChanServ::service->FindPrivilege(params[3]) : nullptr;
+ if (p != NULL && p->level)
+ level = p->level;
+ }
+
+ if (!level)
+ {
+ source.Reply(_("Access level must be non-zero."));
+ return;
+ }
+
+ if (level <= ChanServ::ACCESS_INVALID || level >= ChanServ::ACCESS_FOUNDER)
+ {
+ source.Reply(_("Access level must be between \002{0}\002 and \002{1}\002 inclusive."), ChanServ::ACCESS_INVALID + 1, ChanServ::ACCESS_FOUNDER - 1);
+ return;
+ }
+
+ ChanServ::AccessGroup u_access = source.AccessFor(ci);
+ ChanServ::ChanAccess *highest = u_access.Highest();
+
+ AccessChanAccess *access = Serialize::New<AccessChanAccess *>();
+ access->SetChannel(ci);
+ access->SetLevel(level);
+
+ bool override = false;
+
+ if ((!highest || *highest <= *access) && !u_access.founder)
+ {
+ if (source.HasPriv("chanserv/access/modify"))
+ {
+ override = true;
+ }
+ else
+ {
+ source.Reply(_("Access denied. You do not have enough privileges on \002{0}\002 to add someone at level \002{1}\002."), ci->GetName(), level);
+ access->Delete();
+ return;
+ }
+ }
+
+ access->Delete();
+
+ NickServ::Nick *na = NickServ::FindNick(mask);
+
+ if (!na && Config->GetModule("chanserv")->Get<bool>("disallow_hostmask_access"))
+ {
+ source.Reply(_("Masks and unregistered users may not be on access lists."));
+ return;
+ }
+
+ if (mask.find_first_of("!*@") == Anope::string::npos && !na)
+ {
+ User *targ = User::Find(mask, true);
+ if (targ != NULL)
+ mask = "*!*@" + targ->GetDisplayedHost();
+ else
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), mask);
+ return;
+ }
+ }
+
+ if (na)
+ mask = na->GetNick();
+
+ for (unsigned i = ci->GetAccessCount(); i > 0; --i)
+ {
+ ChanServ::ChanAccess *access = ci->GetAccess(i - 1);
+ if (mask.equals_ci(access->Mask()))
+ {
+ /* Don't allow lowering from a level >= u_level */
+ if ((!highest || *access >= *highest) && !u_access.founder && !source.HasPriv("chanserv/access/modify"))
+ {
+ source.Reply(_("Access denied. You do not have enough privileges on \002{0}\002 to lower the access of \002{1}\002."), ci->GetName(), access->Mask());
+ return;
+ }
+ delete access;
+ break;
+ }
+ }
+
+ unsigned access_max = Config->GetModule("chanserv")->Get<unsigned>("accessmax", "1024");
+ if (access_max && ci->GetAccessCount() >= access_max)
+ {
+ source.Reply(_("Sorry, you can only have %d access entries on a channel, including access entries from other channels."), access_max);
+ return;
+ }
+
+ access = Serialize::New<AccessChanAccess *>();
+ if (na)
+ access->SetObj(na->GetAccount());
+ access->SetChannel(ci);
+ access->SetMask(mask);
+ access->SetCreator(source.GetNick());
+ access->SetLevel(level);
+ access->SetLastSeen(0);
+ access->SetCreated(Anope::CurTime);
+
+ EventManager::Get()->Dispatch(&Event::AccessAdd::OnAccessAdd, ci, source, access);
+
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to add " << mask << " with level " << level;
+ if (p != NULL)
+ source.Reply(_("\002{0}\002 added to the access list of \002{1}\002 with privilege \002{2}\002 (level \002{3}\002)."), access->Mask(), ci->GetName(), p->name, level);
+ else
+ source.Reply(_("\002{0}\002 added to the access list of \002{1}\002 at level \002{2}\002."), access->Mask(), ci->GetName(), level);
+ }
+
+ void DoDel(CommandSource &source, ChanServ::Channel *ci, const std::vector<Anope::string> &params)
+ {
+ Anope::string mask = params[2];
+
+ if (!ci->GetAccessCount())
+ {
+ source.Reply(_("The access list for \002{0}\002 is empty."), ci->GetName());
+ return;
+ }
+
+ if (!isdigit(mask[0]) && mask.find_first_of("#!*@") == Anope::string::npos && !NickServ::FindNick(mask))
+ {
+ User *targ = User::Find(mask, true);
+ if (targ != NULL)
+ mask = "*!*@" + targ->GetDisplayedHost();
+ else
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), mask);
+ return;
+ }
+ }
+
+ if (isdigit(mask[0]) && mask.find_first_not_of("1234567890,-") == Anope::string::npos)
+ {
+ bool override = !source.AccessFor(ci).HasPriv("ACCESS_CHANGE") && source.HasPriv("chanserv/access/modify");
+ Anope::string nicks;
+ bool denied = false;
+ unsigned int deleted = 0;
+
+ NumberList(mask, true,
+ [&](unsigned int num)
+ {
+ if (!num || num > ci->GetAccessCount())
+ return;
+
+ ChanServ::ChanAccess *access = ci->GetAccess(num - 1);
+
+ ChanServ::AccessGroup ag = source.AccessFor(ci);
+ ChanServ::ChanAccess *u_highest = ag.Highest();
+
+ if ((!u_highest || *u_highest <= *access) && !ag.founder && !override && access->GetObj() != source.nc)
+ {
+ denied = true;
+ return;
+ }
+
+ ++deleted;
+ if (!nicks.empty())
+ nicks += ", " + access->Mask();
+ else
+ nicks = access->Mask();
+
+ EventManager::Get()->Dispatch(&Event::AccessDel::OnAccessDel, ci, source, access);
+ delete access;
+ },
+ [&]()
+ {
+ if (denied && !deleted)
+ source.Reply(_("Access denied. You do not have enough privileges on \002{0}\002 to remove any access entries matching \002{1}\002."));
+ else if (!deleted)
+ source.Reply(_("There are no entries matching \002{0}\002 on the access list of \002{1}\002."), mask, ci->GetName());
+ else
+ {
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to delete " << nicks;
+
+ if (deleted == 1)
+ source.Reply(_("Deleted \0021\002 entry from the access list of \002{0}\002."), ci->GetName());
+ else
+ source.Reply(_("Deleted \002{0}\002 entries from the access list of \002{1}\002."), deleted, ci->GetName());
+ }
+ });
+ }
+ else
+ {
+ ChanServ::AccessGroup u_access = source.AccessFor(ci);
+ ChanServ::ChanAccess *highest = u_access.Highest();
+
+ for (unsigned i = ci->GetAccessCount(); i > 0; --i)
+ {
+ ChanServ::ChanAccess *access = ci->GetAccess(i - 1);
+ if (mask.equals_ci(access->Mask()))
+ {
+ if (access->GetObj() != source.nc && !u_access.founder && (!highest || *highest <= *access) && !source.HasPriv("chanserv/access/modify"))
+ source.Reply(_("Access denied. You do not have enough privileges on \002{0}\002 to remove the access of \002{1}\002."), ci->GetName(), access->Mask());
+ else
+ {
+ source.Reply(_("\002{0}\002 deleted from the access list of \002{1}\002."), access->Mask(), ci->GetName());
+ bool override = !u_access.founder && !u_access.HasPriv("ACCESS_CHANGE") && !access->Mask().equals_ci(source.nc->GetDisplay());
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to delete " << access->Mask();
+
+ EventManager::Get()->Dispatch(&Event::AccessDel::OnAccessDel, ci, source, access);
+ delete access;
+ }
+ return;
+ }
+ }
+
+ source.Reply(_("\002{0}\002 was not found on the access list of \002{1}\002."), mask, ci->GetName());
+ }
+ }
+
+ void ProcessList(CommandSource &source, ChanServ::Channel *ci, const std::vector<Anope::string> &params, ListFormatter &list)
+ {
+ const Anope::string &nick = params.size() > 2 ? params[2] : "";
+
+ if (!ci->GetAccessCount())
+ {
+ source.Reply(_("The access list for \002{0}\002 is empty."), ci->GetName());
+ return;
+ }
+
+ if (!nick.empty() && nick.find_first_not_of("1234567890,-") == Anope::string::npos)
+ {
+ NumberList(nick, false,
+ [&](unsigned int number)
+ {
+ if (!number || number > ci->GetAccessCount())
+ return;
+
+ ChanServ::ChanAccess *access = ci->GetAccess(number - 1);
+
+ Anope::string timebuf;
+ if (ci->c)
+ for (Channel::ChanUserList::const_iterator cit = ci->c->users.begin(), cit_end = ci->c->users.end(); cit != cit_end; ++cit)
+ {
+ if (access->Matches(cit->second->user, cit->second->user->Account()))
+ timebuf = "Now";
+ }
+ if (timebuf.empty())
+ {
+ if (access->GetLastSeen() == 0)
+ timebuf = "Never";
+ else
+ timebuf = Anope::strftime(access->GetLastSeen(), NULL, true);
+ }
+
+ ListFormatter::ListEntry entry;
+ entry["Number"] = stringify(number);
+ entry["Level"] = access->AccessSerialize();
+ entry["Mask"] = access->Mask();
+ entry["By"] = access->GetCreator();
+ entry["Last seen"] = timebuf;
+ list.AddEntry(entry);
+ },
+ [&](){});
+ }
+ else
+ {
+ for (unsigned i = 0, end = ci->GetAccessCount(); i < end; ++i)
+ {
+ ChanServ::ChanAccess *access = ci->GetAccess(i);
+
+ if (!nick.empty() && !Anope::Match(access->Mask(), nick))
+ continue;
+
+ Anope::string timebuf;
+ if (ci->c)
+ for (Channel::ChanUserList::const_iterator cit = ci->c->users.begin(), cit_end = ci->c->users.end(); cit != cit_end; ++cit)
+ {
+ if (access->Matches(cit->second->user, cit->second->user->Account()))
+ timebuf = "Now";
+ }
+ if (timebuf.empty())
+ {
+ if (access->GetLastSeen() == 0)
+ timebuf = "Never";
+ else
+ timebuf = Anope::strftime(access->GetLastSeen(), NULL, true);
+ }
+
+ ListFormatter::ListEntry entry;
+ entry["Number"] = stringify(i + 1);
+ entry["Level"] = access->AccessSerialize();
+ entry["Mask"] = access->Mask();
+ entry["By"] = access->GetCreator();
+ entry["Last seen"] = timebuf;
+ list.AddEntry(entry);
+ }
+ }
+
+ if (list.IsEmpty())
+ {
+ source.Reply(_("No matching entries on the access list of \002{0}\002."), ci->GetName());
+ return;
+ }
+
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+ source.Reply(_("Access list for \002{0}\002:"), ci->GetName());
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+
+ source.Reply(_("End of access list."));
+ }
+
+ void DoList(CommandSource &source, ChanServ::Channel *ci, const std::vector<Anope::string> &params)
+ {
+ if (!ci->GetAccessCount())
+ {
+ source.Reply(_("The access list for \002{0}\002 is empty."), ci->GetName());
+ return;
+ }
+
+ ListFormatter list(source.GetAccount());
+ list.AddColumn(_("Number")).AddColumn(_("Level")).AddColumn(_("Mask"));
+ this->ProcessList(source, ci, params, list);
+ }
+
+ void DoView(CommandSource &source, ChanServ::Channel *ci, const std::vector<Anope::string> &params)
+ {
+ if (!ci->GetAccessCount())
+ {
+ source.Reply(_("The access list for \002{0}\002 is empty."), ci->GetName());
+ return;
+ }
+
+ ListFormatter list(source.GetAccount());
+ list.AddColumn(_("Number")).AddColumn(_("Level")).AddColumn(_("Mask")).AddColumn(_("By")).AddColumn(_("Last seen"));
+ this->ProcessList(source, ci, params, list);
+ }
+
+ void DoClear(CommandSource &source, ChanServ::Channel *ci)
+ {
+ if (!source.IsFounder(ci) && !source.HasPriv("chanserv/access/modify"))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "FOUNDER", ci->GetName());
+ return;
+ }
+
+ EventManager::Get()->Dispatch(&Event::AccessClear::OnAccessClear, ci, source);
+
+ ci->ClearAccess();
+
+ source.Reply(_("The access list of \002{0}\002 has been cleared."), ci->GetName());
+
+ bool override = !source.IsFounder(ci);
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to clear the access list";
+ }
+
+ public:
+ CommandCSAccess(Module *creator) : Command(creator, "chanserv/access", 2, 4)
+ {
+ this->SetDesc(_("Modify the list of privileged users"));
+ this->SetSyntax(_("\037channel\037 ADD \037mask\037 \037level\037"));
+ this->SetSyntax(_("\037channel\037 DEL {\037mask\037 | \037entry-num\037 | \037list\037}"));
+ this->SetSyntax(_("\037channel\037 LIST [\037mask\037 | \037list\037]"));
+ this->SetSyntax(_("\037channel\037 VIEW [\037mask\037 | \037list\037]"));
+ this->SetSyntax(_("\037channel\037 CLEAR"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &chan = params[0];
+ const Anope::string &cmd = params[1];
+ const Anope::string &nick = params.size() > 2 ? params[2] : "";
+ const Anope::string &s = params.size() > 3 ? params[3] : "";
+
+ ChanServ::Channel *ci = ChanServ::Find(chan);
+ if (ci == NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
+ return;
+ }
+
+ bool is_list = cmd.equals_ci("LIST") || cmd.equals_ci("VIEW");
+ bool is_clear = cmd.equals_ci("CLEAR");
+ bool is_del = cmd.equals_ci("DEL");
+
+ ChanServ::AccessGroup access = source.AccessFor(ci);
+
+ bool has_access = false;
+ if (source.HasPriv("chanserv/access/modify"))
+ has_access = true;
+ else if (is_list && source.HasPriv("chanserv/access/list"))
+ has_access = true;
+ else if (is_list && access.HasPriv("ACCESS_LIST"))
+ has_access = true;
+ else if (access.HasPriv("ACCESS_CHANGE"))
+ has_access = true;
+ else if (is_del)
+ {
+ NickServ::Nick *na = NickServ::FindNick(nick);
+ if (na && na->GetAccount() == source.GetAccount())
+ has_access = true;
+ }
+
+ /* If LIST, we don't *require* any parameters, but we can take any.
+ * If DEL, we require a nick and no level.
+ * Else (ADD), we require a level (which implies a nick). */
+ if (is_list || is_clear ? 0 : (cmd.equals_ci("DEL") ? (nick.empty() || !s.empty()) : s.empty()))
+ {
+ this->OnSyntaxError(source, cmd);
+ return;
+ }
+
+ if (!has_access)
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), is_list ? "ACCESS_LIST" : "ACCESS_CHANGE", ci->GetName());
+ return;
+ }
+
+ if (Anope::ReadOnly && !is_list)
+ {
+ source.Reply(_("Sorry, channel access list modification is temporarily disabled."));
+ return;
+ }
+
+ if (cmd.equals_ci("ADD"))
+ this->DoAdd(source, ci, params);
+ else if (cmd.equals_ci("DEL"))
+ this->DoDel(source, ci, params);
+ else if (cmd.equals_ci("LIST"))
+ this->DoList(source, ci, params);
+ else if (cmd.equals_ci("VIEW"))
+ this->DoView(source, ci, params);
+ else if (cmd.equals_ci("CLEAR"))
+ this->DoClear(source, ci);
+ else
+ this->OnSyntaxError(source, "");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ if (subcommand.equals_ci("ADD"))
+ {
+ source.Reply(_("The \002{0} ADD\002 adds \037mask\037 to the access list of \037channel\037 at level \037level\037."
+ " If \037mask\037 is already present on the access list, the access level for it is changed to \037level\037."
+ " The \037level\037 may be a numerical level between \002{1}\002 and \002{2}\002 or the name of a privilege (eg. \002{3}\002)."
+ " The privilege set granted to a given user is the union of the privileges of access entries that match the user."
+ " Use of this command requires the \002{4}\002 privilege on \037channel\037."),
+ source.command, ChanServ::ACCESS_INVALID + 1, ChanServ::ACCESS_FOUNDER - 1, "AUTOOP", "ACCESS_CHANGE");
+
+ if (!Config->GetModule("chanserv")->Get<bool>("disallow_channel_access"))
+ source.Reply(_("The given \037mask\037 may also be a channel, which will use the access list from the other channel up to the given \037level\037."));
+
+ //XXX show def levels
+
+ source.Reply(_("\n"
+ "Examples:\n"
+ " {command} #anope ADD Adam 9001\n"
+ " Adds \"Adam\" to the access list of \"#anope\" at level \"9001\".\n"
+ "\n"
+ " {command} #anope ADD *!*@anope.org AUTOOP\n"
+ " Adds the host mask \"*!*@anope.org\" to the access list of \"#anope\" with the privilege \"AUTOOP\"."));
+ }
+ else if (subcommand.equals_ci("DEL"))
+ source.Reply(_("The \002{0} DEL\002 command removes \037mask\037 from the access list of \037channel\037."
+ " If a list of entry numbers is given, those entries are deleted."
+ " You may remove yourself from an access list, even if you do not have access to modify that list otherwise."
+ " Use of this command requires the \002{1}\002 privilege on \037channel\037.\n"
+ "\n"
+ "Example:\n"
+ " {command} #anope del DukePyrolator\n"
+ " Removes the access of \"DukePyrolator\" from \"#anope\"."),
+ source.command, "ACCESS_CHANGE");
+ else if (subcommand.equals_ci("LIST") || subcommand.equals_ci("VIEW"))
+ source.Reply(_("The \002{0} LIST\002 and \002{0} VIEW\002 command displays the access list of \037channel\037."
+ " If a wildcard mask is given, only those entries matching the mask are displayed."
+ " If a list of entry numbers is given, only those entries are shown."
+ " \002VIEW\002 is similar to \002LIST\002 but also shows who created the access entry, and when the access entry was last used."
+ " Use of these commands requires the \002{1}\002 privilege on \037channel\037.\n"
+ "\n"
+ "Example:\n"
+ " {0} #anope LIST 2-5,7-9\n"
+ " Lists access entries numbered 2 through 5 and 7 through 9 on #anope."),
+ source.command, "ACCESS_LIST");
+ else if (subcommand.equals_ci("CLEAR"))
+ source.Reply(_("The \002{0} CLEAR\002 command clears the access list of \037channel\037."
+ " Use of this command requires the \002{1}\002 privilege on \037channel\037."),
+ source.command, "FOUNDER");
+
+ else
+ {
+ source.Reply(_("Maintains the access list for \037channel\037. The access list specifies which users are granted which privileges to the channel."
+ " The access system uses numerical levels to represent different sets of privileges. Users who are identified but do not match any entries on"
+ " the access list has a level of 0. Unregistered or unidentified users who do not match any entries have a user level of 0."));
+ ServiceBot *bi;
+ Anope::string name;
+ CommandInfo *help = source.service->FindCommand("generic/help");
+ if (Command::FindCommandFromService("chanserv/levels", bi, name) && help)
+ source.Reply(_("\n"
+ "Access levels can be configured via the \002{levels}\002 command. See \002{msg}{service} {help} {levels}\002 for more information."),
+ "msg"_kw = Config->StrictPrivmsg, "service"_kw = bi->nick, "help"_kw = help->cname, "levels"_kw = name);
+
+ if (help)
+ source.Reply(_("\n"
+ "The \002ADD\002 command adds \037mask\037 to the access list at level \037level\037.\n"
+ "Use of this command requires the \002{change}\002 privilege on \037channel\037.\n"
+ "\002{msg}{service} {help} {command} ADD\002 for more information.\n"
+ "\n"
+ "The \002DEL\002 command removes \037mask\037 from the access list.\n"
+ "Use of this command requires the \002{change}\002 privilege on \037channel\037.\n"
+ "\002{msg}{service} {help} {command} DEL\002 for more information.\n"
+ "\n"
+ "The \002LIST\002 and \002VIEW\002 commands both show the access list for \037channel\037, but \002VIEW\002 also shows who created the access entry, and when the user was last seen.\n"
+ "Use of these commands requires the \002{list}\002 privilege on \037channel\037.\n"
+ "\002{msg}{service} {help} {command} [LIST | VIEW]\002 for more information.\n"
+ "\n"
+ "The \002CLEAR\002 command clears the access list."
+ "Use of this command requires the \002{founder}\002 privilege on \037channel\037.\n"
+ "\002{msg}{service} {help} {command} CLEAR\002 for more information."),
+ "msg"_kw = Config->StrictPrivmsg, "service"_kw = source.service->nick, "command"_kw = source.command,
+ "help"_kw = help->cname, "change"_kw = "ACCESS_CHANGE", "list"_kw = "ACCESS_LIST", "founder"_kw = "FOUNDER");
+ }
+
+ return true;
+ }
+};
+
+class CommandCSLevels : public Command
+{
+ void DoSet(CommandSource &source, ChanServ::Channel *ci, const std::vector<Anope::string> &params)
+ {
+ const Anope::string &what = params[2];
+ const Anope::string &lev = params[3];
+
+ int level;
+
+ if (lev.equals_ci("FOUNDER"))
+ level = ChanServ::ACCESS_FOUNDER;
+ else
+ {
+ try
+ {
+ level = convertTo<int>(lev);
+ }
+ catch (const ConvertException &)
+ {
+ this->OnSyntaxError(source, "SET");
+ return;
+ }
+ }
+
+ if (level <= ChanServ::ACCESS_INVALID || level > ChanServ::ACCESS_FOUNDER)
+ source.Reply(_("Level must be between \002{0}\002 and \002{1}\002 inclusive."), ChanServ::ACCESS_INVALID + 1, ChanServ::ACCESS_FOUNDER - 1);
+ else
+ {
+ ChanServ::Privilege *p = ChanServ::service ? ChanServ::service->FindPrivilege(what) : nullptr;
+ if (p == NULL)
+ {
+ CommandInfo *help = source.service->FindCommand("generic/help");
+ if (help)
+ source.Reply(_("There is no such privilege \002{0}\002. See \002{0}{1} {2} {3}\002 for a list of valid settings."),
+ what, Config->StrictPrivmsg, source.service->nick, help->cname, source.command);
+ }
+ else
+ {
+ bool override = !source.AccessFor(ci).HasPriv("FOUNDER");
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to set " << p->name << " to level " << level;
+
+ ci->SetLevel(p->name, level);
+ EventManager::Get()->Dispatch(&Event::LevelChange::OnLevelChange, source, ci, p->name, level);
+
+ if (level == ChanServ::ACCESS_FOUNDER)
+ source.Reply(_("Level for privilege \002{0}\002 on channel \002{1}\002 changed to \002founder only\002."), p->name, ci->GetName());
+ else
+ source.Reply(_("Level for privilege \002{0}\002 on channel \002{1}\002 changed to \002{3}\002."), p->name, ci->GetName(), level);
+ }
+ }
+ }
+
+ void DoDisable(CommandSource &source, ChanServ::Channel *ci, const std::vector<Anope::string> &params)
+ {
+ const Anope::string &what = params[2];
+
+ /* Don't allow disabling of the founder level. It would be hard to change it back if you don't have access to use this command */
+ if (what.equals_ci("FOUNDER"))
+ {
+ source.Reply(_("You can not disable the founder privilege because it would be impossible to reenable it at a later time."));
+ return;
+ }
+
+ ChanServ::Privilege *p = ChanServ::service ? ChanServ::service->FindPrivilege(what) : nullptr;
+ if (p != NULL)
+ {
+ bool override = !source.AccessFor(ci).HasPriv("FOUNDER");
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to disable " << p->name;
+
+ ci->SetLevel(p->name, ChanServ::ACCESS_INVALID);
+ EventManager::Get()->Dispatch(&Event::LevelChange::OnLevelChange, source, ci, p->name, ChanServ::ACCESS_INVALID);
+
+ source.Reply(_("Privileged \002{0}\002 disabled on channel \002{1}\002."), p->name, ci->GetName());
+ return;
+ }
+
+ CommandInfo *help = source.service->FindCommand("generic/help");
+ if (help)
+ source.Reply(_("There is no such privilege \002{0}\002. See \002{0}{1} {2} {3}\002 for a list of valid settings."),
+ what, Config->StrictPrivmsg, source.service->nick, help->cname, source.command);
+ }
+
+ void DoList(CommandSource &source, ChanServ::Channel *ci)
+ {
+ if (!ChanServ::service)
+ return;
+
+ source.Reply(_("Access level settings for channel \002{0}\002"), ci->GetName());
+
+ ListFormatter list(source.GetAccount());
+ list.AddColumn(_("Name")).AddColumn(_("Level"));
+
+ const std::vector<ChanServ::Privilege> &privs = ChanServ::service->GetPrivileges();
+
+ for (unsigned i = 0; i < privs.size(); ++i)
+ {
+ const ChanServ::Privilege &p = privs[i];
+ int16_t j = ci->GetLevel(p.name);
+
+ ListFormatter::ListEntry entry;
+ entry["Name"] = p.name;
+
+ if (j == ChanServ::ACCESS_INVALID)
+ entry["Level"] = Language::Translate(source.GetAccount(), _("(disabled)"));
+ else if (j == ChanServ::ACCESS_FOUNDER)
+ entry["Level"] = Language::Translate(source.GetAccount(), _("(founder only)"));
+ else
+ entry["Level"] = stringify(j);
+
+ list.AddEntry(entry);
+ }
+
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+ }
+
+ void DoReset(CommandSource &source, ChanServ::Channel *ci)
+ {
+ bool override = !source.AccessFor(ci).HasPriv("FOUNDER");
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to reset all levels";
+
+ ci->ClearLevels();
+ EventManager::Get()->Dispatch(&Event::LevelChange::OnLevelChange, source, ci, "ALL", 0);
+
+ source.Reply(_("Levels for \002{0}\002 reset to defaults."), ci->GetName());
+ }
+
+ public:
+ CommandCSLevels(Module *creator) : Command(creator, "chanserv/levels", 2, 4)
+ {
+ this->SetDesc(_("Redefine the meanings of access levels"));
+ this->SetSyntax(_("\037channel\037 SET \037privilege\037 \037level\037"));
+ this->SetSyntax(_("\037channel\037 {DIS | DISABLE} \037privilege\037"));
+ this->SetSyntax(_("\037channel\037 LIST"));
+ this->SetSyntax(_("\037channel\037 RESET"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &chan = params[0];
+ const Anope::string &cmd = params[1];
+ const Anope::string &what = params.size() > 2 ? params[2] : "";
+ const Anope::string &s = params.size() > 3 ? params[3] : "";
+
+ ChanServ::Channel *ci = ChanServ::Find(chan);
+ if (ci == NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
+ return;
+ }
+
+ bool has_access = false;
+ if (source.HasPriv("chanserv/access/modify"))
+ has_access = true;
+ else if (cmd.equals_ci("LIST") && source.HasPriv("chanserv/access/list"))
+ has_access = true;
+ else if (source.AccessFor(ci).HasPriv("FOUNDER"))
+ has_access = true;
+
+ /* If SET, we want two extra parameters; if DIS[ABLE] or FOUNDER, we want only
+ * one; else, we want none.
+ */
+ if (cmd.equals_ci("SET") ? s.empty() : (cmd.substr(0, 3).equals_ci("DIS") ? (what.empty() || !s.empty()) : !what.empty()))
+ {
+ this->OnSyntaxError(source, cmd);
+ return;
+ }
+
+ if (!has_access)
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "FOUNDER", ci->GetName());
+ return;
+ }
+
+ if (Anope::ReadOnly && !cmd.equals_ci("LIST"))
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ if (cmd.equals_ci("SET"))
+ this->DoSet(source, ci, params);
+ else if (cmd.equals_ci("DIS") || cmd.equals_ci("DISABLE"))
+ this->DoDisable(source, ci, params);
+ else if (cmd.equals_ci("LIST"))
+ this->DoList(source, ci);
+ else if (cmd.equals_ci("RESET"))
+ this->DoReset(source, ci);
+ else
+ this->OnSyntaxError(source, "");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ if (subcommand.equals_ci("DESC"))
+ {
+ source.Reply(_("The following privileges are available:"));
+
+ ListFormatter list(source.GetAccount());
+ list.AddColumn(_("Name")).AddColumn(_("Description"));
+
+ if (ChanServ::service)
+ {
+ const std::vector<ChanServ::Privilege> &privs = ChanServ::service->GetPrivileges();
+ for (unsigned i = 0; i < privs.size(); ++i)
+ {
+ const ChanServ::Privilege &p = privs[i];
+ ListFormatter::ListEntry entry;
+ entry["Name"] = p.name;
+ entry["Description"] = Language::Translate(source.nc, p.desc.c_str());
+ list.AddEntry(entry);
+ }
+ }
+
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+ }
+ else
+ {
+ ServiceBot *bi;
+ Anope::string name;
+ if (!Command::FindCommandFromService("chanserv/access", bi, name) || bi != source.service)
+ return false;
+ CommandInfo *help = source.service->FindCommand("generic/help");
+ if (!help)
+ return false;
+
+ source.Reply(_("The \002{0}\002 command allows fine control over the meaning of numeric access levels used in the \002{1}\001 command.\n"
+ "\n"
+ "\002{0} SET\002 allows changing which \037privilege\037 is included in a given \037level\37.\n"
+ "\n"
+ "\002{0} DISABLE\002 disables a privilege and prevents anyone from be granted it, even channel founders."
+ " The \002{2}\002 privilege can not be disabled.\n"
+ "\n"
+ "\002{0} LIST\002 shows the current level for each privilege.\n"
+ "\n"
+ "\002{0} RESET\002 resets the levels to the default levels for newly registered channels.\n"
+ "\n"
+ "For the list of privileges and their descriptions, see \002{3} {4} DESC\002."),
+ source.command, name, "FOUNDER", help->cname, source.command);
+ }
+ return true;
+ }
+};
+
+class CSAccess : public Module
+ , public EventHook<Event::GroupCheckPriv>
+{
+ CommandCSAccess commandcsaccess;
+ CommandCSLevels commandcslevels;
+ AccessChanAccessType accesschanaccesstype;
+
+ public:
+ CSAccess(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , EventHook<Event::GroupCheckPriv>(this)
+ , commandcsaccess(this)
+ , commandcslevels(this)
+ , accesschanaccesstype(this)
+ {
+ this->SetPermanent(true);
+
+ }
+
+ EventReturn OnGroupCheckPriv(const ChanServ::AccessGroup *group, const Anope::string &priv) override
+ {
+ if (group->ci == NULL)
+ return EVENT_CONTINUE;
+ /* Special case. Allows a level of -1 to match anyone, and a level of 0 to match anyone identified. */
+ int16_t level = group->ci->GetLevel(priv);
+ if (level == -1)
+ return EVENT_ALLOW;
+ else if (level == 0 && group->nc)
+ return EVENT_ALLOW;
+ return EVENT_CONTINUE;
+ }
+};
+
+template<> void ModuleInfo<CSAccess>(ModuleDef *def)
+{
+ def->Depends("chanserv");
+}
+
+MODULE_INIT(CSAccess)
diff --git a/modules/chanserv/akick.cpp b/modules/chanserv/akick.cpp
new file mode 100644
index 000000000..71ea85e73
--- /dev/null
+++ b/modules/chanserv/akick.cpp
@@ -0,0 +1,735 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/chanserv/akick.h"
+
+class AutoKickImpl : public AutoKick
+{
+ friend class AutoKickType;
+
+ ChanServ::Channel *channel = nullptr;
+ NickServ::Account *account = nullptr;
+ Anope::string mask, reason, creator;
+ time_t addtime = 0, last_time = 0;
+
+ public:
+ AutoKickImpl(Serialize::TypeBase *type) : AutoKick(type) { }
+ AutoKickImpl(Serialize::TypeBase *type, Serialize::ID id) : AutoKick(type, id) { }
+
+ ChanServ::Channel *GetChannel() override;
+ void SetChannel(ChanServ::Channel *ci) override;
+
+ Anope::string GetMask() override;
+ void SetMask(const Anope::string &mask) override;
+
+ NickServ::Account *GetAccount() override;
+ void SetAccount(NickServ::Account *nc) override;
+
+ Anope::string GetReason() override;
+ void SetReason(const Anope::string &r) override;
+
+ Anope::string GetCreator() override;
+ void SetCreator(const Anope::string &c) override;
+
+ time_t GetAddTime() override;
+ void SetAddTime(const time_t &t) override;
+
+ time_t GetLastUsed() override;
+ void SetLastUsed(const time_t &t) override;
+};
+
+class AutoKickType : public Serialize::Type<AutoKickImpl>
+{
+ public:
+ Serialize::ObjectField<AutoKickImpl, ChanServ::Channel *> ci;
+
+ Serialize::Field<AutoKickImpl, Anope::string> mask;
+ Serialize::ObjectField<AutoKickImpl, NickServ::Account *> nc;
+
+ Serialize::Field<AutoKickImpl, Anope::string> reason;
+ Serialize::Field<AutoKickImpl, Anope::string> creator;
+ Serialize::Field<AutoKickImpl, time_t> addtime;
+ Serialize::Field<AutoKickImpl, time_t> last_time;
+
+ AutoKickType(Module *me)
+ : Serialize::Type<AutoKickImpl>(me)
+ , ci(this, "ci", &AutoKickImpl::channel, true)
+ , mask(this, "mask", &AutoKickImpl::mask)
+ , nc(this, "nc", &AutoKickImpl::account, true)
+ , reason(this, "reason", &AutoKickImpl::reason)
+ , creator(this, "creator", &AutoKickImpl::creator)
+ , addtime(this, "addtime", &AutoKickImpl::addtime)
+ , last_time(this, "last_time", &AutoKickImpl::last_time)
+ {
+ }
+};
+
+ChanServ::Channel *AutoKickImpl::GetChannel()
+{
+ return Get(&AutoKickType::ci);
+}
+
+void AutoKickImpl::SetChannel(ChanServ::Channel *ci)
+{
+ Set(&AutoKickType::ci, ci);
+}
+
+Anope::string AutoKickImpl::GetMask()
+{
+ return Get(&AutoKickType::mask);
+}
+
+void AutoKickImpl::SetMask(const Anope::string &mask)
+{
+ Set(&AutoKickType::mask, mask);
+}
+
+NickServ::Account *AutoKickImpl::GetAccount()
+{
+ return Get(&AutoKickType::nc);
+}
+
+void AutoKickImpl::SetAccount(NickServ::Account *nc)
+{
+ Set(&AutoKickType::nc, nc);
+}
+
+Anope::string AutoKickImpl::GetReason()
+{
+ return Get(&AutoKickType::reason);
+}
+
+void AutoKickImpl::SetReason(const Anope::string &r)
+{
+ Set(&AutoKickType::reason, r);
+}
+
+Anope::string AutoKickImpl::GetCreator()
+{
+ return Get(&AutoKickType::creator);
+}
+
+void AutoKickImpl::SetCreator(const Anope::string &c)
+{
+ Set(&AutoKickType::creator, c);
+}
+
+time_t AutoKickImpl::GetAddTime()
+{
+ return Get(&AutoKickType::addtime);
+}
+
+void AutoKickImpl::SetAddTime(const time_t &t)
+{
+ Set(&AutoKickType::addtime, t);
+}
+
+time_t AutoKickImpl::GetLastUsed()
+{
+ return Get(&AutoKickType::last_time);
+}
+
+void AutoKickImpl::SetLastUsed(const time_t &t)
+{
+ Set(&AutoKickType::last_time, t);
+}
+
+class CommandCSAKick : public Command
+{
+ void DoAdd(CommandSource &source, ChanServ::Channel *ci, const std::vector<Anope::string> &params)
+ {
+ Anope::string mask = params[2];
+ Anope::string reason = params.size() > 3 ? params[3] : "";
+ NickServ::Nick *na = NickServ::FindNick(mask);
+ NickServ::Account *nc = NULL;
+ unsigned reasonmax = Config->GetModule("chanserv")->Get<unsigned>("reasonmax", "200");
+
+ if (reason.length() > reasonmax)
+ reason = reason.substr(0, reasonmax);
+
+ if (IRCD->IsExtbanValid(mask))
+ ; /* If this is an extban don't try to complete the mask */
+ else if (IRCD->IsChannelValid(mask))
+ {
+ /* Also don't try to complete the mask if this is a channel */
+
+ if (mask.equals_ci(ci->GetName()) && ci->HasFieldS("PEACE"))
+ {
+ source.Reply(_("Access denied."));
+ return;
+ }
+ }
+ else if (!na)
+ {
+ /* If the mask contains a realname the reason must be prepended with a : */
+ if (mask.find('#') != Anope::string::npos)
+ {
+ size_t r = reason.find(':');
+ if (r != Anope::string::npos)
+ {
+ mask += " " + reason.substr(0, r);
+ mask.trim();
+ reason = reason.substr(r + 1);
+ reason.trim();
+ }
+ else
+ {
+ mask = mask + " " + reason;
+ reason.clear();
+ }
+ }
+
+ Entry e("", mask);
+
+ mask = (e.nick.empty() ? "*" : e.nick) + "!"
+ + (e.user.empty() ? "*" : e.user) + "@"
+ + (e.host.empty() ? "*" : e.host);
+ if (!e.real.empty())
+ mask += "#" + e.real;
+ }
+ else
+ nc = na->GetAccount();
+
+ /* Check excepts BEFORE we get this far */
+ if (ci->c)
+ {
+ std::vector<Anope::string> modes = ci->c->GetModeList("EXCEPT");
+ for (unsigned int i = 0; i < modes.size(); ++i)
+ {
+ if (Anope::Match(modes[i], mask))
+ {
+ source.Reply(_("\002{0}\002 matches an except on \002{1}\002 and cannot be banned until the except has been removed."), mask, ci->GetName());
+ return;
+ }
+ }
+ }
+
+ bool override = !source.AccessFor(ci).HasPriv("AKICK");
+ /* Opers overriding get to bypass PEACE */
+ if (override)
+ ;
+ /* These peace checks are only for masks */
+ else if (IRCD->IsChannelValid(mask))
+ ;
+ /* Check whether target nick has equal/higher access
+ * or whether the mask matches a user with higher/equal access - Viper */
+ else if (ci->HasFieldS("PEACE") && nc)
+ {
+ ChanServ::AccessGroup nc_access = ci->AccessFor(nc), u_access = source.AccessFor(ci);
+ if (nc == ci->GetFounder() || nc_access >= u_access)
+ {
+ source.Reply(_("Access denied. You can not auto kick \002{0}\002 because they have more privileges than you on \002{1}\002 and \002{2}\002 is enabled."), nc->GetDisplay(), ci->GetName(), "PEACE");
+ return;
+ }
+ }
+ else if (ci->HasFieldS("PEACE"))
+ {
+#warning "peace"
+#if 0
+ /* Match against all currently online users with equal or
+ * higher access. - Viper */
+ for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it)
+ {
+ User *u2 = it->second;
+
+ ChanServ::AccessGroup nc_access = ci->AccessFor(nc), u_access = source.AccessFor(ci);
+ Entry entry_mask("", mask);
+
+ if ((ci->AccessFor(u2).HasPriv("FOUNDER") || nc_access >= u_access) && entry_mask.Matches(u2))
+ {
+ source.Reply(ACCESS_DENIED);
+ return;
+ }
+ }
+
+ /* Match against the lastusermask of all nickalias's with equal
+ * or higher access. - Viper */
+ for (nickalias_map::const_iterator it = NickServ::NickList->begin(), it_end = NickServ::NickList->end(); it != it_end; ++it)
+ {
+ na = it->second;
+
+ ChanServ::AccessGroup nc_access = ci->AccessFor(na->GetAccount()), u_access = source.AccessFor(ci);
+ if (na->GetAccount() && (na->GetAccount() == ci->GetFounder() || nc_access >= u_access))
+ {
+ Anope::string buf = na->GetNick() + "!" + na->GetLastUsermask();
+ if (Anope::Match(buf, mask))
+ {
+ source.Reply(ACCESS_DENIED);
+ return;
+ }
+ }
+ }
+#endif
+ }
+
+ for (unsigned j = 0, end = ci->GetAkickCount(); j < end; ++j)
+ {
+ AutoKick *ak = ci->GetAkick(j);
+ if (ak->GetAccount() ? ak->GetAccount() == nc : mask.equals_ci(ak->GetMask()))
+ {
+ source.Reply(_("\002{0}\002 already exists on \002{1}\002 autokick list."), ak->GetAccount() ? ak->GetAccount()->GetDisplay() : ak->GetMask(), ci->GetName());
+ return;
+ }
+ }
+
+ if (ci->GetAkickCount() >= Config->GetModule(this->GetOwner())->Get<unsigned>("autokickmax"))
+ {
+ source.Reply(_("Sorry, you can only have \002{0}\002 autokick masks on a channel."), Config->GetModule(this->GetOwner())->Get<unsigned>("autokickmax"));
+ return;
+ }
+
+ AutoKick *ak;
+ if (nc)
+ ak = ci->AddAkick(source.GetNick(), nc, reason);
+ else
+ ak = ci->AddAkick(source.GetNick(), mask, reason);
+
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to add " << mask << (reason == "" ? "" : ": ") << reason;
+
+ EventManager::Get()->Dispatch(&Event::Akick::OnAkickAdd, source, ci, ak);
+
+ source.Reply(_("\002{0}\002 added to \002{1}\002 autokick list."), mask, ci->GetName());
+
+ this->DoEnforce(source, ci);
+ }
+
+ void DoDel(CommandSource &source, ChanServ::Channel *ci, const std::vector<Anope::string> &params)
+ {
+ const Anope::string &mask = params[2];
+ bool override = !source.AccessFor(ci).HasPriv("AKICK");
+
+ if (!ci->GetAkickCount())
+ {
+ source.Reply(_("The autokick list of \002{0}\002 is empty."), ci->GetName());
+ return;
+ }
+
+ /* Special case: is it a number/list? Only do search if it isn't. */
+ if (isdigit(mask[0]) && mask.find_first_not_of("1234567890,-") == Anope::string::npos)
+ {
+ unsigned int deleted = 0;
+
+ NumberList(mask, true,
+ [&](unsigned int number)
+ {
+ if (!number || number > ci->GetAkickCount())
+ return;
+
+ AutoKick *ak = ci->GetAkick(number - 1);
+
+ EventManager::Get()->Dispatch(&Event::Akick::OnAkickDel, source, ci, ak);
+
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to delete " << (ak->GetAccount() ? ak->GetAccount()->GetDisplay() : ak->GetMask());
+
+ ++deleted;
+ delete ak;
+ },
+ [&]()
+ {
+ if (!deleted)
+ source.Reply(_("There are no entries matching \002{0}\002 on the auto kick list of \002{1}\002."),
+ mask, ci->GetName());
+ else if (deleted == 1)
+ source.Reply(_("Deleted \0021\002 entry from the autokick list of \002{0}\002."), ci->GetName());
+ else
+ source.Reply(_("Deleted \002{0}\002 entries from \002{1}\002 autokick list."), deleted, ci->GetName());
+ });
+ }
+ else
+ {
+ NickServ::Nick *na = NickServ::FindNick(mask);
+ NickServ::Account *nc = na ? na->GetAccount() : nullptr;
+
+ unsigned int i, end;
+ for (i = 0, end = ci->GetAkickCount(); i < end; ++i)
+ {
+ AutoKick *ak = ci->GetAkick(i);
+
+ if (ak->GetAccount() ? ak->GetAccount() == nc : mask.equals_ci(ak->GetMask()))
+ break;
+ }
+
+ if (i == ci->GetAkickCount())
+ {
+ source.Reply(_("\002{0}\002 was not found on the auto kick list of \002{1}\002."), mask, ci->GetName());
+ return;
+ }
+
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to delete " << mask;
+
+ EventManager::Get()->Dispatch(&Event::Akick::OnAkickDel, source, ci, ci->GetAkick(i));
+
+ delete ci->GetAkick(i);
+
+ source.Reply(_("\002{0}\002 deleted from the auto kick list of \002{1}\002."), mask, ci->GetName());
+ }
+ }
+
+ void ProcessList(CommandSource &source, ChanServ::Channel *ci, const std::vector<Anope::string> &params, ListFormatter &list)
+ {
+ const Anope::string &mask = params.size() > 2 ? params[2] : "";
+
+ if (!mask.empty() && isdigit(mask[0]) && mask.find_first_not_of("1234567890,-") == Anope::string::npos)
+ {
+ NumberList(mask, false,
+ [&](unsigned int number)
+ {
+ if (!number || number > ci->GetAkickCount())
+ return;
+
+ AutoKick *ak = ci->GetAkick(number - 1);
+
+ Anope::string timebuf, lastused;
+ if (ak->GetAddTime())
+ timebuf = Anope::strftime(ak->GetAddTime(), NULL, true);
+ else
+ timebuf = _("<unknown>");
+ if (ak->GetLastUsed())
+ lastused = Anope::strftime(ak->GetLastUsed(), NULL, true);
+ else
+ lastused = _("<unknown>");
+
+ ListFormatter::ListEntry entry;
+ entry["Number"] = stringify(number);
+ if (ak->GetAccount())
+ entry["Mask"] = ak->GetAccount()->GetDisplay();
+ else
+ entry["Mask"] = ak->GetMask();
+ entry["Creator"] = ak->GetCreator();
+ entry["Created"] = timebuf;
+ entry["Last used"] = ak->GetLastUsed();
+ entry["Reason"] = ak->GetReason();
+ list.AddEntry(entry);
+ },
+ []{});
+ }
+ else
+ {
+ for (unsigned i = 0, end = ci->GetAkickCount(); i < end; ++i)
+ {
+ AutoKick *ak = ci->GetAkick(i);
+
+ if (!mask.empty())
+ {
+ if (!ak->GetAccount() && !Anope::Match(ak->GetMask(), mask))
+ continue;
+ if (ak->GetAccount() && !Anope::Match(ak->GetAccount()->GetDisplay(), mask))
+ continue;
+ }
+
+ Anope::string timebuf, lastused;
+ if (ak->GetAddTime())
+ timebuf = Anope::strftime(ak->GetAddTime(), NULL, true);
+ else
+ timebuf = _("<unknown>");
+ if (ak->GetLastUsed())
+ lastused = Anope::strftime(ak->GetLastUsed(), NULL, true);
+ else
+ lastused = _("<unknown>");
+
+ ListFormatter::ListEntry entry;
+ entry["Number"] = stringify(i + 1);
+ if (ak->GetAccount())
+ entry["Mask"] = ak->GetAccount()->GetDisplay();
+ else
+ entry["Mask"] = ak->GetMask();
+ entry["Creator"] = ak->GetCreator();
+ entry["Created"] = timebuf;
+ entry["Last used"] = lastused;
+ entry["Reason"] = ak->GetReason();
+ list.AddEntry(entry);
+ }
+ }
+
+ if (list.IsEmpty())
+ {
+ source.Reply(_("There are no entries matching \002{0}\002 on the autokick list of \002{1}\002."), mask, ci->GetName());
+ return;
+ }
+
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+ source.Reply(_("Autokick list for \002{0}\002:"), ci->GetName());
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+
+ source.Reply(_("End of autokick list."));
+ }
+
+ void DoList(CommandSource &source, ChanServ::Channel *ci, const std::vector<Anope::string> &params)
+ {
+ if (!ci->GetAkickCount())
+ {
+ source.Reply(_("The autokick list of \002{0}\002 is empty."), ci->GetName());
+ return;
+ }
+
+ ListFormatter list(source.GetAccount());
+ list.AddColumn(_("Number")).AddColumn(_("Mask")).AddColumn(_("Reason"));
+ this->ProcessList(source, ci, params, list);
+ }
+
+ void DoView(CommandSource &source, ChanServ::Channel *ci, const std::vector<Anope::string> &params)
+ {
+ if (!ci->GetAkickCount())
+ {
+ source.Reply(_("The autokick list of \002{0}\002 is empty."), ci->GetName());
+ return;
+ }
+
+ ListFormatter list(source.GetAccount());
+ list.AddColumn(_("Number")).AddColumn(_("Mask")).AddColumn(_("Creator")).AddColumn(_("Created")).AddColumn(_("Last used")).AddColumn(_("Reason"));
+ this->ProcessList(source, ci, params, list);
+ }
+
+ void DoEnforce(CommandSource &source, ChanServ::Channel *ci)
+ {
+ Channel *c = ci->c;
+ int count = 0;
+
+ if (!c)
+ {
+ source.Reply(_("Channel \002{0}\002 doesn't exist."), ci->GetName());
+ return;
+ }
+
+ for (Channel::ChanUserList::iterator it = c->users.begin(), it_end = c->users.end(); it != it_end; )
+ {
+ ChanUserContainer *uc = it->second;
+ ++it;
+
+ if (c->CheckKick(uc->user))
+ ++count;
+ }
+
+ bool override = !source.AccessFor(ci).HasPriv("AKICK");
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "ENFORCE, affects " << count << " users";
+
+ source.Reply(_("Autokick enforce for \002{0}\002 complete; \002{1}\002 users were affected."), ci->GetName(), count);
+ }
+
+ void DoClear(CommandSource &source, ChanServ::Channel *ci)
+ {
+ bool override = !source.AccessFor(ci).HasPriv("AKICK");
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to clear the akick list";
+
+ ci->ClearAkick();
+ source.Reply(_("The autokick list of \002{0}\002 has been cleared."), ci->GetName());
+ }
+
+ public:
+ CommandCSAKick(Module *creator) : Command(creator, "chanserv/akick", 2, 4)
+ {
+ this->SetDesc(_("Maintain the AutoKick list"));
+ this->SetSyntax(_("\037channel\037 ADD {\037nick\037 | \037mask\037} [\037reason\037]"));
+ this->SetSyntax(_("\037channel\037 DEL {\037nick\037 | \037mask\037 | \037entry-num\037 | \037list\037}"));
+ this->SetSyntax(_("\037channel\037 LIST [\037mask\037 | \037entry-num\037 | \037list\037]"));
+ this->SetSyntax(_("\037channel\037 VIEW [\037mask\037 | \037entry-num\037 | \037list\037]"));
+ this->SetSyntax(_("\037channel\037 ENFORCE"));
+ this->SetSyntax(_("\037channel\037 CLEAR"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &chan = params[0];
+ const Anope::string &cmd = params[1];
+ const Anope::string &mask = params.size() > 2 ? params[2] : "";
+
+ ChanServ::Channel *ci = ChanServ::Find(chan);
+ if (ci == NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
+ return;
+ }
+
+ bool is_list = cmd.equals_ci("LIST") || cmd.equals_ci("VIEW");
+
+ if (mask.empty() && (cmd.equals_ci("ADD") || cmd.equals_ci("DEL")))
+ {
+ this->OnSyntaxError(source, cmd);
+ return;
+ }
+
+ if (!source.AccessFor(ci).HasPriv("AKICK") && !source.HasPriv("chanserv/access/modify") && (!is_list || source.HasPriv("chanserv/access/list")))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "AKICK", ci->GetName());
+ return;
+ }
+
+ if (Anope::ReadOnly && (cmd.equals_ci("ADD") || cmd.equals_ci("DEL") || cmd.equals_ci("CLEAR")))
+ {
+ source.Reply(_("Sorry, channel autokick list modification is temporarily disabled."));
+ return;
+ }
+
+ if (cmd.equals_ci("ADD"))
+ this->DoAdd(source, ci, params);
+ else if (cmd.equals_ci("DEL"))
+ this->DoDel(source, ci, params);
+ else if (cmd.equals_ci("LIST"))
+ this->DoList(source, ci, params);
+ else if (cmd.equals_ci("VIEW"))
+ this->DoView(source, ci, params);
+ else if (cmd.equals_ci("ENFORCE"))
+ this->DoEnforce(source, ci);
+ else if (cmd.equals_ci("CLEAR"))
+ this->DoClear(source, ci);
+ else
+ this->OnSyntaxError(source, "");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ if (subcommand.equals_ci("ADD"))
+ source.Reply(_("The \002{0} ADD\002 command adds \037mask\037 to the auto kick list list of \037channel\037."
+ " If \037reason\037 is given, it is used when the user is kicked."
+ " If \037mask\037 is an account, then all users using the account will be affected.\n"
+ "\n"
+ "Examples:\n"
+ " {command} #anope ADD Cronus noob!\n"
+ " Adds the account \"Cronus\" to the auto kick list of \"#anope\" with the reason \"noob!\".\n"
+ "\n"
+ " {command} #anope ADD Guest*!*@*\n"
+ " Adds the mask \"Guest*!*@*\" to the auto kick list of \"#anope\"."),
+ source.command);
+ else if (subcommand.equals_ci("DEL"))
+ source.Reply(_("The \002{0} DEL\002 command removes \037mask\037 from the auto kick list."
+ " It does not, however, remove any bans placed by an auto kick; those must be removed manually.\n"
+ "\n"
+ "Example:\n"
+ " {command} #anope DEL Cronus!\n"
+ " Removes the auto kick for \"Cronus\" from the auto kick list of \"#anope\".\n"),
+ source.command);
+ else if (subcommand.equals_ci("LIST") || subcommand.equals_ci("VIEW"))
+ source.Reply(_("The \002{0} LIST\002 and \002{0} VIEW\002 command displays the auto kick list of \037channel\037."
+ " If a wildcard mask is given, only those entries matching the mask are displayed."
+ " If a list of entry numbers is given, only those entries are shown."
+ " \002VIEW\002 is similar to \002LIST\002 but also shows who created the auto kick entry, when it was created, and when it was last used."
+ "\n"
+ "Example:\n"
+ " \002{0} #anope LIST 2-5,7-9\002\n"
+ " Lists auto kick entries numbered 2 through 5 and 7 through 9 on #anope."),
+ source.command);
+ else if (subcommand.equals_ci("ENFORCE"))
+ source.Reply(_("The \002{0} ENFORCE\002 command enforces the auto kick list by forcibly removing users who match an entry on the auto kick list."
+ "This can be useful if someone does not authenticate or change their host mask until after joining.\n"
+ "\n"
+ "Example:\n"
+ " \002{0} #anope ENFORCE\002\n"
+ " Enforces the auto kick list of #anope."),
+ source.command);
+ else if (subcommand.equals_ci("CLEAR"))
+ source.Reply(_("The \002{0} CLEAR\002 command clears the auto kick list of \037channel\37. As with the \002DEL\002 command, existing bans placed by auto kick will not be removed.\n"
+ "\n"
+ "Example:\n"
+ " \002{0} #anope CLEAR\002\n"
+ " Clears the auto kick list of #anope."),
+ source.command);
+ else
+ {
+ source.Reply(_("Maintains the auto kick list for \037channel\037."
+ " If a user matching an entry on the auto kick list joins the channel, {0} will place a ban on the user and kick them.\n"
+ "\n"
+ "Use of this command requires the \002{1}\002 privilege on \037channel\037."),
+ source.service->nick, "AKICK");
+ CommandInfo *help = source.service->FindCommand("generic/help");
+ if (help)
+ source.Reply(_("\n"
+ "The \002ADD\002 command adds \037nick\037 or \037mask\037 to the auto kick list of \037channel\037.\n"
+ "\002{msg}{service} {help} {command} ADD\002 for more information.\n"
+ "\n"
+ "The \002DEL\002 command removes \037mask\037 from the auto kick list of \037channel\037.\n"
+ "\002{msg}{service} {help} {command} DEL\002 for more information.\n"
+ "\n"
+ "The \002LIST\002 and \002VIEW\002 commands both display the auto kick list of \037channel\037, but \002VIEW\002 also displays who created the auto kick entry, when it was created, and when it was last used.\n"
+ "\002{msg}{service} {help} {command} [LIST | VIEW]\002 for more information.\n"
+ "\n"
+ "The \002ENFORCE\002 command enforces the auto kick list by forcibly removing users who match an entry on the auto kick list.\n"
+ "\002{msg}{service} {help} {command} ENFORCE\002 for more information.\n"
+ ""
+ "The \002CLEAR\002 clears the auto kick list of \037channel\037."
+ "\002{msg}{service} {help} {command} CLEAR\002 for more information.\n"),
+ "msg"_kw = Config->StrictPrivmsg, "service"_kw = source.service->nick, "help"_kw = help->cname, "command"_kw = source.command);
+ }
+
+ return true;
+ }
+};
+
+class CSAKick : public Module
+ , public EventHook<Event::CheckKick>
+{
+ CommandCSAKick commandcsakick;
+ AutoKickType akick_type;
+
+ public:
+ CSAKick(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , EventHook<Event::CheckKick>(this)
+ , commandcsakick(this)
+ , akick_type(this)
+ {
+ }
+
+ EventReturn OnCheckKick(User *u, Channel *c, Anope::string &mask, Anope::string &reason) override
+ {
+ if (!c->ci || c->MatchesList(u, "EXCEPT"))
+ return EVENT_CONTINUE;
+
+ for (unsigned j = 0, end = c->ci->GetAkickCount(); j < end; ++j)
+ {
+ AutoKick *ak = c->ci->GetAkick(j);
+ bool kick = false;
+
+ if (ak->GetAccount())
+ kick = ak->GetAccount() == u->Account();
+ else if (IRCD->IsChannelValid(ak->GetMask()))
+ {
+ Channel *chan = Channel::Find(ak->GetMask());
+ kick = chan != NULL && chan->FindUser(u);
+ }
+ else
+ kick = Entry("BAN", ak->GetMask()).Matches(u);
+
+ if (kick)
+ {
+ Log(LOG_DEBUG_2) << u->nick << " matched akick " << (ak->GetAccount() ? ak->GetAccount()->GetDisplay() : ak->GetMask());
+ ak->SetLastUsed(Anope::CurTime);
+ if (!ak->GetAccount() && ak->GetMask().find('#') == Anope::string::npos)
+ mask = ak->GetMask();
+ reason = ak->GetReason();
+ if (reason.empty())
+ {
+ reason = Language::Translate(u, Config->GetModule(this)->Get<Anope::string>("autokickreason"));
+ reason = reason.replace_all_cs("%n", u->nick);
+ reason = reason.replace_all_cs("%c", c->name);
+ }
+ if (reason.empty())
+ reason = Language::Translate(u, _("User has been banned from the channel"));
+ return EVENT_STOP;
+ }
+ }
+
+ return EVENT_CONTINUE;
+ }
+};
+
+MODULE_INIT(CSAKick)
diff --git a/modules/chanserv/ban.cpp b/modules/chanserv/ban.cpp
new file mode 100644
index 000000000..c8d9023e3
--- /dev/null
+++ b/modules/chanserv/ban.cpp
@@ -0,0 +1,275 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+
+static Module *me;
+
+class TempBan : public Timer
+{
+ private:
+ Anope::string channel;
+ Anope::string mask;
+ Anope::string mode;
+
+ public:
+ TempBan(time_t seconds, Channel *c, const Anope::string &banmask, const Anope::string &mod) : Timer(me, seconds), channel(c->name), mask(banmask), mode(mod) { }
+
+ void Tick(time_t ctime) override
+ {
+ Channel *c = Channel::Find(this->channel);
+ if (c)
+ c->RemoveMode(NULL, mode, this->mask);
+ }
+};
+
+class CommandCSBan : public Command
+{
+ public:
+ CommandCSBan(Module *creator) : Command(creator, "chanserv/ban", 2, 4)
+ {
+ this->SetDesc(_("Bans a given nick or mask on a channel"));
+ this->SetSyntax(_("\037channel\037 [+\037expiry\037] {\037nick\037 | \037mask\037} [\037reason\037]"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &chan = params[0];
+ Configuration::Block *block = Config->GetCommand(source);
+ const Anope::string &mode = block->Get<Anope::string>("mode", "BAN");
+
+ ChanServ::Channel *ci = ChanServ::Find(chan);
+ if (ci == NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
+ return;
+ }
+
+ Channel *c = ci->c;
+ if (c == NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 doesn't exist."), ci->GetName());
+ return;
+ }
+
+ if (IRCD->GetMaxListFor(c) && c->HasMode(mode) >= IRCD->GetMaxListFor(c))
+ {
+ source.Reply(_("The %s list for %s is full."), mode.lower().c_str(), c->name.c_str());
+ return;
+ }
+
+ Anope::string expiry, target, reason;
+ time_t ban_time;
+ if (params[1][0] == '+')
+ {
+ ban_time = Anope::DoTime(params[1]);
+ if (ban_time == -1)
+ {
+ source.Reply(_("Invalid expiry time \002{0}\002."), params[1]);
+ return;
+ }
+ if (params.size() < 3)
+ {
+ this->SendSyntax(source);
+ return;
+ }
+ target = params[2];
+ reason = "Requested";
+ if (params.size() > 3)
+ reason = params[3];
+ }
+ else
+ {
+ ban_time = 0;
+ target = params[1];
+ reason = "Requested";
+ if (params.size() > 2)
+ reason = params[2];
+ if (params.size() > 3)
+ reason += " " + params[3];
+ }
+
+ unsigned reasonmax = Config->GetModule("chanserv")->Get<unsigned>("reasonmax", "200");
+ if (reason.length() > reasonmax)
+ reason = reason.substr(0, reasonmax);
+
+ Anope::string signkickformat = Config->GetModule("chanserv")->Get<Anope::string>("signkickformat", "%m (%n)");
+ signkickformat = signkickformat.replace_all_cs("%n", source.GetNick());
+
+ User *u = source.GetUser();
+ User *u2 = User::Find(target, true);
+
+ ChanServ::AccessGroup u_access = source.AccessFor(ci);
+
+ if (!u_access.HasPriv("BAN") && !source.HasPriv("chanserv/kick"))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "BAN", ci->GetName());
+ return;
+ }
+
+ if (u2)
+ {
+ ChanServ::AccessGroup u2_access = ci->AccessFor(u2);
+
+ if (u != u2 && ci->HasFieldS("PEACE") && u2_access >= u_access && !source.HasPriv("chanserv/kick"))
+ {
+ source.Reply(_("Access denied. \002{0}\002 has the same or more privileges than you on \002{1}\002."), u2->nick, ci->GetName());
+ return;
+ }
+
+ /*
+ * Don't ban/kick the user on channels where he is excepted
+ * to prevent services <-> server wars.
+ */
+ if (c->MatchesList(u2, "EXCEPT"))
+ {
+ source.Reply(_("\002{0}\002 matches an except on \002{1}\002 and can not be banned until the except has been removed."), u2->nick, ci->GetName());
+ return;
+ }
+
+ if (u2->IsProtected())
+ {
+ source.Reply(_("Access denied. \002{0}\002 is protected and can not be kicked."), u2->nick);
+ return;
+ }
+
+ Anope::string mask = ci->GetIdealBan(u2);
+
+ bool override = !u_access.HasPriv("BAN") || (u != u2 && ci->HasFieldS("PEACE") && u2_access >= u_access);
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "for " << mask;
+
+ if (!c->HasMode(mode, mask))
+ {
+ c->SetMode(NULL, mode, mask);
+ if (ban_time)
+ {
+ new TempBan(ban_time, c, mask, mode);
+ source.Reply(_("Ban on \002{0}\002 expires in \002{1}\002."), mask, Anope::Duration(ban_time, source.GetAccount()));
+ }
+ }
+
+ /* We still allow host banning while not allowing to kick */
+ if (!c->FindUser(u2))
+ return;
+
+ if (block->Get<bool>("kick", "yes"))
+ {
+ if (ci->HasFieldS("SIGNKICK") || (ci->HasFieldS("SIGNKICK_LEVEL") && !source.AccessFor(ci).HasPriv("SIGNKICK")))
+ {
+ signkickformat = signkickformat.replace_all_cs("%m", reason);
+ c->Kick(ci->WhoSends(), u2, "%s", signkickformat.c_str());
+ }
+ else
+ c->Kick(ci->WhoSends(), u2, "%s", reason.c_str());
+ }
+ }
+ else
+ {
+ bool founder = u_access.HasPriv("FOUNDER");
+ bool override = !founder && !u_access.HasPriv("BAN");
+
+ Anope::string mask = IRCD->NormalizeMask(target);
+
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "for " << mask;
+
+ if (!c->HasMode(mode, mask))
+ {
+ c->SetMode(NULL, mode, mask);
+ if (ban_time)
+ {
+ new TempBan(ban_time, c, mask, mode);
+ source.Reply(_("Ban on \002{0}\002 expires in \002{1}\002."), mask, Anope::Duration(ban_time, source.GetAccount()));
+ }
+ }
+
+ int matched = 0, kicked = 0;
+ for (Channel::ChanUserList::iterator it = c->users.begin(), it_end = c->users.end(); it != it_end;)
+ {
+ ChanUserContainer *uc = it->second;
+ ++it;
+
+ Entry e(mode, mask);
+ if (e.Matches(uc->user))
+ {
+ ++matched;
+
+ ChanServ::AccessGroup u2_access = ci->AccessFor(uc->user);
+
+ if (matched > 1 && !founder)
+ continue;
+ if (u != uc->user && ci->HasFieldS("PEACE") && u2_access >= u_access)
+ continue;
+ else if (ci->c->MatchesList(uc->user, "EXCEPT"))
+ continue;
+ else if (uc->user->IsProtected())
+ continue;
+
+ if (block->Get<bool>("kick", "yes"))
+ {
+ ++kicked;
+
+ if (ci->HasFieldS("SIGNKICK") || (ci->HasFieldS("SIGNKICK_LEVEL") && !u_access.HasPriv("SIGNKICK")))
+ {
+ reason += " (Matches " + mask + ")";
+ signkickformat = signkickformat.replace_all_cs("%m", reason);
+ c->Kick(ci->WhoSends(), uc->user, "%s", signkickformat.c_str());
+ }
+ else
+ c->Kick(ci->WhoSends(), uc->user, "%s (Matches %s)", reason.c_str(), mask.c_str());
+ }
+ }
+ }
+
+ if (matched)
+ source.Reply(_("Kicked \002{0}/{1}\002 users matching \002{2}\002 from \002{3}\002."), kicked, matched, mask, c->name);
+ else
+ source.Reply(_("No users on \002{0}\002 match \002{1}\002."), c->name, mask);
+ }
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Bans a given \037nick\037 or \037mask\037 on \002channel\002. An optional expiry may be given to cause services to remove the ban after a set amount of time.\n"
+ "\n"
+ "Use of this command requires the \002{0}\002 privilege on \037channel\037.\n"
+ "\n"
+ "Examples:\n"
+ " {command} #anope Jobe\n"
+ " Bans the user \"Jobe\" from \"#anope\".\n"
+ "\n"
+ " {command} #anope Guest!*@*\n"
+ " Bans the mask \"Guest!*@*\" on \"#anope\"."),
+ "BAN");
+ return true;
+ }
+};
+
+class CSBan : public Module
+{
+ CommandCSBan commandcsban;
+
+ public:
+ CSBan(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandcsban(this)
+ {
+ me = this;
+ }
+};
+
+MODULE_INIT(CSBan)
diff --git a/modules/chanserv/clone.cpp b/modules/chanserv/clone.cpp
new file mode 100644
index 000000000..8483b0af3
--- /dev/null
+++ b/modules/chanserv/clone.cpp
@@ -0,0 +1,235 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2010-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/botserv/badwords.h"
+#include "modules/chanserv/akick.h"
+
+class CommandCSClone : public Command
+{
+ ServiceReference<BadWords> badwords;
+
+#warning "levels hasnt been merged"
+#if 0
+ void CopyLevels(CommandSource &source, ChannelInfo *ci, ChannelInfo *target_ci)
+ {
+ const Anope::map<int16_t> &cilevels = ci->GetLevelEntries();
+
+ for (Anope::map<int16_t>::const_iterator it = cilevels.begin(); it != cilevels.end(); ++it)
+ {
+ target_ci->SetLevel(it->first, it->second);
+ }
+
+ source.Reply(_("All level entries from \002%s\002 have been cloned into \002%s\002."), ci->name.c_str(), target_ci->name.c_str());
+ }
+#endif
+
+public:
+ CommandCSClone(Module *creator) : Command(creator, "chanserv/clone", 2, 3)
+ {
+ this->SetDesc(_("Copy all settings from one channel to another"));
+ this->SetSyntax(_("\037channel\037 \037target\037 [\037what\037]"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &channel = params[0];
+ const Anope::string &target = params[1];
+ Anope::string what = params.size() > 2 ? params[2] : "";
+
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ User *u = source.GetUser();
+ ChanServ::Channel *ci = ChanServ::Find(channel);
+ bool override = false;
+
+ if (ci == NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), channel);
+ return;
+ }
+
+ ChanServ::Channel *target_ci = ChanServ::Find(target);
+ if (!target_ci)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), target);
+ return;
+ }
+
+ if (ci == target_ci)
+ {
+ source.Reply(_("Cannot clone channel \002{0}\002 to itself!"), ci->GetName());
+ return;
+ }
+
+ if (!source.IsFounder(ci) || !source.IsFounder(target_ci))
+ {
+ if (!source.HasPriv("chanserv/administration"))
+ {
+ source.Reply(_("Access denied. You do not have the privilege \002{0}\002 on \002{1}\002 and \002{2}\002."), "FOUNDER", ci->GetName(), target_ci->GetName());
+ return;
+ }
+ else
+ override = true;
+ }
+
+ if (what.equals_ci("ALL"))
+ what.clear();
+
+ if (what.empty())
+ {
+ target_ci->Delete();
+ target_ci = Serialize::New<ChanServ::Channel *>();
+ target_ci->SetName(target);
+ target_ci->SetTimeRegistered(Anope::CurTime);
+ ChanServ::registered_channel_map& map = ChanServ::service->GetChannels();
+ map[target_ci->GetName()] = target_ci;
+ target_ci->c = Channel::Find(target_ci->GetName());
+
+ if (ci->GetBot())
+ ci->GetBot()->Assign(u, target_ci);
+ else
+ target_ci->SetBot(nullptr);
+
+ if (target_ci->c)
+ {
+ target_ci->c->ci = target_ci;
+
+ target_ci->c->CheckModes();
+
+ target_ci->c->SetCorrectModes(u, true);
+ }
+
+ if (target_ci->c && !target_ci->c->topic.empty())
+ {
+ target_ci->SetLastTopic(target_ci->GetLastTopic());
+ target_ci->SetLastTopicSetter(target_ci->c->topic_setter);
+ target_ci->SetLastTopicTime(target_ci->c->topic_time);
+ }
+ else
+ {
+ target_ci->SetLastTopicSetter(source.service->nick);
+ }
+
+ EventManager::Get()->Dispatch(&Event::ChanRegistered::OnChanRegistered, target_ci);
+
+ source.Reply(_("All settings from \002{0}\002 have been cloned to \002{0}\002."), channel, target);
+ }
+ else if (what.equals_ci("ACCESS"))
+ {
+ std::set<Anope::string> masks;
+ unsigned access_max = Config->GetModule("chanserv")->Get<unsigned>("accessmax", "1024");
+ unsigned count = 0;
+
+ for (unsigned i = 0; i < target_ci->GetAccessCount(); ++i)
+ masks.insert(target_ci->GetAccess(i)->Mask());
+
+ for (unsigned i = 0; i < ci->GetAccessCount(); ++i)
+ {
+ ChanServ::ChanAccess *taccess = ci->GetAccess(i);
+
+ if (access_max && target_ci->GetAccessCount() >= access_max)
+ break;
+
+ if (masks.count(taccess->Mask()))
+ continue;
+ masks.insert(taccess->Mask());
+
+ ChanServ::ChanAccess *newaccess = anope_dynamic_static_cast<ChanServ::ChanAccess *>(taccess->GetSerializableType()->Create());
+ newaccess->SetChannel(taccess->GetChannel());
+ newaccess->SetMask(taccess->GetMask());
+ newaccess->SetMask(taccess->GetMask());
+ newaccess->SetCreator(taccess->GetCreator());
+ newaccess->SetLastSeen(taccess->GetLastSeen());
+ newaccess->SetCreated(taccess->GetCreated());
+ newaccess->AccessUnserialize(taccess->AccessSerialize());
+
+ ++count;
+ }
+
+ source.Reply(_("\002{0}\002 access entries from \002{1}\002 have been cloned to \002{2}\002."), count, channel, target);
+ }
+ else if (what.equals_ci("AKICK"))
+ {
+ target_ci->ClearAkick();
+ for (unsigned i = 0; i < ci->GetAkickCount(); ++i)
+ {
+ AutoKick *ak = ci->GetAkick(i);
+ if (ak->GetAccount())
+ target_ci->AddAkick(ak->GetCreator(), ak->GetAccount(), ak->GetReason(), ak->GetAddTime(), ak->GetLastUsed());
+ else
+ target_ci->AddAkick(ak->GetCreator(), ak->GetMask(), ak->GetReason(), ak->GetAddTime(), ak->GetLastUsed());
+ }
+
+ source.Reply(_("All auto kick entries from \002{0}\002 have been cloned to \002{1}\002."), channel, target);
+ }
+ else if (what.equals_ci("BADWORDS"))
+ {
+ if (!badwords)
+ {
+ source.Reply(_("All badword entries from \002{0}\002 have been cloned to \002{1}\002."), channel, target); // BotServ doesn't exist/badwords isn't loaded
+ return;
+ }
+
+ badwords->ClearBadWords(target_ci);
+
+ for (unsigned i = 0; i < badwords->GetBadWordCount(ci); ++i)
+ {
+ BadWord *bw = badwords->GetBadWord(ci, i);
+ badwords->AddBadWord(target_ci, bw->GetWord(), bw->GetType());
+ }
+
+ source.Reply(_("All badword entries from \002{0}\002 have been cloned to \002{1}\002."), channel, target);
+ }
+ else
+ {
+ this->OnSyntaxError(source, "");
+ return;
+ }
+
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to clone " << (what.empty() ? "everything from it" : what) << " to " << target_ci->GetName();
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Copies all settings, access, akicks, etc. from \037channel\037 to the \037target\037 channel."
+ " If \037what\037 is \002ACCESS\002, \002AKICK\002, or \002BADWORDS\002 then only the respective settings are cloned.\n"
+ "\n"
+ "You must be the founder of \037channel\037 and \037target\037."));
+ return true;
+ }
+};
+
+class CSClone : public Module
+{
+ CommandCSClone commandcsclone;
+
+ public:
+ CSClone(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandcsclone(this)
+ {
+
+ }
+};
+
+MODULE_INIT(CSClone)
diff --git a/modules/chanserv/drop.cpp b/modules/chanserv/drop.cpp
new file mode 100644
index 000000000..1bc880ae9
--- /dev/null
+++ b/modules/chanserv/drop.cpp
@@ -0,0 +1,105 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/chanserv/drop.h"
+
+class CommandCSDrop : public Command
+{
+ public:
+ CommandCSDrop(Module *creator) : Command(creator, "chanserv/drop", 1, 2)
+ {
+ this->SetDesc(_("Cancel the registration of a channel"));
+ this->SetSyntax(_("\037channel\037 \037channel\037"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &chan = params[0];
+
+ if (Anope::ReadOnly && !source.HasPriv("chanserv/administration"))
+ {
+ source.Reply(_("Sorry, channel de-registration is temporarily disabled."));
+ return;
+ }
+
+ ChanServ::Channel *ci = ChanServ::Find(chan);
+ if (ci == NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
+ return;
+ }
+
+ if (params.size() < 2 || !chan.equals_ci(params[1]))
+ {
+ source.Reply(_("You must enter the channel name twice as a confirmation that you wish to drop \002{0}\002."), ci->GetName());
+ return;
+ }
+
+ if ((ci->HasFieldS("SECUREFOUNDER") ? !source.IsFounder(ci) : !source.AccessFor(ci).HasPriv("FOUNDER")) && !source.HasCommand("chanserv/drop"))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "FOUNDER", ci->GetName());
+ return;
+ }
+
+ EventReturn MOD_RESULT = EventManager::Get()->Dispatch(&Event::ChanDrop::OnChanDrop, source, ci);
+ if (MOD_RESULT == EVENT_STOP)
+ return;
+
+ bool override = (ci->HasFieldS("SECUREFOUNDER") ? !source.IsFounder(ci) : !source.AccessFor(ci).HasPriv("FOUNDER"));
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "(founder was: " << (ci->GetFounder() ? ci->GetFounder()->GetDisplay() : "none") << ")";
+
+ Reference<Channel> c = ci->c;
+ ci->Delete();
+
+ source.Reply(_("Channel \002{0}\002 has been dropped."), chan);
+
+ if (c)
+ c->CheckModes();
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Unregisters \037channel\037. All channel settings will be deleted. As a precaution, you must give the \037channel\037 name twice.\n"
+ "\n"
+ "Use of this command requires the \002{0}\002 privilege on \037channel\037."),
+ "FOUNDER");
+
+ if (source.HasCommand("chanserv/drop"))
+ source.Reply(_("\n"
+ "As a Services Operator with the command \002{0}\002, you may drop any channel."),
+ "chanserv/drop");
+
+ return true;
+ }
+};
+
+class CSDrop : public Module
+{
+ CommandCSDrop commandcsdrop;
+
+ public:
+ CSDrop(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandcsdrop(this)
+ {
+
+ }
+};
+
+MODULE_INIT(CSDrop)
diff --git a/modules/commands/cs_enforce.cpp b/modules/chanserv/enforce.cpp
index b9f700284..5a57d3cc2 100644
--- a/modules/commands/cs_enforce.cpp
+++ b/modules/chanserv/enforce.cpp
@@ -1,14 +1,20 @@
-/* ChanServ core functions
+/*
+ * Anope IRC Services
*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
*
- * Original Coder: GeniusDex <geniusdex@anope.org>
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
*
- * Please read COPYING and README for further details.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * Send any bug reports to the Anope Coder, as he will be able
- * to deal with it best.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "module.h"
@@ -16,7 +22,7 @@
class CommandCSEnforce : public Command
{
private:
- void DoSecureOps(CommandSource &source, ChannelInfo *ci)
+ void DoSecureOps(CommandSource &source, ChanServ::Channel *ci)
{
bool override = !source.AccessFor(ci).HasPriv("AKICK") && source.HasPriv("chanserv/access/modify");
Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to enforce secureops";
@@ -26,8 +32,8 @@ class CommandCSEnforce : public Command
* part of the code. This way we can enforce SECUREOPS even
* if it's off.
*/
- bool hadsecureops = ci->HasExt("SECUREOPS");
- ci->Extend<bool>("SECUREOPS");
+ bool hadsecureops = ci->HasFieldS("SECUREOPS");
+ ci->SetS<bool>("SECUREOPS", true);
for (Channel::ChanUserList::iterator it = ci->c->users.begin(), it_end = ci->c->users.end(); it != it_end; ++it)
{
@@ -37,12 +43,12 @@ class CommandCSEnforce : public Command
}
if (!hadsecureops)
- ci->Shrink<bool>("SECUREOPS");
+ ci->UnsetS<bool>("SECUREOPS");
- source.Reply(_("Secureops enforced on %s."), ci->name.c_str());
+ source.Reply(_("\002Secureops\002 enforced on \002{0}\002."), ci->GetName());
}
- void DoRestricted(CommandSource &source, ChannelInfo *ci)
+ void DoRestricted(CommandSource &source, ChanServ::Channel *ci)
{
bool override = !source.AccessFor(ci).HasPriv("AKICK") && source.HasPriv("chanserv/access/modify");
Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to enforce restricted";
@@ -61,7 +67,7 @@ class CommandCSEnforce : public Command
}
for (unsigned i = 0; i < users.size(); ++i)
- {
+ {
User *user = users[i];
Anope::string mask = ci->GetIdealBan(user);
@@ -70,10 +76,10 @@ class CommandCSEnforce : public Command
ci->c->Kick(NULL, user, "%s", reason.c_str());
}
- source.Reply(_("Restricted enforced on %s."), ci->name.c_str());
+ source.Reply(_("\002Restricted\002 enforced on \002{0}\002."), ci->GetName());
}
- void DoRegOnly(CommandSource &source, ChannelInfo *ci)
+ void DoRegOnly(CommandSource &source, ChanServ::Channel *ci)
{
bool override = !source.AccessFor(ci).HasPriv("AKICK") && source.HasPriv("chanserv/access/modify");
Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to enforce registered only";
@@ -102,10 +108,10 @@ class CommandCSEnforce : public Command
ci->c->Kick(NULL, user, "%s", reason.c_str());
}
- source.Reply(_("Registered only enforced on %s."), ci->name.c_str());
+ source.Reply(_("\002Registered only\002 enforced on \002{0}\002."), ci->GetName());
}
- void DoSSLOnly(CommandSource &source, ChannelInfo *ci)
+ void DoSSLOnly(CommandSource &source, ChanServ::Channel *ci)
{
bool override = !source.AccessFor(ci).HasPriv("AKICK") && source.HasPriv("chanserv/access/modify");
Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to enforce SSL only";
@@ -119,7 +125,7 @@ class CommandCSEnforce : public Command
if (user->IsProtected())
continue;
- if (!user->HasMode("SSL") && !user->HasExt("ssl"))
+ if (!user->HasMode("SSL") && !user->HasExtOK("ssl"))
users.push_back(user);
}
@@ -134,10 +140,10 @@ class CommandCSEnforce : public Command
ci->c->Kick(NULL, user, "%s", reason.c_str());
}
- source.Reply(_("SSL only enforced on %s."), ci->name.c_str());
+ source.Reply(_("\002SSL only\002 enforced on %s."), ci->GetName().c_str());
}
- void DoBans(CommandSource &source, ChannelInfo *ci)
+ void DoBans(CommandSource &source, ChanServ::Channel *ci)
{
bool override = !source.AccessFor(ci).HasPriv("AKICK") && source.HasPriv("chanserv/access/modify");
Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to enforce bans";
@@ -158,15 +164,15 @@ class CommandCSEnforce : public Command
for (unsigned i = 0; i < users.size(); ++i)
{
User *user = users[i];
-
+
Anope::string reason = Language::Translate(user, _("BANS enforced by ")) + source.GetNick();
ci->c->Kick(NULL, user, "%s", reason.c_str());
}
- source.Reply(_("Bans enforced on %s."), ci->name.c_str());
+ source.Reply(_("\002Bans\002 enforced on %s."), ci->GetName().c_str());
}
- void DoLimit(CommandSource &source, ChannelInfo *ci)
+ void DoLimit(CommandSource &source, ChanServ::Channel *ci)
{
bool override = !source.AccessFor(ci).HasPriv("AKICK") && source.HasPriv("chanserv/access/modify");
Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to enforce limit";
@@ -174,7 +180,7 @@ class CommandCSEnforce : public Command
Anope::string l_str;
if (!ci->c->GetParam("LIMIT", l_str))
{
- source.Reply(_("No limit is set on %s."), ci->name.c_str());
+ source.Reply(_("There is no limit is set on \002{0}\002."), ci->GetName());
return;
}
@@ -187,7 +193,7 @@ class CommandCSEnforce : public Command
}
catch (const ConvertException &)
{
- source.Reply(_("The limit on %s is not valid."), ci->name.c_str());
+ source.Reply(_("The limit on \002{0}\002 is not valid."), ci->GetName());
return;
}
@@ -213,12 +219,12 @@ class CommandCSEnforce : public Command
for (unsigned i = 0; i < users.size(); ++i)
{
User *user = users[i];
-
+
Anope::string reason = Language::Translate(user, _("LIMIT enforced by ")) + source.GetNick();
ci->c->Kick(NULL, user, "%s", reason.c_str());
}
- source.Reply(_("LIMIT enforced on %s, %d users removed."), ci->name.c_str(), users.size());
+ source.Reply(_("LIMIT enforced on \002{0}\002, \002{1]\002 users removed."), ci->GetName(), users.size());
}
public:
@@ -228,19 +234,31 @@ class CommandCSEnforce : public Command
this->SetSyntax(_("\037channel\037 \037what\037"));
}
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
{
const Anope::string &what = params.size() > 1 ? params[1] : "";
- ChannelInfo *ci = ChannelInfo::Find(params[0]);
+ ChanServ::Channel *ci = ChanServ::Find(params[0]);
if (!ci)
- source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
- else if (!ci->c)
- source.Reply(CHAN_X_NOT_IN_USE, ci->name.c_str());
- else if (!source.AccessFor(ci).HasPriv("AKICK") && !source.HasPriv("chanserv/access/modify"))
- source.Reply(ACCESS_DENIED);
- else if (what.equals_ci("SECUREOPS"))
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), params[0]);
+ return;
+ }
+
+ if (!ci->c)
+ {
+ source.Reply(_("Channel \002{0}\002 doesn't exist."), ci->GetName());
+ return;
+ }
+
+ if (!source.AccessFor(ci).HasPriv("AKICK") && !source.HasPriv("chanserv/access/modify"))
+ {
+ source.Reply("Access denied. You do not have the \002{0}\002 privilege on \002{1}\002.", "AKICK", ci->GetName());
+ return;
+ }
+
+ if (what.equals_ci("SECUREOPS"))
this->DoSecureOps(source, ci);
else if (what.equals_ci("RESTRICTED"))
this->DoRestricted(source, ci);
@@ -256,23 +274,26 @@ class CommandCSEnforce : public Command
this->OnSyntaxError(source, "");
}
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
{
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Enforce various channel modes and set options. The \037channel\037\n"
- "option indicates what channel to enforce the modes and options\n"
- "on. The \037what\037 option indicates what modes and options to\n"
- "enforce, and can be any of \002SECUREOPS\002, \002RESTRICTED\002, \002REGONLY\002, \002SSLONLY\002,\n"
- "\002BANS\002, or \002LIMIT\002.\n"
- " \n"
- "Use \002SECUREOPS\002 to enforce the SECUREOPS option, even if it is not\n"
- "enabled. Use \002RESTRICTED\002 to enfore the RESTRICTED option, also\n"
- "if it's not enabled. Use \002REGONLY\002 to kick all unregistered users\n"
- "from the channel. Use \002SSLONLY\002 to kick all users not using a secure\n"
- "connection from the channel. \002BANS\002 will enforce bans on the channel by\n"
- "kicking users affected by them, and \002LIMIT\002 will kick users until the\n"
- "user count drops below the channel limit, if one is set."));
+ source.Reply(_("Enforce various channel modes and set options on \037channel\037."
+ "\n"
+ "The \002SECUREOPS\002 option enforces SECUREOPS, even if it is not enabled.\n"
+ "See \002/msg ChanServ HELP SET SECUREOPS\002 for information about SECUREOPS.\n" //XXX
+ "\n"
+ "The \002RESTRICTED\002 option enforces RESTRICTED, even if it is not enabled.\n"
+ "See \002/msg ChanServ HELP SET RESTRICTED\002 for information about RESTRICTED.\n" //XXX
+ "\n"
+ "The \002REGONLY\002 option removes all unregistered users.\n"
+ "\n"
+ "The \002SSLONLY\002 option removes all users not using a secure connection.\n"
+ "\n"
+ "The \002BANS\002 option enforces bans on the channel by removing users affected by then.\n"
+ "\n"
+ "The \002LIMIT\002 option will remove users until the user count drops below the channel limit, if one is set.\n"
+ "\n"
+ "Use of this command requires the \002{0}\002 privilege."),
+ "AKICK");
return true;
}
};
@@ -282,8 +303,8 @@ class CSEnforce : public Module
CommandCSEnforce commandcsenforce;
public:
- CSEnforce(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandcsenforce(this)
+ CSEnforce(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandcsenforce(this)
{
}
diff --git a/modules/chanserv/entrymsg.cpp b/modules/chanserv/entrymsg.cpp
new file mode 100644
index 000000000..b607ba399
--- /dev/null
+++ b/modules/chanserv/entrymsg.cpp
@@ -0,0 +1,285 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2010-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/chanserv/entrymsg.h"
+
+class EntryMsgImpl : public EntryMsg
+{
+ friend class EntryMsgType;
+
+ ChanServ::Channel *channel = nullptr;
+ Anope::string creator, message;
+ time_t when = 0;
+
+ public:
+ EntryMsgImpl(Serialize::TypeBase *type) : EntryMsg(type) { }
+ EntryMsgImpl(Serialize::TypeBase *type, Serialize::ID id) : EntryMsg(type, id) { }
+
+ ChanServ::Channel *GetChannel() override;
+ void SetChannel(ChanServ::Channel *ci) override;
+
+ Anope::string GetCreator() override;
+ void SetCreator(const Anope::string &c) override;
+
+ Anope::string GetMessage() override;
+ void SetMessage(const Anope::string &m) override;
+
+ time_t GetWhen() override;
+ void SetWhen(const time_t &t) override;
+};
+
+class EntryMsgType : public Serialize::Type<EntryMsgImpl>
+{
+ public:
+ Serialize::ObjectField<EntryMsgImpl, ChanServ::Channel *> chan;
+ Serialize::Field<EntryMsgImpl, Anope::string> creator, message;
+ Serialize::Field<EntryMsgImpl, time_t> when;
+
+ EntryMsgType(Module *me) : Serialize::Type<EntryMsgImpl>(me)
+ , chan(this, "chan", &EntryMsgImpl::channel, true)
+ , creator(this, "creator", &EntryMsgImpl::creator)
+ , message(this, "message", &EntryMsgImpl::message)
+ , when(this, "when", &EntryMsgImpl::when)
+ {
+ }
+};
+
+ChanServ::Channel *EntryMsgImpl::GetChannel()
+{
+ return Get(&EntryMsgType::chan);
+}
+
+void EntryMsgImpl::SetChannel(ChanServ::Channel *ci)
+{
+ Set(&EntryMsgType::chan, ci);
+}
+
+Anope::string EntryMsgImpl::GetCreator()
+{
+ return Get(&EntryMsgType::creator);
+}
+
+void EntryMsgImpl::SetCreator(const Anope::string &c)
+{
+ Set(&EntryMsgType::creator, c);
+}
+
+Anope::string EntryMsgImpl::GetMessage()
+{
+ return Get(&EntryMsgType::message);
+}
+
+void EntryMsgImpl::SetMessage(const Anope::string &m)
+{
+ Set(&EntryMsgType::message, m);
+}
+
+time_t EntryMsgImpl::GetWhen()
+{
+ return Get(&EntryMsgType::when);
+}
+
+void EntryMsgImpl::SetWhen(const time_t &t)
+{
+ Set(&EntryMsgType::when, t);
+}
+
+class CommandEntryMessage : public Command
+{
+ private:
+ void DoList(CommandSource &source, ChanServ::Channel *ci)
+ {
+ std::vector<EntryMsg *> messages = ci->GetRefs<EntryMsg *>();
+
+ if (messages.empty())
+ {
+ source.Reply(_("Entry message list for \002{0}\002 is empty."), ci->GetName());
+ return;
+ }
+
+ source.Reply(_("Entry message list for \002{0}\002:"), ci->GetName());
+
+ ListFormatter list(source.GetAccount());
+ list.AddColumn(_("Number")).AddColumn(_("Creator")).AddColumn(_("Created")).AddColumn(_("Message"));
+ for (unsigned i = 0; i < messages.size(); ++i)
+ {
+ EntryMsg *msg = messages[i];
+
+ ListFormatter::ListEntry entry;
+ entry["Number"] = stringify(i + 1);
+ entry["Creator"] = msg->GetCreator();
+ entry["Created"] = Anope::strftime(msg->GetWhen(), NULL, true);
+ entry["Message"] = msg->GetMessage();
+ list.AddEntry(entry);
+ }
+
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+
+ source.Reply(_("End of entry message list."));
+ }
+
+ void DoAdd(CommandSource &source, ChanServ::Channel *ci, const Anope::string &message)
+ {
+ std::vector<EntryMsg *> messages = ci->GetRefs<EntryMsg *>();
+
+ if (messages.size() >= Config->GetModule(this->GetOwner())->Get<unsigned>("maxentries"))
+ {
+ source.Reply(_("The entry message list for \002{0}\002 is full."), ci->GetName());
+ return;
+ }
+
+ EntryMsg *msg = Serialize::New<EntryMsg *>();
+ msg->SetChannel(ci);
+ msg->SetCreator(source.GetNick());
+ msg->SetMessage(message);
+ Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to add a message";
+ source.Reply(_("Entry message added to \002{0}\002"), ci->GetName());
+ }
+
+ void DoDel(CommandSource &source, ChanServ::Channel *ci, const Anope::string &message)
+ {
+ std::vector<EntryMsg *> messages = ci->GetRefs<EntryMsg *>();
+
+ if (!message.is_pos_number_only())
+ source.Reply(("Entry message \002{0}\002 not found on channel \002{1}\002."), message, ci->GetName());
+ else if (messages.empty())
+ source.Reply(_("Entry message list for \002{0}\002 is empty."), ci->GetName());
+ else
+ {
+ try
+ {
+ unsigned i = convertTo<unsigned>(message);
+ if (i > 0 && i <= messages.size())
+ {
+ messages[i - 1]->Delete();
+ Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to remove a message";
+ source.Reply(_("Entry message \002{0}\002 for \002{1]\002 deleted."), i, ci->GetName());
+ }
+ else
+ throw ConvertException();
+ }
+ catch (const ConvertException &)
+ {
+ source.Reply(_("Entry message \002{0}\002 not found on channel \002{1}\002."), message, ci->GetName());
+ }
+ }
+ }
+
+ void DoClear(CommandSource &source, ChanServ::Channel *ci)
+ {
+ for (EntryMsg *e : ci->GetRefs<EntryMsg *>())
+ delete e;
+
+ Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to remove all messages";
+ source.Reply(_("Entry messages for \002{0}\002 have been cleared."), ci->GetName());
+ }
+
+ public:
+ CommandEntryMessage(Module *creator) : Command(creator, "chanserv/entrymsg", 2, 3)
+ {
+ this->SetDesc(_("Manage the channel's entry messages"));
+ this->SetSyntax(_("\037channel\037 ADD \037message\037"));
+ this->SetSyntax(_("\037channel\037 DEL \037num\037"));
+ this->SetSyntax(_("\037channel\037 LIST"));
+ this->SetSyntax(_("\037channel\037 CLEAR"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &chan = params[0];
+
+ ChanServ::Channel *ci = ChanServ::Find(chan);
+ if (ci == NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
+ return;
+ }
+
+ if (Anope::ReadOnly && !params[1].equals_ci("LIST"))
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ if (!source.AccessFor(ci).HasPriv("SET") && !source.HasPriv("chanserv/administration"))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "FOUNDER", ci->GetName());
+ return;
+ }
+
+ if (params[1].equals_ci("LIST"))
+ this->DoList(source, ci);
+ else if (params[1].equals_ci("CLEAR"))
+ this->DoClear(source, ci);
+ else if (params.size() < 3)
+ this->OnSyntaxError(source, "");
+ else if (params[1].equals_ci("ADD"))
+ this->DoAdd(source, ci, params[2]);
+ else if (params[1].equals_ci("DEL"))
+ this->DoDel(source, ci, params[2]);
+ else
+ this->OnSyntaxError(source, "");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Controls what messages will be sent to users when they join \037channel\037.\n"
+ "\n"
+ "The \002{0} ADD\002 command adds the given message to the list of messages to be shown to users when they join the channel.\n"
+ "\n"
+ "The \002{0} DEL\002 command removes the given message from the entry message list.\n"
+ " You can remove the message by specifying its number which you can get by listing the messages as explained below.\n"
+ "\n"
+ "The \002{0} LIST\002 command displays the list of messages on the entry message list.\n"
+ "\n"
+ "The \002{0} CLEAR\002 command clears the entry message list.\n"
+ "\n"
+ "Use of this command requires the \002SET\002 privilege on \037channel\037."),
+ source.command);
+ return true;
+ }
+};
+
+class CSEntryMessage : public Module
+ , public EventHook<Event::JoinChannel>
+{
+ CommandEntryMessage commandentrymsg;
+ EntryMsgType entrymsg_type;
+
+ public:
+ CSEntryMessage(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , EventHook<Event::JoinChannel>(this)
+ , commandentrymsg(this)
+ , entrymsg_type(this)
+ {
+ }
+
+ void OnJoinChannel(User *u, Channel *c) override
+ {
+ if (u && c && c->ci && u->server->IsSynced())
+ for (EntryMsg *msg : c->ci->GetRefs<EntryMsg *>())
+ u->SendMessage(c->ci->WhoSends(), "[{0}] {1}", c->ci->GetName(), msg->GetMessage());
+ }
+};
+
+MODULE_INIT(CSEntryMessage)
diff --git a/modules/chanserv/flags.cpp b/modules/chanserv/flags.cpp
new file mode 100644
index 000000000..5f3212481
--- /dev/null
+++ b/modules/chanserv/flags.cpp
@@ -0,0 +1,541 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2011-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+/* Dependencies: anope_chanserv.main */
+
+#include "module.h"
+#include "modules/chanserv.h"
+#include "modules/chanserv/access.h"
+#include "modules/chanserv/main/chanaccess.h"
+#include "main/chanaccesstype.h"
+
+static std::map<Anope::string, char> defaultFlags;
+
+class FlagsChanAccessImpl : public FlagsChanAccess
+{
+ friend class FlagsChanAccessType;
+
+ Anope::string flags;
+
+ public:
+ using FlagsChanAccess::FlagsChanAccess;
+
+ const Anope::string &GetFlags() override;
+ void SetFlags(const Anope::string &) override;
+
+ bool HasPriv(const Anope::string &priv) override
+ {
+ std::map<Anope::string, char>::iterator it = defaultFlags.find(priv);
+ return it != defaultFlags.end() && GetFlags().find(it->second) != Anope::string::npos;
+ }
+
+ Anope::string AccessSerialize() override
+ {
+ return GetFlags();
+ }
+
+ void AccessUnserialize(const Anope::string &data) override
+ {
+ SetFlags(data);
+ }
+
+ static Anope::string DetermineFlags(ChanServ::ChanAccess *access)
+ {
+ if (access->GetSerializableType()->GetName() != NAME)
+ return access->AccessSerialize();
+
+ std::set<char> buffer;
+
+ for (std::map<Anope::string, char>::iterator it = defaultFlags.begin(), it_end = defaultFlags.end(); it != it_end; ++it)
+ if (access->HasPriv(it->first))
+ buffer.insert(it->second);
+
+ if (buffer.empty())
+ return "(none)";
+ else
+ return Anope::string(buffer.begin(), buffer.end());
+ }
+};
+
+class FlagsChanAccessType : public ChanAccessType<FlagsChanAccessImpl>
+{
+ public:
+ Serialize::Field<FlagsChanAccessImpl, Anope::string> flags;
+
+ FlagsChanAccessType(Module *me) : ChanAccessType<FlagsChanAccessImpl>(me)
+ , flags(this, "flags", &FlagsChanAccessImpl::flags)
+ {
+ Serialize::SetParent(FlagsChanAccess::NAME, ChanServ::ChanAccess::NAME);
+ }
+};
+
+const Anope::string &FlagsChanAccessImpl::GetFlags()
+{
+ return Get(&FlagsChanAccessType::flags);
+}
+
+void FlagsChanAccessImpl::SetFlags(const Anope::string &i)
+{
+ Object::Set(&FlagsChanAccessType::flags, i);
+}
+
+class CommandCSFlags : public Command
+{
+ void DoModify(CommandSource &source, ChanServ::Channel *ci, Anope::string mask, const Anope::string &flags)
+ {
+ if (flags.empty())
+ {
+ this->OnSyntaxError(source, "");
+ return;
+ }
+
+ ChanServ::AccessGroup u_access = source.AccessFor(ci);
+ ChanServ::ChanAccess *highest = u_access.Highest();
+
+ NickServ::Nick *na = nullptr;
+ ChanServ::Channel *targ_ci = nullptr;
+
+ if (IRCD->IsChannelValid(mask))
+ {
+ if (Config->GetModule("chanserv")->Get<bool>("disallow_channel_access"))
+ {
+ source.Reply(_("Channels may not be on access lists."));
+ return;
+ }
+
+ targ_ci = ChanServ::Find(mask);
+ if (targ_ci == NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), mask);
+ return;
+ }
+ else if (ci == targ_ci)
+ {
+ source.Reply(_("You can't add a channel to its own access list."));
+ return;
+ }
+
+ mask = targ_ci->GetName();
+ }
+ else
+ {
+ na = NickServ::FindNick(mask);
+ if (!na && Config->GetModule("chanserv")->Get<bool>("disallow_hostmask_access"))
+ {
+ source.Reply(_("Masks and unregistered users may not be on access lists."));
+ return;
+ }
+ else if (mask.find_first_of("!*@") == Anope::string::npos && !na)
+ {
+ User *targ = User::Find(mask, true);
+ if (targ != NULL)
+ mask = "*!*@" + targ->GetDisplayedHost();
+ else
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), mask);
+ return;
+ }
+ }
+
+ if (na)
+ mask = na->GetNick();
+ }
+
+ ChanServ::ChanAccess *current = NULL;
+ unsigned current_idx;
+ std::set<char> current_flags;
+ bool override = false;
+ for (current_idx = ci->GetAccessCount(); current_idx > 0; --current_idx)
+ {
+ ChanServ::ChanAccess *access = ci->GetAccess(current_idx - 1);
+ if ((na && na->GetAccount() == access->GetAccount()) || mask.equals_ci(access->Mask()))
+ {
+ // Flags allows removing others that have the same access as you,
+ // but no other access system does.
+ if (highest && highest->GetSerializableType()->GetName() != "FlagsChanAccess" && !u_access.founder)
+ // operator<= on the non-me entry!
+ if (*highest <= *access)
+ {
+ if (source.HasPriv("chanserv/access/modify"))
+ override = true;
+ else
+ {
+ source.Reply(_("Access denied. You do not have enough privileges on \002{0}\002 to modify the access of \002{1}\002."), ci->GetName(), access->Mask());
+ return;
+ }
+ }
+
+ current = access;
+ Anope::string cur_flags = FlagsChanAccessImpl::DetermineFlags(access);
+ for (unsigned j = cur_flags.length(); j > 0; --j)
+ current_flags.insert(cur_flags[j - 1]);
+ break;
+ }
+ }
+
+ unsigned access_max = Config->GetModule("chanserv")->Get<unsigned>("accessmax", "1024");
+ if (access_max && ci->GetAccessCount() >= access_max)
+ {
+ source.Reply(_("Sorry, you can only have \002{0}\002 access entries on a channel, including access entries from other channels."), access_max);
+ return;
+ }
+
+ ChanServ::Privilege *p = NULL;
+ bool add = true;
+ for (size_t i = 0; i < flags.length(); ++i)
+ {
+ char f = flags[i];
+ switch (f)
+ {
+ case '+':
+ add = true;
+ break;
+ case '-':
+ add = false;
+ break;
+ case '*':
+ for (std::map<Anope::string, char>::iterator it = defaultFlags.begin(), it_end = defaultFlags.end(); it != it_end; ++it)
+ {
+ bool has = current_flags.count(it->second);
+ // If we are adding a flag they already have or removing one they don't have, don't bother
+ if (add == has)
+ continue;
+
+ if (!u_access.HasPriv(it->first) && !u_access.founder)
+ {
+ if (source.HasPriv("chanserv/access/modify"))
+ override = true;
+ else
+ continue;
+ }
+
+ if (add)
+ current_flags.insert(it->second);
+ else
+ current_flags.erase(it->second);
+ }
+ break;
+ default:
+ p = ChanServ::service ? ChanServ::service->FindPrivilege(flags.substr(i)) : nullptr;
+ if (p != NULL && defaultFlags[p->name])
+ {
+ f = defaultFlags[p->name];
+ i = flags.length();
+ }
+
+ for (std::map<Anope::string, char>::iterator it = defaultFlags.begin(), it_end = defaultFlags.end(); it != it_end; ++it)
+ {
+ if (f != it->second)
+ continue;
+ else if (!u_access.HasPriv(it->first) && !u_access.founder)
+ {
+ if (source.HasPriv("chanserv/access/modify"))
+ override = true;
+ else
+ {
+ source.Reply(_("You can not set the \002{0}\002 flag."), f);
+ break;
+ }
+ }
+ if (add)
+ current_flags.insert(f);
+ else
+ current_flags.erase(f);
+ break;
+ }
+ }
+ }
+ if (current_flags.empty())
+ {
+ if (current != NULL)
+ {
+ EventManager::Get()->Dispatch(&Event::AccessDel::OnAccessDel, ci, source, current);
+ delete current;
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to delete " << mask;
+ source.Reply(_("\002{0}\002 removed from the access list of \002{1}\002."), mask, ci->GetName());
+ }
+ else
+ {
+ source.Reply(_("\002{0}\002 not found on the access list of \002{1}\002."), mask, ci->GetName());
+ }
+ return;
+ }
+
+ FlagsChanAccess *access = Serialize::New<FlagsChanAccess *>();
+ if (na)
+ access->SetObj(na->GetAccount());
+ else if (targ_ci)
+ access->SetObj(targ_ci);
+ access->SetChannel(ci);
+ access->SetMask(mask);
+ access->SetCreator(source.GetNick());
+ access->SetLastSeen(current ? current->GetLastSeen() : 0);
+ access->SetCreated(Anope::CurTime);
+ access->SetFlags(Anope::string(current_flags.begin(), current_flags.end()));
+
+ if (current != NULL)
+ delete current;
+
+ EventManager::Get()->Dispatch(&Event::AccessAdd::OnAccessAdd, ci, source, access);
+
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to modify " << mask << "'s flags to " << access->AccessSerialize();
+ if (p != NULL)
+ {
+ if (add)
+ source.Reply(_("Privilege \002{0}\002 added to \002{1}\002 on \002{2}\002, new flags are +\002{3}\002"), p->name, access->Mask(), ci->GetName(), access->AccessSerialize());
+ else
+ source.Reply(_("Privilege \002{0}\002 removed from \002{1}\002 on \002{2}\002, new flags are +\002{3}\002"), p->name, access->Mask(), ci->GetName(), access->AccessSerialize());
+ }
+ else
+ source.Reply(_("Flags for \002{0}\002 on \002{1}\002 set to +\002{2}\002"), access->Mask(), ci->GetName(), access->AccessSerialize());
+ }
+
+ void DoList(CommandSource &source, ChanServ::Channel *ci, const std::vector<Anope::string> &params)
+ {
+ const Anope::string &arg = params.size() > 2 ? params[2] : "";
+
+ if (!ci->GetAccessCount())
+ {
+ source.Reply(_("The access list of \002{0}\002 is empty."), ci->GetName());
+ return;
+ }
+
+ ListFormatter list(source.GetAccount());
+
+ list.AddColumn(_("Number")).AddColumn(_("Mask")).AddColumn(_("Flags")).AddColumn(_("Creator")).AddColumn(_("Created"));
+
+ unsigned count = 0;
+ for (unsigned i = 0, end = ci->GetAccessCount(); i < end; ++i)
+ {
+ ChanServ::ChanAccess *access = ci->GetAccess(i);
+ const Anope::string &flags = FlagsChanAccessImpl::DetermineFlags(access);
+
+ if (!arg.empty())
+ {
+ if (arg[0] == '+')
+ {
+ bool pass = true;
+ for (size_t j = 1; j < arg.length(); ++j)
+ if (flags.find(arg[j]) == Anope::string::npos)
+ pass = false;
+ if (pass == false)
+ continue;
+ }
+ else if (!Anope::Match(access->Mask(), arg))
+ continue;
+ }
+
+ ListFormatter::ListEntry entry;
+ ++count;
+ entry["Number"] = stringify(i + 1);
+ entry["Mask"] = access->Mask();
+ entry["Flags"] = flags;
+ entry["Creator"] = access->GetCreator();
+ entry["Created"] = Anope::strftime(access->GetCreated(), source.nc, true);
+ list.AddEntry(entry);
+ }
+
+ if (list.IsEmpty())
+ source.Reply(_("No matching entries on the access list of \002{0}\002."), ci->GetName());
+ else
+ {
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+ source.Reply(_("Flags list for \002{0}\002:"), ci->GetName());
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+ if (count == ci->GetAccessCount())
+ source.Reply(_("End of access list."));
+ else
+ source.Reply(_("End of access list - \002{0}/{1}\002 entries shown."), count, ci->GetAccessCount());
+ }
+ }
+
+ void DoClear(CommandSource &source, ChanServ::Channel *ci)
+ {
+ if (!source.IsFounder(ci) && !source.HasPriv("chanserv/access/modify"))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "FOUNDER", ci->GetName());
+ return;
+ }
+
+ ci->ClearAccess();
+
+ EventManager::Get()->Dispatch(&Event::AccessClear::OnAccessClear, ci, source);
+
+ source.Reply(_("The access list of \002{0}\002 has been cleared."), ci->GetName());
+
+ bool override = !source.IsFounder(ci);
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to clear the access list";
+ }
+
+ public:
+ CommandCSFlags(Module *creator) : Command(creator, "chanserv/flags", 1, 4)
+ {
+ this->SetDesc(_("Modify the list of privileged users"));
+ this->SetSyntax(_("\037channel\037 [MODIFY] \037mask\037 \037changes\037"));
+ this->SetSyntax(_("\037channel\037 LIST [\037mask\037 | +\037flags\037]"));
+ this->SetSyntax(_("\037channel\037 CLEAR"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &chan = params[0];
+ const Anope::string &cmd = params.size() > 1 ? params[1] : "";
+
+ ChanServ::Channel *ci = ChanServ::Find(chan);
+ if (ci == NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
+ return;
+ }
+
+ bool is_list = cmd.empty() || cmd.equals_ci("LIST");
+ bool has_access = false;
+ if (source.HasPriv("chanserv/access/modify"))
+ has_access = true;
+ else if (is_list && source.HasPriv("chanserv/access/list"))
+ has_access = true;
+ else if (is_list && source.AccessFor(ci).HasPriv("ACCESS_LIST"))
+ has_access = true;
+ else if (source.AccessFor(ci).HasPriv("ACCESS_CHANGE"))
+ has_access = true;
+
+ if (!has_access)
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), is_list ? "ACCESS_LIST" : "ACCESS_CHANGE", ci->GetName());
+ return;
+ }
+
+ if (Anope::ReadOnly && !is_list)
+ {
+ source.Reply(_("Sorry, channel access list modification is temporarily disabled."));
+ return;
+ }
+
+ if (is_list)
+ this->DoList(source, ci, params);
+ else if (cmd.equals_ci("CLEAR"))
+ this->DoClear(source, ci);
+ else
+ {
+ Anope::string mask, flags;
+ if (cmd.equals_ci("MODIFY"))
+ {
+ mask = params.size() > 2 ? params[2] : "";
+ flags = params.size() > 3 ? params[3] : "";
+ }
+ else
+ {
+ mask = cmd;
+ flags = params.size() > 2 ? params[2] : "";
+ }
+
+ this->DoModify(source, ci, mask, flags);
+ }
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("{0} allow granularly granting privileges on channels to users by assigning them flags, which map to one or more privileges.\n"
+ "\n"
+ "The \002MODIFY\002 command allows you to modify the access list."
+ " If \037mask\037 is not already on the access list is it added, then the \037changes\037 are applied."
+ " If the mask has no more flags, then the mask is removed from the access list."
+ " Additionally, you may use +* or -* to add or remove all flags, respectively."
+ " You are only able to modify the access list if you have the \002{1}\002 privilege on the channel, and can only give privileges to up what you have."),
+ source.command, "ACCESS_CHANGE");
+ source.Reply(" ");
+ source.Reply(_("The \002LIST\002 command allows you to list existing entries on the channel access list."
+ " If \037mask\037 is given, the \037mask\037 is wildcard matched against all existing entries on the access list, and only those entries are returned."
+ " If a set of flags is given, only those on the access list with the specified flags are returned."));
+ source.Reply(" ");
+ source.Reply(_("The \002CLEAR\002 command clears the channel access list, which requires being the founder of \037channel\037."));
+ source.Reply(" ");
+ source.Reply(_("\n"
+ "Examples:\n"
+ " {command} #anope MODIFY Attila +fHhu-i\n"
+ " Modifies the flags of \"Attila\" on the access list of \"#anope\" to \"+fHhu-i\".\n"
+ "\n"
+ " {command} #anope MODIFY *!*@anope.org +*\n"
+ " Modifies the flags of the host mask \"*!*@anope.org\" on the access list of \"#anope\" to have all flags."));
+ source.Reply(" ");
+ source.Reply(_("The available flags are:"));
+
+ typedef std::multimap<char, Anope::string, ci::less> reverse_map;
+ reverse_map reverse;
+ for (std::map<Anope::string, char>::iterator it = defaultFlags.begin(), it_end = defaultFlags.end(); it != it_end; ++it)
+ reverse.insert(std::make_pair(it->second, it->first));
+
+ for (reverse_map::iterator it = reverse.begin(), it_end = reverse.end(); it != it_end; ++it)
+ {
+ ChanServ::Privilege *p = ChanServ::service ? ChanServ::service->FindPrivilege(it->second) : nullptr;
+ if (p == NULL)
+ continue;
+ source.Reply(" %c - %s", it->first, Language::Translate(source.nc, p->desc.c_str()));
+ }
+
+ return true;
+ }
+};
+
+class CSFlags : public Module
+{
+ CommandCSFlags commandcsflags;
+ FlagsChanAccessType flagsaccesstype;
+
+ public:
+ CSFlags(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandcsflags(this)
+ , flagsaccesstype(this)
+ {
+ this->SetPermanent(true);
+
+ }
+
+ void OnReload(Configuration::Conf *conf) override
+ {
+ defaultFlags.clear();
+
+ for (int i = 0; i < conf->CountBlock("privilege"); ++i)
+ {
+ Configuration::Block *priv = conf->GetBlock("privilege", i);
+
+ const Anope::string &pname = priv->Get<Anope::string>("name");
+
+ ChanServ::Privilege *p = ChanServ::service ? ChanServ::service->FindPrivilege(pname) : nullptr;
+ if (p == NULL)
+ continue;
+
+ const Anope::string &value = priv->Get<Anope::string>("flag");
+ if (value.empty())
+ continue;
+
+ defaultFlags[p->name] = value[0];
+ }
+ }
+};
+
+template<> void ModuleInfo<CSFlags>(ModuleDef *def)
+{
+ def->Depends("chanserv");
+}
+
+MODULE_INIT(CSFlags)
diff --git a/modules/chanserv/getkey.cpp b/modules/chanserv/getkey.cpp
new file mode 100644
index 000000000..cbcc66720
--- /dev/null
+++ b/modules/chanserv/getkey.cpp
@@ -0,0 +1,83 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+
+class CommandCSGetKey : public Command
+{
+ public:
+ CommandCSGetKey(Module *creator) : Command(creator, "chanserv/getkey", 1, 1)
+ {
+ this->SetDesc(_("Returns the key of the given channel"));
+ this->SetSyntax(_("\037channel\037"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &chan = params[0];
+
+ ChanServ::Channel *ci = ChanServ::Find(chan);
+ if (ci == NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
+ return;
+ }
+
+ if (!source.AccessFor(ci).HasPriv("GETKEY") && !source.HasCommand("chanserv/getkey"))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "GETKEY", ci->GetName());
+ return;
+ }
+
+ Anope::string key;
+ if (!ci->c || !ci->c->GetParam("KEY", key))
+ {
+ source.Reply(_("Channel \002{0}\002 does not have a key."), ci->GetName());
+ return;
+ }
+
+ bool override = !source.AccessFor(ci).HasPriv("GETKEY");
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci);
+
+ source.Reply(_("Key for channel \002{0}\002 is \002{1}\002."), ci->GetName(), key);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Returns the key of \037channel\037.\n"
+ "\n"
+ "Use of this command requires the \002{0}\002 privilege on \037channel\037."),
+ "GETKEY");
+ return true;
+ }
+};
+
+class CSGetKey : public Module
+{
+ CommandCSGetKey commandcsgetkey;
+
+ public:
+ CSGetKey(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandcsgetkey(this)
+ {
+
+ }
+};
+
+MODULE_INIT(CSGetKey)
diff --git a/modules/chanserv/info.cpp b/modules/chanserv/info.cpp
new file mode 100644
index 000000000..43807fb4b
--- /dev/null
+++ b/modules/chanserv/info.cpp
@@ -0,0 +1,101 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/chanserv/info.h"
+
+class CommandCSInfo : public Command
+{
+ public:
+ CommandCSInfo(Module *creator) : Command(creator, "chanserv/info", 1, 2)
+ {
+ this->SetDesc(_("Lists information about the specified registered channel"));
+ this->SetSyntax(_("\037channel\037"));
+ this->AllowUnregistered(true);
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &chan = params[0];
+
+ NickServ::Account *nc = source.nc;
+ ChanServ::Channel *ci = ChanServ::Find(chan);
+ if (ci == NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
+ return;
+ }
+
+ bool has_auspex = source.HasPriv("chanserv/auspex");
+ bool show_all = false;
+
+ /* Should we show all fields? Only for sadmins and identified users */
+ if (source.AccessFor(ci).HasPriv("INFO") || has_auspex)
+ show_all = true;
+
+ InfoFormatter info(nc);
+
+ source.Reply(_("Information for channel \002{0}\002:"), ci->GetName());
+ if (ci->GetFounder())
+ info[_("Founder")] = ci->GetFounder()->GetDisplay();
+
+ if (show_all && ci->GetSuccessor())
+ info[_("Successor")] = ci->GetSuccessor()->GetDisplay();
+
+ if (!ci->GetDesc().empty())
+ info[_("Description")] = ci->GetDesc();
+
+ info[_("Registered")] = Anope::strftime(ci->GetTimeRegistered(), source.GetAccount());
+ info[_("Last used")] = Anope::strftime(ci->GetLastUsed(), source.GetAccount());
+
+ if (show_all)
+ {
+ info[_("Ban type")] = stringify(ci->GetBanType());
+ }
+
+ EventManager::Get()->Dispatch(&Event::ChanInfo::OnChanInfo, source, ci, info, show_all);
+
+ std::vector<Anope::string> replies;
+ info.Process(replies);
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Shows information about \037channel\037, including its founder, description, time of registration, and last time used."
+ " If the user issuing the command has \002{0}\002 privilege on \037channel\037, then the successor, last topic set, settings and expiration time will also be displayed when applicable."));
+ return true;
+ }
+};
+
+class CSInfo : public Module
+{
+ CommandCSInfo commandcsinfo;
+
+ public:
+ CSInfo(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandcsinfo(this)
+ {
+
+ }
+};
+
+MODULE_INIT(CSInfo)
diff --git a/modules/chanserv/invite.cpp b/modules/chanserv/invite.cpp
new file mode 100644
index 000000000..b6d0f19c9
--- /dev/null
+++ b/modules/chanserv/invite.cpp
@@ -0,0 +1,117 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+
+class CommandCSInvite : public Command
+{
+ public:
+ CommandCSInvite(Module *creator) : Command(creator, "chanserv/invite", 1, 3)
+ {
+ this->SetDesc(_("Invites you or an optionally specified nick into a channel"));
+ this->SetSyntax(_("\037channel\037 [\037nick\037]"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &chan = params[0];
+
+ User *u = source.GetUser();
+ Channel *c = Channel::Find(chan);
+
+ if (!c)
+ {
+ source.Reply(_("Channel \002{0}\002 doesn't exist."), chan);
+ return;
+ }
+
+ ChanServ::Channel *ci = c->ci;
+ if (!ci)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), c->name);
+ return;
+ }
+
+ if (!source.AccessFor(ci).HasPriv("INVITE") && !source.HasCommand("chanserv/invite"))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "INVITE", ci->GetName());
+ return;
+ }
+
+ User *u2;
+ if (params.size() == 1)
+ u2 = u;
+ else
+ u2 = User::Find(params[1], true);
+
+ if (!u2)
+ {
+ source.Reply(_("\002{0}\002 isn't currently online."), params.size() > 1 ? params[1] : source.GetNick());
+ return;
+ }
+
+ if (c->FindUser(u2))
+ {
+ if (u2 == u)
+ source.Reply(_("You are already in \002{0}\002!"), c->name);
+ else
+ source.Reply(_("\002{0}\002 is already in \002{1}\002!"), u2->nick, c->name);
+
+ return;
+ }
+
+ bool override = !source.AccessFor(ci).HasPriv("INVITE");
+
+ IRCD->SendInvite(ci->WhoSends(), c, u2);
+ if (u2 != u)
+ {
+ source.Reply(_("\002{0}\002 has been invited to \002{1}\002."), u2->nick, c->name);
+ u2->SendMessage(ci->WhoSends(), _("You have been invited to \002{0}\002 by \002{1}\002."), c->name, source.GetNick());
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "for " << u2->nick;
+ }
+ else
+ {
+ u2->SendMessage(ci->WhoSends(), _("You have been invited to \002{0}\002."), c->name);
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci);
+ }
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Causes \002{0}\002 to invite you or an optionally specified \037nick\037 to \037channel\037.\n"
+ "\n"
+ "Use of this command requires the \002{0}\002 privilege on \037channel\037."),
+ source.service->nick, "INVITE");
+ return true;
+ }
+};
+
+class CSInvite : public Module
+{
+ CommandCSInvite commandcsinvite;
+
+ public:
+ CSInvite(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandcsinvite(this)
+ {
+
+ }
+};
+
+MODULE_INIT(CSInvite)
diff --git a/modules/chanserv/kick.cpp b/modules/chanserv/kick.cpp
new file mode 100644
index 000000000..b78357d3e
--- /dev/null
+++ b/modules/chanserv/kick.cpp
@@ -0,0 +1,160 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+
+class CommandCSKick : public Command
+{
+ public:
+ CommandCSKick(Module *creator) : Command(creator, "chanserv/kick", 2, 3)
+ {
+ this->SetDesc(_("Kicks a specified nick from a channel"));
+ this->SetSyntax(_("\037channel\037 \037user\037 [\037reason\037]"));
+ this->SetSyntax(_("\037channel\037 \037mask\037 [\037reason\037]"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &chan = params[0];
+ const Anope::string &target = params[1];
+ Anope::string reason = params.size() > 2 ? params[2] : "Requested";
+
+ User *u = source.GetUser();
+ ChanServ::Channel *ci = ChanServ::Find(params[0]);
+ Channel *c = Channel::Find(params[0]);
+ User *u2 = User::Find(target, true);
+
+ if (!c)
+ {
+ source.Reply(_("Channel \002{0}\002 doesn't exist."), chan);
+ return;
+ }
+
+ if (!ci)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), c->name);
+ return;
+ }
+
+ unsigned reasonmax = Config->GetModule("chanserv")->Get<unsigned>("reasonmax", "200");
+ if (reason.length() > reasonmax)
+ reason = reason.substr(0, reasonmax);
+
+ ChanServ::AccessGroup u_access = source.AccessFor(ci);
+
+ Anope::string signkickformat = Config->GetModule("chanserv")->Get<Anope::string>("signkickformat", "%m (%n)");
+ signkickformat = signkickformat.replace_all_cs("%n", source.GetNick());
+
+ if (!u_access.HasPriv("KICK") && !source.HasPriv("chanserv/kick"))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "KICK", ci->GetName());
+ return;
+ }
+
+ if (u2)
+ {
+ ChanServ::AccessGroup u2_access = ci->AccessFor(u2);
+ if (u != u2 && ci->HasFieldS("PEACE") && u2_access >= u_access && !source.HasPriv("chanserv/kick"))
+ source.Reply(_("Access denied. \002{0}\002 has the same or more privileges than you on \002{1}\002."), u2->nick, ci->GetName());
+ else if (u2->IsProtected())
+ source.Reply(_("Access denied. \002{0}\002 is protected and can not be kicked."), u2->nick);
+ else if (!c->FindUser(u2))
+ source.Reply(_("User \002{0}\002 is not on channel \002{1}\002."), u2->nick, c->name);
+ else
+ {
+ bool override = !u_access.HasPriv("KICK") || (u != u2 && ci->HasFieldS("PEACE") && u2_access >= u_access);
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "for " << u2->nick;
+
+ if (ci->HasFieldS("SIGNKICK") || (ci->HasFieldS("SIGNKICK_LEVEL") && !u_access.HasPriv("SIGNKICK")))
+ {
+ signkickformat = signkickformat.replace_all_cs("%m", reason);
+ c->Kick(ci->WhoSends(), u2, "%s", signkickformat.c_str());
+ }
+ else
+ c->Kick(ci->WhoSends(), u2, "%s", reason.c_str());
+ }
+ }
+ else if (u_access.HasPriv("FOUNDER"))
+ {
+ Anope::string mask = IRCD->NormalizeMask(target);
+
+ Log(LOG_COMMAND, source, this, ci) << "for " << mask;
+
+ int matched = 0, kicked = 0;
+ for (Channel::ChanUserList::iterator it = c->users.begin(), it_end = c->users.end(); it != it_end;)
+ {
+ ChanUserContainer *uc = it->second;
+ ++it;
+
+ Entry e("", mask);
+ if (e.Matches(uc->user))
+ {
+ ++matched;
+
+ ChanServ::AccessGroup u2_access = ci->AccessFor(uc->user);
+ if (u != uc->user && ci->HasFieldS("PEACE") && u2_access >= u_access)
+ continue;
+ else if (uc->user->IsProtected())
+ continue;
+
+ ++kicked;
+
+ if (ci->HasFieldS("SIGNKICK") || (ci->HasFieldS("SIGNKICK_LEVEL") && !u_access.HasPriv("SIGNKICK")))
+ {
+ reason += " (Matches " + mask + ")";
+ signkickformat = signkickformat.replace_all_cs("%m", reason);
+ c->Kick(ci->WhoSends(), uc->user, "%s", signkickformat.c_str());
+ }
+ else
+ c->Kick(ci->WhoSends(), uc->user, "%s (Matches %s)", reason.c_str(), mask.c_str());
+ }
+ }
+
+ if (matched)
+ source.Reply(_("Kicked \002{0}/{1}\002 users matching \002{2}\002 from \002{3}\002."), kicked, matched, mask, c->name);
+ else
+ source.Reply(_("No users on\002{0}\002 match \002{1}\002."), c->name, mask);
+ }
+ else
+ source.Reply(_("\002{0}\002 isn't currently in use."), target);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Kicks \037user\037 from a \037channel\037. Users with the \002{0}\002 privilege on \037channel\037 may give a \037mask\037, which kicks all users who match the wildcard mask.\n"
+ " \n"
+ "Use of this command requires the \002{1}\002 privilege on \037channel\037."),
+ "FOUNDER", "KICK");
+ return true;
+ }
+};
+
+class CSKick : public Module
+{
+ CommandCSKick commandcskick;
+
+ public:
+ CSKick(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandcskick(this)
+ {
+
+ }
+};
+
+MODULE_INIT(CSKick)
diff --git a/modules/chanserv/list.cpp b/modules/chanserv/list.cpp
new file mode 100644
index 000000000..75ffdb67c
--- /dev/null
+++ b/modules/chanserv/list.cpp
@@ -0,0 +1,274 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/chanserv/info.h"
+#include "modules/chanserv/set.h"
+#include "modules/chanserv/mode.h"
+
+class CommandCSList : public Command
+{
+ ServiceReference<ModeLocks> mlocks;
+
+ public:
+ CommandCSList(Module *creator) : Command(creator, "chanserv/list", 1, 2)
+ {
+ this->SetDesc(_("Lists all registered channels matching the given pattern"));
+ this->SetSyntax(_("\037pattern\037 [SUSPENDED] [NOEXPIRE]"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ Anope::string pattern = params[0];
+ unsigned nchans;
+ bool is_servadmin = source.HasCommand("chanserv/list");
+ int count = 0, from = 0, to = 0;
+ bool suspended = false, channoexpire = false;
+
+ if (pattern[0] == '#')
+ {
+ Anope::string n1, n2;
+ sepstream(pattern.substr(1), '-').GetToken(n1, 0);
+ sepstream(pattern, '-').GetToken(n2, 1);
+
+ try
+ {
+ from = convertTo<int>(n1);
+ to = convertTo<int>(n2);
+ }
+ catch (const ConvertException &)
+ {
+ source.Reply(_("Incorrect range specified. The correct syntax is \002#\037from\037-\037to\037\002."));
+ source.Reply(_("To search for channels starting with #, search for the channel name without the #-sign prepended (\002anope\002 instead of \002#anope\002)."));
+ return;
+ }
+
+ pattern = "*";
+ }
+
+ nchans = 0;
+
+ if (is_servadmin && params.size() > 1)
+ {
+ Anope::string keyword;
+ spacesepstream keywords(params[1]);
+ while (keywords.GetToken(keyword))
+ {
+ if (keyword.equals_ci("SUSPENDED"))
+ suspended = true;
+ if (keyword.equals_ci("NOEXPIRE"))
+ channoexpire = true;
+ }
+ }
+
+ Anope::string spattern = "#" + pattern;
+ unsigned listmax = Config->GetModule(this->GetOwner())->Get<unsigned>("listmax", "50");
+
+ source.Reply(_("List of entries matching \002{0}\002:"), pattern);
+
+ ListFormatter list(source.GetAccount());
+ list.AddColumn(_("Name")).AddColumn(_("Description"));
+
+ // XXX wtf
+ Anope::map<ChanServ::Channel *> ordered_map;
+ if (ChanServ::service)
+ for (auto& it : ChanServ::service->GetChannels())
+ ordered_map[it.first] = it.second;
+
+ for (Anope::map<ChanServ::Channel *>::const_iterator it = ordered_map.begin(), it_end = ordered_map.end(); it != it_end; ++it)
+ {
+ ChanServ::Channel *ci = it->second;
+
+ if (!is_servadmin)
+ {
+ if (ci->HasFieldS("CS_PRIVATE") || ci->HasFieldS("CS_SUSPENDED"))
+ continue;
+ if (ci->c && ci->c->HasMode("SECRET"))
+ continue;
+
+ if (mlocks)
+ {
+ ModeLock *secret = mlocks->GetMLock(ci, "SECRET");
+ if (secret && secret->GetSet())
+ continue;
+ }
+ }
+
+ if (suspended && !ci->HasFieldS("CS_SUSPENDED"))
+ continue;
+
+ if (channoexpire && !ci->HasFieldS("CS_NO_EXPIRE"))
+ continue;
+
+ if (pattern.equals_ci(ci->GetName()) || ci->GetName().equals_ci(spattern) || Anope::Match(ci->GetName(), pattern, false, true) || Anope::Match(ci->GetName(), spattern, false, true) || Anope::Match(ci->GetDesc(), pattern, false, true) || Anope::Match(ci->GetLastTopic(), pattern, false, true))
+ {
+ if (((count + 1 >= from && count + 1 <= to) || (!from && !to)) && ++nchans <= listmax)
+ {
+ bool isnoexpire = false;
+ if (is_servadmin && (ci->HasFieldS("CS_NO_EXPIRE")))
+ isnoexpire = true;
+
+ ListFormatter::ListEntry entry;
+ entry["Name"] = (isnoexpire ? "!" : "") + ci->GetName();
+ if (ci->HasFieldS("CS_SUSPENDED"))
+ entry["Description"] = Language::Translate(source.GetAccount(), _("[Suspended]"));
+ else
+ entry["Description"] = ci->GetDesc();
+ list.AddEntry(entry);
+ }
+ ++count;
+ }
+ }
+
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+
+ source.Reply(_("End of list - \002{0}/{1}\002 matches shown."), nchans > listmax ? listmax : nchans, nchans);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Lists all registered channels matching the given pattern."
+ " Channels with the \002PRIVATE\002 option set will only be displayed to Services Operators with the proper access."
+ " Channels with the \002NOEXPIRE\002 option set will have a \002!\002 prefixed to the channel for Services Operators to see."
+ "\n"
+ "Note that a preceding '#' specifies a range, channel names are to be written without '#'.\n"
+ "\n"
+ "If the SUSPENDED or NOEXPIRE options are given, only channels which, respectively, are SUSPENDED or have the NOEXPIRE flag set will be displayed."
+ " If multiple options are given, all channels matching at least one option will be displayed."
+ " Note that these options are limited to \037Services Operators\037.\n"
+ "\n"
+ "Examples:\n"
+ "\n"
+ " {0} *anope*\n"
+ " Lists all registered channels with \002anope\002 in their names (case insensitive).\n"
+ "\n"
+ " {0} * NOEXPIRE\n"
+ " Lists all registered channels which have been set to not expire.\n"
+ "\n"
+ " {0} #51-100\n"
+ " Lists all registered channels within the given range (51-100)."));
+
+ if (!Config->GetBlock("options")->Get<Anope::string>("regexengine").empty())
+ {
+ source.Reply(" ");
+ source.Reply(_("Regex matches are also supported using the {0} engine. Enclose your pattern in // if this is desired."), Config->GetBlock("options")->Get<Anope::string>("regexengine"));
+ }
+
+ return true;
+ }
+};
+
+class CommandCSSetPrivate : public Command
+{
+ public:
+ CommandCSSetPrivate(Module *creator, const Anope::string &cname = "chanserv/set/private") : Command(creator, cname, 2, 2)
+ {
+ this->SetDesc(_("Hide channel from the LIST command"));
+ this->SetSyntax(_("\037channel\037 {ON | OFF}"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &chan = params[0];
+
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ ChanServ::Channel *ci = ChanServ::Find(chan);
+ if (ci == NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
+ return;
+ }
+
+ EventReturn MOD_RESULT = EventManager::Get()->Dispatch(&Event::SetChannelOption::OnSetChannelOption, source, this, ci, params[1]);
+ if (MOD_RESULT == EVENT_STOP)
+ return;
+
+ if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "SET", ci->GetName());
+ return;
+ }
+
+ if (params[1].equals_ci("ON"))
+ {
+ Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable private";
+ ci->SetS<bool>("CS_PRIVATE", true);
+ source.Reply(_("Private option for \002{0}\002 is now \002on\002."), ci->GetName());
+ }
+ else if (params[1].equals_ci("OFF"))
+ {
+ Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable private";
+ ci->UnsetS<bool>("CS_PRIVATE");
+ source.Reply(_("Private option for \002{0}\002 is now \002off\002."), ci->GetName());
+ }
+ else
+ this->OnSyntaxError(source, "PRIVATE");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
+ {
+ this->SendSyntax(source);
+ source.Reply(" ");
+ source.Reply(_("Enables or disables the \002private\002 option for a channel."));
+
+ ServiceBot *bi;
+ Anope::string cmd;
+ if (Command::FindCommandFromService("chanserv/list", bi, cmd))
+ source.Reply(_("When \002private\002 is set, the channel will not appear in the \002{0}\002 command of \002{1}\002."), cmd, bi->nick);
+ return true;
+ }
+};
+
+class CSList : public Module
+ , public EventHook<Event::ChanInfo>
+{
+ CommandCSList commandcslist;
+ CommandCSSetPrivate commandcssetprivate;
+
+ Serialize::Field<ChanServ::Channel, bool> priv;
+
+ public:
+ CSList(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , EventHook<Event::ChanInfo>(this)
+ , commandcslist(this)
+ , commandcssetprivate(this)
+ , priv(this, "CS_PRIVATE")
+ {
+ }
+
+ void OnChanInfo(CommandSource &source, ChanServ::Channel *ci, InfoFormatter &info, bool show_all) override
+ {
+ if (!show_all)
+ return;
+
+ if (priv.HasExt(ci))
+ info.AddOption(_("Private"));
+ }
+};
+
+MODULE_INIT(CSList)
diff --git a/modules/chanserv/log.cpp b/modules/chanserv/log.cpp
new file mode 100644
index 000000000..b20427bc0
--- /dev/null
+++ b/modules/chanserv/log.cpp
@@ -0,0 +1,466 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2011-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/chanserv/log.h"
+#include "modules/memoserv.h"
+
+class LogSettingImpl : public LogSetting
+{
+ friend class LogSettingType;
+
+ ChanServ::Channel *channel = nullptr;
+ Anope::string service_name, command_service, command_name, method, extra, creator;
+ time_t created = 0;
+
+ public:
+ LogSettingImpl(Serialize::TypeBase *type) : LogSetting(type) { }
+ LogSettingImpl(Serialize::TypeBase *type, Serialize::ID id) : LogSetting(type, id) { }
+
+ ChanServ::Channel *GetChannel() override;
+ void SetChannel(ChanServ::Channel *) override;
+
+ Anope::string GetServiceName() override;
+ void SetServiceName(const Anope::string &) override;
+
+ Anope::string GetCommandService() override;
+ void SetCommandService(const Anope::string &) override;
+
+ Anope::string GetCommandName() override;
+ void SetCommandName(const Anope::string &) override;
+
+ Anope::string GetMethod() override;
+ virtual void SetMethod(const Anope::string &) override;
+
+ Anope::string GetExtra() override;
+ void SetExtra(const Anope::string &) override;
+
+ Anope::string GetCreator() override;
+ void SetCreator(const Anope::string &) override;
+
+ time_t GetCreated() override;
+ void SetCreated(const time_t &) override;
+};
+
+class LogSettingType : public Serialize::Type<LogSettingImpl>
+{
+ public:
+ Serialize::ObjectField<LogSettingImpl, ChanServ::Channel *> ci;
+ Serialize::Field<LogSettingImpl, Anope::string> service_name, command_service, command_name, method, extra, creator;
+ Serialize::Field<LogSettingImpl, time_t> created;
+
+ LogSettingType(Module *me) : Serialize::Type<LogSettingImpl>(me)
+ , ci(this, "ci", &LogSettingImpl::channel, true)
+ , service_name(this, "service_name", &LogSettingImpl::service_name)
+ , command_service(this, "command_service", &LogSettingImpl::command_service)
+ , command_name(this, "command_name", &LogSettingImpl::command_name)
+ , method(this, "method", &LogSettingImpl::method)
+ , extra(this, "extra", &LogSettingImpl::extra)
+ , creator(this, "creator", &LogSettingImpl::creator)
+ , created(this, "created", &LogSettingImpl::created)
+ {
+ }
+};
+
+ChanServ::Channel *LogSettingImpl::GetChannel()
+{
+ return Get(&LogSettingType::ci);
+}
+
+void LogSettingImpl::SetChannel(ChanServ::Channel *ci)
+{
+ Set(&LogSettingType::ci, ci);
+}
+
+Anope::string LogSettingImpl::GetServiceName()
+{
+ return Get(&LogSettingType::service_name);
+}
+
+void LogSettingImpl::SetServiceName(const Anope::string &s)
+{
+ Set(&LogSettingType::service_name, s);
+}
+
+Anope::string LogSettingImpl::GetCommandService()
+{
+ return Get(&LogSettingType::command_service);
+}
+
+void LogSettingImpl::SetCommandService(const Anope::string &s)
+{
+ Set(&LogSettingType::command_service, s);
+}
+
+Anope::string LogSettingImpl::GetCommandName()
+{
+ return Get(&LogSettingType::command_name);
+}
+
+void LogSettingImpl::SetCommandName(const Anope::string &s)
+{
+ Set(&LogSettingType::command_name, s);
+}
+
+Anope::string LogSettingImpl::GetMethod()
+{
+ return Get(&LogSettingType::method);
+}
+
+void LogSettingImpl::SetMethod(const Anope::string &m)
+{
+ Set(&LogSettingType::method, m);
+}
+
+Anope::string LogSettingImpl::GetExtra()
+{
+ return Get(&LogSettingType::extra);
+}
+
+void LogSettingImpl::SetExtra(const Anope::string &e)
+{
+ Set(&LogSettingType::extra, e);
+}
+
+Anope::string LogSettingImpl::GetCreator()
+{
+ return Get(&LogSettingType::creator);
+}
+
+void LogSettingImpl::SetCreator(const Anope::string &creator)
+{
+ Set(&LogSettingType::extra, creator);
+}
+
+time_t LogSettingImpl::GetCreated()
+{
+ return Get(&LogSettingType::created);
+}
+
+void LogSettingImpl::SetCreated(const time_t &t)
+{
+ Set(&LogSettingType::created, t);
+}
+
+class CommandCSLog : public Command
+{
+public:
+ CommandCSLog(Module *creator) : Command(creator, "chanserv/log", 1, 4)
+ {
+ this->SetDesc(_("Configures channel logging settings"));
+ this->SetSyntax(_("\037channel\037"));
+ this->SetSyntax(_("\037channel\037 \037command\037 \037method\037 [\037status\037]"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &channel = params[0];
+
+ ChanServ::Channel *ci = ChanServ::Find(channel);
+ if (ci == NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), channel);
+ return;
+ }
+
+ if (!source.AccessFor(ci).HasPriv("SET") && !source.HasPriv("chanserv/administration"))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "SET", ci->GetName());
+ return;
+ }
+
+ if (params.size() == 1)
+ {
+ std::vector<LogSetting *> ls = ci->GetRefs<LogSetting *>();
+ if (ls.empty())
+ {
+ source.Reply(_("There currently are no logging configurations for \002{0}\002."), ci->GetName());
+ return;
+ }
+
+ ListFormatter list(source.GetAccount());
+ list.AddColumn(_("Number")).AddColumn(_("Service")).AddColumn(_("Command")).AddColumn(_("Method")).AddColumn("");
+
+ for (unsigned i = 0; i < ls.size(); ++i)
+ {
+ LogSetting *log = ls[i];
+
+ ListFormatter::ListEntry entry;
+ entry["Number"] = stringify(i + 1);
+ entry["Service"] = log->GetCommandService();
+ entry["Command"] = !log->GetCommandName().empty() ? log->GetCommandName() : log->GetServiceName();
+ entry["Method"] = log->GetMethod();
+ entry[""] = log->GetExtra();
+ list.AddEntry(entry);
+ }
+
+ source.Reply(_("Log list for \002{0}\002:"), ci->GetName());
+
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+ }
+ else if (params.size() > 2)
+ {
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ const Anope::string &command = params[1];
+ const Anope::string &method = params[2];
+ const Anope::string &extra = params.size() > 3 ? params[3] : "";
+
+ size_t sl = command.find('/');
+ if (sl == Anope::string::npos)
+ {
+ source.Reply(_("\002{0}\002 is not a valid command."), command);
+ return;
+ }
+
+ Anope::string service = command.substr(0, sl),
+ command_name = command.substr(sl + 1);
+ ServiceBot *bi = ServiceBot::Find(service, true);
+
+ Anope::string service_name;
+
+ /* Allow either a command name or a service name. */
+ if (bi && bi->commands.count(command_name))
+ {
+ /* Get service name from command */
+ service_name = bi->commands[command_name].name;
+ }
+ else if (ServiceReference<Command>(command.lower()))
+ {
+ /* This is the service name, don't use any specific command */
+ service_name = command;
+ bi = NULL;
+ command_name.clear();
+ }
+ else
+ {
+ source.Reply(_("\002{0}\002 is not a valid command."), command);
+ return;
+ }
+
+ if (!method.equals_ci("MESSAGE") && !method.equals_ci("NOTICE") && !method.equals_ci("MEMO"))
+ {
+ source.Reply(_("\002%s\002 is not a valid logging method."));
+ return;
+ }
+
+ for (unsigned i = 0; i < extra.length(); ++i)
+ if (ModeManager::GetStatusChar(extra[i]) == 0)
+ {
+ source.Reply(_("\002%c\002 is an unknown status mode."), extra[i]);
+ return;
+ }
+
+ bool override = !source.AccessFor(ci).HasPriv("SET");
+
+ std::vector<LogSetting *> ls = ci->GetRefs<LogSetting *>();
+ for (unsigned i = ls.size(); i > 0; --i)
+ {
+ LogSetting *log = ls[i - 1];
+
+ if (log->GetServiceName() == service_name && log->GetMethod().equals_ci(method) && command_name.equals_ci(log->GetCommandName()))
+ {
+ if (log->GetExtra() == extra)
+ {
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to remove logging for " << command << " with method " << method << (extra == "" ? "" : " ") << extra;
+ source.Reply(_("Logging for command \002{0}\002 on \002{1}\002 with log method \002{2}{3}{4}\002 has been removed."), !log->GetCommandName().empty() ? log->GetCommandName() : log->GetServiceName(), !log->GetCommandService().empty() ? log->GetCommandService() : "any service", method, extra.empty() ? "" : " ", extra);
+ delete log;
+ }
+ else
+ {
+ log->SetExtra(extra);
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to change logging for " << command << " to method " << method << (extra == "" ? "" : " ") << extra;
+ source.Reply(_("Logging changed for command \002{0}\002 on \002{1}\002, now using log method \002{2}{3}{4]\002."), !log->GetCommandName().empty() ? log->GetCommandName() : log->GetServiceName(), !log->GetCommandService().empty() ? log->GetCommandService() : "any service", method, extra.empty() ? "" : " ", extra);
+ }
+ return;
+ }
+ }
+
+ LogSetting *log = Serialize::New<LogSetting *>();
+ log->SetChannel(ci);
+ log->SetServiceName(service_name);
+ if (bi)
+ log->SetCommandService(bi->nick);
+ log->SetCommandName(command_name);
+ log->SetMethod(method);
+ log->SetExtra(extra);
+ log->SetCreated(Anope::CurTime);
+ log->SetCreator(source.GetNick());
+
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to log " << command << " with method " << method << (extra == "" ? "" : " ") << extra;
+
+ source.Reply(_("Logging is now active for command \002{0}\002 on \002{1}\002, using log method \002{2}{3}{4}\002."), !command_name.empty() ? command_name : service_name, bi ? bi->nick : "any service", method, extra.empty() ? "" : " ", extra);
+ }
+ else
+ this->OnSyntaxError(source, "");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("The {0} command allows users to configure logging settings for \037channel\037."
+ " If no parameters are given this command lists the current logging methods in place on \037channel\037."
+ " Otherwise, \037command\037 must be a command name, and \037method\037 must be one of the following logging methods:\n"
+ "\n"
+ " MESSAGE [status], NOTICE [status], MEMO\n"
+ "\n"
+ "Which are used to message, notice, and memo the channel respectively."
+ " With MESSAGE or NOTICE you must have a service bot assigned to and joined to your channel."
+ " Status may be a channel status such as @ or +.\n"
+ "\n"
+ "To remove a logging method use the same syntax as you would to add it.\n"
+ "\n"
+ "Use of this command requires the \002{1}\002 privilege on \037channel\037."
+ "\n"
+ "Example:\n"
+ " {command} #anope chanserv/access MESSAGE @\n"
+ " Would message any channel operators of \"#anope\" whenever someone used the \"ACCESS\" command on ChanServ for \"#anope\"."),
+ source.command, "SET");
+ return true;
+ }
+};
+
+class CSLog : public Module
+ , public EventHook<Event::ChanRegistered>
+ , public EventHook<Event::Log>
+{
+ CommandCSLog commandcslog;
+ LogSettingType logtype;
+ ServiceReference<MemoServ::MemoServService> memoserv;
+
+ struct LogDefault
+ {
+ Anope::string service, command, method;
+ };
+
+ std::vector<LogDefault> defaults;
+
+ public:
+ CSLog(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , EventHook<Event::ChanRegistered>(this)
+ , EventHook<Event::Log>(this)
+ , commandcslog(this)
+ , logtype(this)
+ {
+
+ }
+
+ void OnReload(Configuration::Conf *conf) override
+ {
+ Configuration::Block *block = conf->GetModule(this);
+ defaults.clear();
+
+ for (int i = 0; i < block->CountBlock("default"); ++i)
+ {
+ Configuration::Block *def = block->GetBlock("default", i);
+
+ LogDefault ld;
+
+ ld.service = def->Get<Anope::string>("service");
+ ld.command = def->Get<Anope::string>("command");
+ ld.method = def->Get<Anope::string>("method");
+
+ defaults.push_back(ld);
+ }
+ }
+
+ void OnChanRegistered(ChanServ::Channel *ci) override
+ {
+ if (defaults.empty())
+ return;
+
+ for (unsigned i = 0; i < defaults.size(); ++i)
+ {
+ LogDefault &d = defaults[i];
+
+ LogSetting *log = Serialize::New<LogSetting *>();
+ log->SetChannel(ci);
+
+ if (!d.service.empty())
+ {
+ log->SetServiceName(d.service.lower() + "/" + d.command.lower());
+ log->SetCommandService(d.service);
+ log->SetCommandName(d.command);
+ }
+ else
+ log->SetServiceName(d.command);
+
+ spacesepstream sep(d.method);
+ Anope::string method, extra;
+ sep.GetToken(method);
+ extra = sep.GetRemaining();
+
+ log->SetMethod(method);
+ log->SetExtra(extra);
+ log->SetCreated(Anope::CurTime);
+ log->SetCreator(ci->GetFounder() ? ci->GetFounder()->GetDisplay() : "(default)");
+ }
+ }
+
+ void OnLog(::Log *l) override
+ {
+ if (l->type != LOG_COMMAND || l->u == NULL || l->c == NULL || l->ci == NULL || !Me || !Me->IsSynced())
+ return;
+
+ std::vector<LogSetting *> ls = l->ci->GetRefs<LogSetting *>();
+ for (unsigned i = 0; i < ls.size(); ++i)
+ {
+ LogSetting *log = ls[i];
+
+ /* wrong command */
+ if (log->GetServiceName() != l->c->GetName())
+ continue;
+
+ /* if a command name is given check the service and the command */
+ if (!log->GetCommandName().empty())
+ {
+ /* wrong service (only check if not a fantasy command, though) */
+ if (!l->source->c && log->GetCommandService() != l->source->service->nick)
+ continue;
+
+ if (!log->GetCommandName().equals_ci(l->source->command))
+ continue;
+ }
+
+ Anope::string buffer = l->u->nick + " used " + l->source->command.upper() + " " + l->buf.str();
+
+ if (log->GetMethod().equals_ci("MEMO") && memoserv && l->ci->WhoSends() != NULL)
+ memoserv->Send(l->ci->WhoSends()->nick, l->ci->GetName(), buffer, true);
+ else if (l->source->c)
+ /* Sending a channel message or notice in response to a fantasy command */;
+ else if (log->GetMethod().equals_ci("MESSAGE") && l->ci->c)
+ {
+ IRCD->SendPrivmsg(l->ci->WhoSends(), log->GetExtra() + l->ci->c->name, "%s", buffer.c_str());
+#warning "fix idletimes"
+ //l->ci->WhoSends()->lastmsg = Anope::CurTime;
+ }
+ else if (log->GetMethod().equals_ci("NOTICE") && l->ci->c)
+ IRCD->SendNotice(l->ci->WhoSends(), log->GetExtra() + l->ci->c->name, "%s", buffer.c_str());
+ }
+ }
+};
+
+MODULE_INIT(CSLog)
diff --git a/modules/chanserv/main/CMakeLists.txt b/modules/chanserv/main/CMakeLists.txt
new file mode 100644
index 000000000..781f0ef1f
--- /dev/null
+++ b/modules/chanserv/main/CMakeLists.txt
@@ -0,0 +1 @@
+build_subdir(${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/modules/chanserv/main/chanaccess.cpp b/modules/chanserv/main/chanaccess.cpp
new file mode 100644
index 000000000..bf05cdf8b
--- /dev/null
+++ b/modules/chanserv/main/chanaccess.cpp
@@ -0,0 +1,120 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2015-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/chanserv/main/chanaccess.h"
+#include "chanaccesstype.h"
+
+ChanServ::Channel *ChanAccessImpl::GetChannel()
+{
+ return Get(&ChanAccessType<ChanServ::ChanAccess>::ci);
+}
+
+void ChanAccessImpl::SetChannel(ChanServ::Channel *ci)
+{
+ Object::Set(&ChanAccessType<ChanServ::ChanAccess>::ci, ci);
+}
+
+Anope::string ChanAccessImpl::GetCreator()
+{
+ return Get(&ChanAccessType<ChanServ::ChanAccess>::creator);
+}
+
+void ChanAccessImpl::SetCreator(const Anope::string &c)
+{
+ Object::Set(&ChanAccessType<ChanServ::ChanAccess>::creator, c);
+}
+
+time_t ChanAccessImpl::GetLastSeen()
+{
+ return Get(&ChanAccessType<ChanServ::ChanAccess>::last_seen);
+}
+
+void ChanAccessImpl::SetLastSeen(const time_t &t)
+{
+ Object::Set(&ChanAccessType<ChanServ::ChanAccess>::last_seen, t);
+}
+
+time_t ChanAccessImpl::GetCreated()
+{
+ return Get(&ChanAccessType<ChanServ::ChanAccess>::created);
+}
+
+void ChanAccessImpl::SetCreated(const time_t &t)
+{
+ Object::Set(&ChanAccessType<ChanServ::ChanAccess>::created, t);
+}
+
+Anope::string ChanAccessImpl::GetMask()
+{
+ return Get(&ChanAccessType<ChanServ::ChanAccess>::mask);
+}
+
+void ChanAccessImpl::SetMask(const Anope::string &n)
+{
+ Object::Set(&ChanAccessType<ChanServ::ChanAccess>::mask, n);
+}
+
+Serialize::Object *ChanAccessImpl::GetObj()
+{
+ return Get(&ChanAccessType<ChanServ::ChanAccess>::obj);
+}
+
+void ChanAccessImpl::SetObj(Serialize::Object *o)
+{
+ Object::Set(&ChanAccessType<ChanServ::ChanAccess>::obj, o);
+}
+
+Anope::string ChanAccessImpl::Mask()
+{
+ if (NickServ::Account *acc = GetAccount())
+ return acc->GetDisplay();
+
+ return GetMask();
+}
+
+NickServ::Account *ChanAccessImpl::GetAccount()
+{
+ if (!GetObj() || GetObj()->GetSerializableType()->GetName() != NickServ::Account::NAME)
+ return nullptr;
+
+ return anope_dynamic_static_cast<NickServ::Account *>(GetObj());
+}
+
+bool ChanAccessImpl::Matches(const User *u, NickServ::Account *acc)
+{
+ if (this->GetAccount())
+ return this->GetAccount() == acc;
+
+ if (u)
+ {
+ bool is_mask = this->Mask().find_first_of("!@?*") != Anope::string::npos;
+ if (is_mask && Anope::Match(u->nick, this->Mask()))
+ return true;
+ else if (Anope::Match(u->GetDisplayedMask(), this->Mask()))
+ return true;
+ }
+
+ if (acc)
+ for (NickServ::Nick *na : acc->GetRefs<NickServ::Nick *>())
+ if (Anope::Match(na->GetNick(), this->Mask()))
+ return true;
+
+ return false;
+}
diff --git a/modules/chanserv/main/chanaccesstype.h b/modules/chanserv/main/chanaccesstype.h
new file mode 100644
index 000000000..c755a7df5
--- /dev/null
+++ b/modules/chanserv/main/chanaccesstype.h
@@ -0,0 +1,42 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2015-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+template<typename T>
+class ChanAccessType : public Serialize::Type<T>
+{
+ static_assert(std::is_base_of<ChanServ::ChanAccess, T>::value, "");
+
+ public:
+ Serialize::ObjectField<ChanServ::ChanAccess, ChanServ::Channel *> ci;
+ Serialize::Field<ChanServ::ChanAccess, Anope::string> mask;
+ Serialize::ObjectField<ChanServ::ChanAccess, Serialize::Object *> obj;
+ Serialize::Field<ChanServ::ChanAccess, Anope::string> creator;
+ Serialize::Field<ChanServ::ChanAccess, time_t> last_seen;
+ Serialize::Field<ChanServ::ChanAccess, time_t> created;
+
+ ChanAccessType(Module *me) : Serialize::Type<T>(me)
+ , ci(this, "ci", &ChanServ::ChanAccess::channel, true)
+ , mask(this, "mask", &ChanServ::ChanAccess::mask)
+ , obj(this, "obj", &ChanServ::ChanAccess::object, true)
+ , creator(this, "creator", &ChanServ::ChanAccess::creator)
+ , last_seen(this, "last_seen", &ChanServ::ChanAccess::last_seen)
+ , created(this, "created", &ChanServ::ChanAccess::created)
+ {
+ }
+};
diff --git a/modules/chanserv/main/channel.cpp b/modules/chanserv/main/channel.cpp
new file mode 100644
index 000000000..67a4c94ec
--- /dev/null
+++ b/modules/chanserv/main/channel.cpp
@@ -0,0 +1,401 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+#include "module.h"
+#include "channeltype.h"
+#include "modules/chanserv/akick.h"
+
+ChannelImpl::~ChannelImpl()
+{
+ ChanServ::registered_channel_map& map = ChanServ::service->GetChannels();
+ map.erase(this->GetName());
+}
+
+void ChannelImpl::Delete()
+{
+ EventManager::Get()->Dispatch(&Event::DelChan::OnDelChan, this);
+
+ Log(LOG_DEBUG) << "Deleting channel " << this->GetName();
+
+ if (this->c)
+ {
+ if (this->GetBot() && this->c->FindUser(this->GetBot()))
+ this->GetBot()->Part(this->c);
+
+ /* Parting the service bot can cause the channel to go away */
+
+ if (this->c)
+ {
+ if (this->c && this->c->CheckDelete())
+ this->c->QueueForDeletion();
+
+ this->c = NULL;
+ }
+ }
+
+ return Serialize::Object::Delete();
+}
+
+Anope::string ChannelImpl::GetName()
+{
+ return Get<Anope::string>(&ChannelType::name);
+}
+
+void ChannelImpl::SetName(const Anope::string &name)
+{
+ Set(&ChannelType::name, name);
+}
+
+Anope::string ChannelImpl::GetDesc()
+{
+ return Get(&ChannelType::desc);
+}
+
+void ChannelImpl::SetDesc(const Anope::string &desc)
+{
+ Set(&ChannelType::desc, desc);
+}
+
+time_t ChannelImpl::GetTimeRegistered()
+{
+ return Get(&ChannelType::time_registered);
+}
+
+void ChannelImpl::SetTimeRegistered(const time_t &t)
+{
+ Set(&ChannelType::time_registered, t);
+}
+
+time_t ChannelImpl::GetLastUsed()
+{
+ return Get(&ChannelType::last_used);
+}
+
+void ChannelImpl::SetLastUsed(const time_t &t)
+{
+ Set(&ChannelType::last_used, t);
+}
+
+Anope::string ChannelImpl::GetLastTopic()
+{
+ return Get(&ChannelType::last_topic);
+}
+
+void ChannelImpl::SetLastTopic(const Anope::string &topic)
+{
+ Set(&ChannelType::last_topic, topic);
+}
+
+Anope::string ChannelImpl::GetLastTopicSetter()
+{
+ return Get(&ChannelType::last_topic_setter);
+}
+
+void ChannelImpl::SetLastTopicSetter(const Anope::string &setter)
+{
+ Set(&ChannelType::last_topic_setter, setter);
+}
+
+time_t ChannelImpl::GetLastTopicTime()
+{
+ return Get(&ChannelType::last_topic_time);
+}
+
+void ChannelImpl::SetLastTopicTime(const time_t &t)
+{
+ Set(&ChannelType::last_topic_time, t);
+}
+
+int16_t ChannelImpl::GetBanType()
+{
+ return Get(&ChannelType::bantype);
+}
+
+void ChannelImpl::SetBanType(const int16_t &i)
+{
+ Set(&ChannelType::bantype, i);
+}
+
+time_t ChannelImpl::GetBanExpire()
+{
+ return Get(&ChannelType::banexpire);
+}
+
+void ChannelImpl::SetBanExpire(const time_t &t)
+{
+ Set(&ChannelType::banexpire, t);
+}
+
+BotInfo *ChannelImpl::GetBI()
+{
+ return Get(&ChannelType::bi);
+}
+
+void ChannelImpl::SetBI(BotInfo *bi)
+{
+ Set(&ChannelType::bi, bi);
+}
+
+ServiceBot *ChannelImpl::GetBot()
+{
+ BotInfo *bi = GetBI();
+ return bi ? bi->bot : nullptr;
+}
+
+void ChannelImpl::SetBot(ServiceBot *bi)
+{
+ SetBI(bi->bi);
+}
+
+MemoServ::MemoInfo *ChannelImpl::GetMemos()
+{
+ return GetRef<MemoServ::MemoInfo *>();
+}
+
+void ChannelImpl::SetFounder(NickServ::Account *nc)
+{
+ Set(&ChannelType::founder, nc);
+}
+
+NickServ::Account *ChannelImpl::GetFounder()
+{
+ return Get(&ChannelType::founder);
+}
+
+void ChannelImpl::SetSuccessor(NickServ::Account *nc)
+{
+ Set(&ChannelType::successor, nc);
+}
+
+NickServ::Account *ChannelImpl::GetSuccessor()
+{
+ return Get(&ChannelType::successor);
+}
+
+ChanServ::ChanAccess *ChannelImpl::GetAccess(unsigned index)
+{
+ std::vector<ChanServ::ChanAccess *> a = GetRefs<ChanServ::ChanAccess *>();
+ return a.size() > index ? a[index] : nullptr;
+}
+
+ChanServ::AccessGroup ChannelImpl::AccessFor(const User *u, bool updateLastUsed)
+{
+ ChanServ::AccessGroup group;
+
+ if (u == NULL)
+ return group;
+
+ const NickServ::Account *nc = u->Account();
+ if (nc == NULL && !this->HasFieldS("NS_SECURE") && u->IsRecognized())
+ {
+ NickServ::Nick *na = NickServ::FindNick(u->nick);
+ if (na != NULL)
+ nc = na->GetAccount();
+ }
+
+ group.super_admin = u->super_admin;
+ group.founder = IsFounder(u);
+ group.ci = this;
+ group.nc = nc;
+
+ for (unsigned i = 0, end = this->GetAccessCount(); i < end; ++i)
+ {
+ ChanServ::ChanAccess *a = this->GetAccess(i);
+ if (a->Matches(u, u->Account()))
+ group.push_back(a);
+ }
+
+ if (group.founder || !group.empty())
+ {
+ if (updateLastUsed)
+ this->SetLastUsed(Anope::CurTime);
+
+ for (unsigned i = 0; i < group.size(); ++i)
+ group[i]->SetLastSeen(Anope::CurTime);
+ }
+
+ return group;
+}
+
+ChanServ::AccessGroup ChannelImpl::AccessFor(NickServ::Account *nc, bool updateLastUsed)
+{
+ ChanServ::AccessGroup group;
+
+ group.founder = GetFounder() && GetFounder() == nc;
+ group.ci = this;
+ group.nc = nc;
+
+ for (unsigned i = 0, end = this->GetAccessCount(); i < end; ++i)
+ {
+ ChanServ::ChanAccess *a = this->GetAccess(i);
+ if (a->Matches(NULL, nc))
+ group.push_back(a);
+ }
+
+ if (group.founder || !group.empty())
+ if (updateLastUsed)
+ this->SetLastUsed(Anope::CurTime);
+
+ /* don't update access last seen here, this isn't the user requesting access */
+
+ return group;
+}
+
+unsigned ChannelImpl::GetAccessCount()
+{
+ return GetRefs<ChanServ::ChanAccess *>().size();
+}
+
+void ChannelImpl::ClearAccess()
+{
+ for (ChanServ::ChanAccess *a : GetRefs<ChanServ::ChanAccess *>())
+ a->Delete();
+}
+
+AutoKick *ChannelImpl::AddAkick(const Anope::string &user, NickServ::Account *akicknc, const Anope::string &reason, time_t t, time_t lu)
+{
+ AutoKick *ak = Serialize::New<AutoKick *>();
+ ak->SetChannel(this);
+ ak->SetAccount(akicknc);
+ ak->SetReason(reason);
+ ak->SetCreator(user);
+ ak->SetAddTime(t);
+ ak->SetLastUsed(lu);
+
+ return ak;
+}
+
+AutoKick *ChannelImpl::AddAkick(const Anope::string &user, const Anope::string &mask, const Anope::string &reason, time_t t, time_t lu)
+{
+ AutoKick *ak = Serialize::New<AutoKick *>();
+ ak->SetChannel(this);
+ ak->SetMask(mask);
+ ak->SetReason(reason);
+ ak->SetCreator(user);
+ ak->SetAddTime(t);
+ ak->SetLastUsed(lu);
+
+ return ak;
+}
+
+AutoKick *ChannelImpl::GetAkick(unsigned index)
+{
+ std::vector<AutoKick *> a = GetRefs<AutoKick *>();
+ return a.size() > index ? a[index] : nullptr;
+}
+
+unsigned ChannelImpl::GetAkickCount()
+{
+ std::vector<AutoKick *> t = GetRefs<AutoKick *>();
+ return t.size();
+}
+
+void ChannelImpl::ClearAkick()
+{
+ for (AutoKick *ak : GetRefs<AutoKick *>())
+ ak->Delete();
+}
+
+int16_t ChannelImpl::GetLevel(const Anope::string &priv)
+{
+ for (ChanServ::Level *l : this->GetRefs<ChanServ::Level *>())
+ if (l->GetName() == priv)
+ return l->GetLevel();
+
+ ChanServ::Privilege *p = ChanServ::service ? ChanServ::service->FindPrivilege(priv) : nullptr;
+ if (!p)
+ {
+ Log(LOG_DEBUG) << "Unknown privilege " + priv;
+ return ChanServ::ACCESS_INVALID;
+ }
+
+ return p->level;
+}
+
+void ChannelImpl::SetLevel(const Anope::string &priv, int16_t level)
+{
+ for (ChanServ::Level *l : this->GetRefs<ChanServ::Level *>())
+ if (l->GetName() == priv)
+ {
+ l->SetLevel(level);
+ return;
+ }
+
+ ChanServ::Privilege *p = ChanServ::service ? ChanServ::service->FindPrivilege(priv) : nullptr;
+ if (!p)
+ {
+ Log(LOG_DEBUG) << "Unknown privilege " + priv;
+ return;
+ }
+
+ ChanServ::Level *l = Serialize::New<ChanServ::Level *>();
+ l->SetChannel(this);
+ l->SetName(priv);
+ l->SetLevel(level);
+}
+
+void ChannelImpl::RemoveLevel(const Anope::string &priv)
+{
+ for (ChanServ::Level *l : this->GetRefs<ChanServ::Level *>())
+ if (l->GetName() == priv)
+ {
+ l->Delete();
+ return;
+ }
+}
+
+void ChannelImpl::ClearLevels()
+{
+ for (ChanServ::Level *l : this->GetRefs<ChanServ::Level *>())
+ l->Delete();
+}
+
+Anope::string ChannelImpl::GetIdealBan(User *u)
+{
+ int bt = this->GetBanType();
+ switch (bt)
+ {
+ case 0:
+ return "*!" + u->GetVIdent() + "@" + u->GetDisplayedHost();
+ case 1:
+ if (u->GetVIdent()[0] == '~')
+ return "*!*" + u->GetVIdent() + "@" + u->GetDisplayedHost();
+ else
+ return "*!" + u->GetVIdent() + "@" + u->GetDisplayedHost();
+ case 3:
+ return "*!" + u->Mask();
+ case 2:
+ default:
+ return "*!*@" + u->GetDisplayedHost();
+ }
+}
+
+bool ChannelImpl::IsFounder(const User *user)
+{
+ if (!user)
+ return false;
+
+ if (user->super_admin)
+ return true;
+
+ if (user->Account() && user->Account() == this->GetFounder())
+ return true;
+
+ return false;
+}
+
diff --git a/modules/chanserv/main/channel.h b/modules/chanserv/main/channel.h
new file mode 100644
index 000000000..94f56ddd4
--- /dev/null
+++ b/modules/chanserv/main/channel.h
@@ -0,0 +1,96 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+class ChannelImpl : public ChanServ::Channel
+{
+ friend class ChannelType;
+
+ NickServ::Account *founder = nullptr, *successor = nullptr;
+ Anope::string name, desc;
+ time_t time_registered = 0, last_used = 0;
+ Anope::string last_topic, last_topic_setter;
+ time_t last_topic_time = 0;
+ int16_t bantype = 0;
+ time_t banexpire = 0;
+ BotInfo *bi = nullptr;
+
+ public:
+ ChannelImpl(Serialize::TypeBase *type) : ChanServ::Channel(type) { }
+ ChannelImpl(Serialize::TypeBase *type, Serialize::ID id) : ChanServ::Channel(type, id) { }
+ ~ChannelImpl();
+ void Delete() override;
+
+ Anope::string GetName() override;
+ void SetName(const Anope::string &) override;
+
+ Anope::string GetDesc() override;
+ void SetDesc(const Anope::string &) override;
+
+ time_t GetTimeRegistered() override;
+ void SetTimeRegistered(const time_t &) override;
+
+ time_t GetLastUsed() override;
+ void SetLastUsed(const time_t &) override;
+
+ Anope::string GetLastTopic() override;
+ void SetLastTopic(const Anope::string &) override;
+
+ Anope::string GetLastTopicSetter() override;
+ void SetLastTopicSetter(const Anope::string &) override;
+
+ time_t GetLastTopicTime() override;
+ void SetLastTopicTime(const time_t &) override;
+
+ int16_t GetBanType() override;
+ void SetBanType(const int16_t &) override;
+
+ time_t GetBanExpire() override;
+ void SetBanExpire(const time_t &) override;
+
+ BotInfo *GetBI() override;
+ void SetBI(BotInfo *) override;
+
+ ServiceBot *GetBot() override;
+ void SetBot(ServiceBot *) override;
+
+ MemoServ::MemoInfo *GetMemos() override;
+
+ void SetFounder(NickServ::Account *nc) override;
+ NickServ::Account *GetFounder() override;
+
+ void SetSuccessor(NickServ::Account *nc) override;
+ NickServ::Account *GetSuccessor() override;
+
+ bool IsFounder(const User *user) override;
+ ChanServ::ChanAccess *GetAccess(unsigned index) /*const*/ override;
+ ChanServ::AccessGroup AccessFor(const User *u, bool = true) override;
+ ChanServ::AccessGroup AccessFor(NickServ::Account *nc, bool = true) override;
+ unsigned GetAccessCount()/* const*/ override;
+ void ClearAccess() override;
+ AutoKick* AddAkick(const Anope::string &user, NickServ::Account *akicknc, const Anope::string &reason, time_t t = Anope::CurTime, time_t lu = 0) override;
+ AutoKick* AddAkick(const Anope::string &user, const Anope::string &mask, const Anope::string &reason, time_t t = Anope::CurTime, time_t lu = 0) override;
+ AutoKick* GetAkick(unsigned index) override;
+ unsigned GetAkickCount() override;
+ void ClearAkick() override;
+ int16_t GetLevel(const Anope::string &priv) override;
+ void SetLevel(const Anope::string &priv, int16_t level) override;
+ void RemoveLevel(const Anope::string &priv) override;
+ void ClearLevels() override;
+ Anope::string GetIdealBan(User *u) override;
+};
diff --git a/modules/chanserv/main/channeltype.cpp b/modules/chanserv/main/channeltype.cpp
new file mode 100644
index 000000000..fa5e03e4c
--- /dev/null
+++ b/modules/chanserv/main/channeltype.cpp
@@ -0,0 +1,64 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2015-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "channeltype.h"
+
+ChannelType::ChannelType(Module *me) : Serialize::Type<ChannelImpl>(me)
+ , name(this, "name", &ChannelImpl::name)
+ , desc(this, "desc", &ChannelImpl::desc)
+ , time_registered(this, "time_registered", &ChannelImpl::time_registered)
+ , last_used(this, "last_used", &ChannelImpl::last_used)
+ , last_topic(this, "last_topic", &ChannelImpl::last_topic)
+ , last_topic_setter(this, "last_topic_setter", &ChannelImpl::last_topic_setter)
+ , last_topic_time(this, "last_topic_time", &ChannelImpl::last_topic_time)
+ , bantype(this, "bantype", &ChannelImpl::bantype)
+ , banexpire(this, "banexpire", &ChannelImpl::banexpire)
+ , founder(this, "founder", &ChannelImpl::founder)
+ , successor(this, "successor", &ChannelImpl::successor)
+ , bi(this, "bi", &ChannelImpl::bi)
+{
+
+}
+
+void ChannelType::Name::SetField(ChannelImpl *c, const Anope::string &value)
+{
+ ChanServ::registered_channel_map& map = ChanServ::service->GetChannels();
+ map.erase(GetField(c));
+
+ Serialize::Field<ChannelImpl, Anope::string>::SetField(c, value);
+
+ map[value] = c;
+}
+
+ChanServ::Channel *ChannelType::FindChannel(const Anope::string &chan)
+{
+ Serialize::ID id;
+ EventReturn result = EventManager::Get()->Dispatch(&Event::SerializeEvents::OnSerializeFind, this, &this->name, chan, id);
+ if (result == EVENT_ALLOW)
+ return RequireID(id);
+
+ // fall back
+ ChanServ::registered_channel_map& map = ChanServ::service->GetChannels();
+ auto it = map.find(chan);
+ if (it != map.end())
+ return it->second;
+ return nullptr;
+}
+
diff --git a/modules/chanserv/main/channeltype.h b/modules/chanserv/main/channeltype.h
new file mode 100644
index 000000000..95158427a
--- /dev/null
+++ b/modules/chanserv/main/channeltype.h
@@ -0,0 +1,53 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2015-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "channel.h"
+
+class ChannelType : public Serialize::Type<ChannelImpl>
+{
+ public:
+ /* Channel name */
+ struct Name : Serialize::Field<ChannelImpl, Anope::string>
+ {
+ using Serialize::Field<ChannelImpl, Anope::string>::Field;
+
+ void SetField(ChannelImpl *c, const Anope::string &value) override;
+ } name;
+ Serialize::Field<ChannelImpl, Anope::string> desc;
+ Serialize::Field<ChannelImpl, time_t> time_registered;
+ Serialize::Field<ChannelImpl, time_t> last_used;
+
+ Serialize::Field<ChannelImpl, Anope::string> last_topic;
+ Serialize::Field<ChannelImpl, Anope::string> last_topic_setter;
+ Serialize::Field<ChannelImpl, time_t> last_topic_time;
+
+ Serialize::Field<ChannelImpl, int16_t> bantype;
+ Serialize::Field<ChannelImpl, time_t> banexpire;
+
+ /* Channel founder */
+ Serialize::ObjectField<ChannelImpl, NickServ::Account *> founder;
+ /* Who gets the channel if the founder nick is dropped or expires */
+ Serialize::ObjectField<ChannelImpl, NickServ::Account *> successor;
+
+ Serialize::ObjectField<ChannelImpl, BotInfo *> bi;
+
+ ChannelType(Module *);
+
+ ChanServ::Channel *FindChannel(const Anope::string &);
+};
diff --git a/modules/chanserv/main/chanserv.cpp b/modules/chanserv/main/chanserv.cpp
new file mode 100644
index 000000000..f53f6301b
--- /dev/null
+++ b/modules/chanserv/main/chanserv.cpp
@@ -0,0 +1,607 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/chanserv/mode.h"
+#include "modules/help.h"
+#include "modules/botserv/bot.h"
+#include "modules/chanserv.h"
+#include "modules/chanserv/info.h"
+#include "modules/chanserv/akick.h"
+#include "channeltype.h"
+#include "leveltype.h"
+#include "modetype.h"
+#include "chanaccesstype.h"
+#include "modules/chanserv/main/chanaccess.h"
+
+class ChanServCore : public Module
+ , public ChanServ::ChanServService
+ , public EventHook<Event::ChannelCreate>
+ , public EventHook<Event::BotDelete>
+ , public EventHook<Event::BotPrivmsg>
+ , public EventHook<Event::DelCore>
+ , public EventHook<Event::DelChan>
+ , public EventHook<Event::Help>
+ , public EventHook<Event::CheckModes>
+ , public EventHook<Event::CanSet>
+ , public EventHook<Event::ChannelSync>
+ , public EventHook<Event::Log>
+ , public EventHook<Event::ExpireTick>
+ , public EventHook<Event::CheckDelete>
+ , public EventHook<Event::PreUplinkSync>
+ , public EventHook<Event::ChanRegistered>
+ , public EventHook<Event::JoinChannel>
+ , public EventHook<Event::ChannelModeSet>
+ , public EventHook<Event::ChanInfo>
+ , public EventHook<Event::SetCorrectModes>
+{
+ Reference<ServiceBot> ChanServ;
+ std::vector<Anope::string> defaults;
+ ExtensibleItem<bool> inhabit;
+ bool always_lower;
+ std::vector<ChanServ::Privilege> Privileges;
+ ChanServ::registered_channel_map registered_channels;
+ ChannelType channel_type;
+ LevelType level_type;
+ CSModeType mode_type;
+
+ public:
+ ChanServCore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, PSEUDOCLIENT | VENDOR)
+ , ChanServService(this)
+ , EventHook<Event::ChannelCreate>(this)
+ , EventHook<Event::BotDelete>(this)
+ , EventHook<Event::BotPrivmsg>(this)
+ , EventHook<Event::DelCore>(this)
+ , EventHook<Event::DelChan>(this)
+ , EventHook<Event::Help>(this)
+ , EventHook<Event::CheckModes>(this)
+ , EventHook<Event::CanSet>(this)
+ , EventHook<Event::ChannelSync>(this)
+ , EventHook<Event::Log>(this)
+ , EventHook<Event::ExpireTick>(this)
+ , EventHook<Event::CheckDelete>(this)
+ , EventHook<Event::PreUplinkSync>(this)
+ , EventHook<Event::ChanRegistered>(this)
+ , EventHook<Event::JoinChannel>(this)
+ , EventHook<Event::ChannelModeSet>(this)
+ , EventHook<Event::ChanInfo>(this)
+ , EventHook<Event::SetCorrectModes>(this)
+ , inhabit(this, "inhabit")
+ , always_lower(false)
+ , channel_type(this)
+ , level_type(this)
+ , mode_type(this)
+ {
+ ChanServ::service = this;
+ }
+
+ ~ChanServCore()
+ {
+ ChanServ::service = nullptr;
+ }
+
+ ChanServ::Channel *Find(const Anope::string &name) override
+ {
+ return channel_type.FindChannel(name);
+ }
+
+ ChanServ::registered_channel_map& GetChannels() override
+ {
+ return registered_channels;
+ }
+
+ void Hold(Channel *c) override
+ {
+ /** A timer used to keep the BotServ bot/ChanServ in the channel
+ * after kicking the last user in a channel
+ */
+ class ChanServTimer : public Timer
+ {
+ Reference<ServiceBot> &ChanServ;
+ ExtensibleItem<bool> &inhabit;
+ Reference<Channel> c;
+
+ public:
+ /** Constructor
+ * @param chan The channel
+ */
+ ChanServTimer(Reference<ServiceBot> &cs, ExtensibleItem<bool> &i, Module *m, Channel *chan) : Timer(m, Config->GetModule(m)->Get<time_t>("inhabit", "15s")), ChanServ(cs), inhabit(i), c(chan)
+ {
+ if (!ChanServ || !c)
+ return;
+ inhabit.Set(c, true);
+ if (!c->ci || !c->ci->GetBot())
+ ChanServ->Join(c);
+ else if (!c->FindUser(c->ci->GetBot()))
+ c->ci->GetBot()->Join(c);
+
+ /* Set +ntsi to prevent rejoin */
+ c->SetMode(NULL, "NOEXTERNAL");
+ c->SetMode(NULL, "TOPIC");
+ c->SetMode(NULL, "SECRET");
+ c->SetMode(NULL, "INVITE");
+ }
+
+ /** Called when the delay is up
+ * @param The current time
+ */
+ void Tick(time_t) override
+ {
+ if (!c)
+ return;
+
+ inhabit.Unset(c);
+
+ /* In the event we don't part */
+ c->RemoveMode(NULL, "SECRET");
+ c->RemoveMode(NULL, "INVITE");
+
+ if (!c->ci || !c->ci->GetBot())
+ {
+ if (ChanServ)
+ ChanServ->Part(c);
+ }
+ /* If someone has rejoined this channel in the meantime, don't part the bot */
+ else if (c->users.size() <= 1)
+ c->ci->GetBot()->Part(c);
+ }
+ };
+
+ if (inhabit.HasExt(c))
+ return;
+
+ new ChanServTimer(ChanServ, inhabit, this, c);
+ }
+
+ void AddPrivilege(ChanServ::Privilege p) override
+ {
+ unsigned i;
+ for (i = 0; i < Privileges.size(); ++i)
+ {
+ ChanServ::Privilege &priv = Privileges[i];
+
+ if (priv.rank > p.rank)
+ break;
+ }
+
+ Privileges.insert(Privileges.begin() + i, p);
+ }
+
+ void RemovePrivilege(ChanServ::Privilege &p) override
+ {
+ std::vector<ChanServ::Privilege>::iterator it = std::find(Privileges.begin(), Privileges.end(), p);
+ if (it != Privileges.end())
+ Privileges.erase(it);
+
+ for (auto& cit : GetChannels())
+ {
+ ChanServ::Channel *ci = cit.second;
+ ci->RemoveLevel(p.name);
+ }
+ }
+
+ ChanServ::Privilege *FindPrivilege(const Anope::string &name) override
+ {
+ for (unsigned i = Privileges.size(); i > 0; --i)
+ if (Privileges[i - 1].name.equals_ci(name))
+ return &Privileges[i - 1];
+ return NULL;
+ }
+
+ std::vector<ChanServ::Privilege> &GetPrivileges() override
+ {
+ return Privileges;
+ }
+
+ void ClearPrivileges() override
+ {
+ Privileges.clear();
+ }
+
+ void OnReload(Configuration::Conf *conf) override
+ {
+ const Anope::string &channick = conf->GetModule(this)->Get<Anope::string>("client");
+
+ if (channick.empty())
+ throw ConfigException(Module::name + ": <client> must be defined");
+
+ ServiceBot *bi = ServiceBot::Find(channick, true);
+ if (!bi)
+ throw ConfigException(Module::name + ": no bot named " + channick);
+
+ ChanServ = bi;
+
+ ClearPrivileges();
+ for (int i = 0; i < conf->CountBlock("privilege"); ++i)
+ {
+ Configuration::Block *privilege = conf->GetBlock("privilege", i);
+
+ const Anope::string &nname = privilege->Get<Anope::string>("name"),
+ &desc = privilege->Get<Anope::string>("desc");
+ int rank = privilege->Get<int>("rank");
+ Anope::string value = privilege->Get<Anope::string>("level");
+ int level;
+ if (value.equals_ci("founder"))
+ level = ChanServ::ACCESS_FOUNDER;
+ else if (value.equals_ci("disabled"))
+ level = ChanServ::ACCESS_INVALID;
+ else
+ level = privilege->Get<int>("level");
+
+ AddPrivilege(ChanServ::Privilege(nname, desc, rank, level));
+ }
+
+ spacesepstream(conf->GetModule(this)->Get<Anope::string>("defaults", "greet fantasy")).GetTokens(defaults);
+ if (defaults.empty())
+ {
+ defaults.push_back("KEEPTOPIC");
+ defaults.push_back("CS_SECURE");
+ defaults.push_back("SECUREFOUNDER");
+ defaults.push_back("SIGNKICK");
+ }
+ else if (defaults[0].equals_ci("none"))
+ defaults.clear();
+
+ always_lower = conf->GetModule(this)->Get<bool>("always_lower_ts");
+ }
+
+ void OnChannelCreate(Channel *c) override
+ {
+ c->ci = Find(c->name);
+ if (c->ci)
+ c->ci->c = c;
+ }
+
+ void OnBotDelete(ServiceBot *bi) override
+ {
+ if (bi == ChanServ)
+ ChanServ = NULL;
+ }
+
+ EventReturn OnBotPrivmsg(User *u, ServiceBot *bi, Anope::string &message) override
+ {
+ if (bi == ChanServ && Config->GetModule(this)->Get<bool>("opersonly") && !u->HasMode("OPER"))
+ {
+ u->SendMessage(bi, _("Access denied."));
+ return EVENT_STOP;
+ }
+
+ return EVENT_CONTINUE;
+ }
+
+ void OnDelCore(NickServ::Account *nc) override
+ {
+ unsigned int max_reg = Config->GetModule(this)->Get<unsigned int>("maxregistered");
+ for (ChanServ::Channel *ci : nc->GetRefs<ChanServ::Channel *>())
+ {
+ if (ci->GetFounder() == nc)
+ {
+ NickServ::Account *newowner = NULL;
+ if (ci->GetSuccessor() && ci->GetSuccessor() != nc && (ci->GetSuccessor()->IsServicesOper() || !max_reg || ci->GetSuccessor()->GetChannelCount() < max_reg))
+ newowner = ci->GetSuccessor();
+ else
+ {
+ ChanServ::ChanAccess *highest = NULL;
+ for (unsigned j = 0; j < ci->GetAccessCount(); ++j)
+ {
+ ChanServ::ChanAccess *ca = ci->GetAccess(j);
+ NickServ::Account *anc = ca->GetAccount();
+
+ if (!anc || (!anc->IsServicesOper() && max_reg && anc->GetChannelCount() >= max_reg) || (anc == nc))
+ continue;
+ if (!highest || *ca > *highest)
+ highest = ca;
+ }
+ if (highest)
+ newowner = highest->GetAccount();
+ }
+
+ if (newowner)
+ {
+ ::Log(LOG_NORMAL, "chanserv/drop", ChanServ) << "Transferring foundership of " << ci->GetName() << " from deleted nick " << nc->GetDisplay() << " to " << newowner->GetDisplay();
+ ci->SetFounder(newowner);
+ ci->SetSuccessor(NULL);
+ }
+ else
+ {
+ ::Log(LOG_NORMAL, "chanserv/drop", ChanServ) << "Deleting channel " << ci->GetName() << " owned by deleted nick " << nc->GetDisplay();
+
+ delete ci;
+ continue;
+ }
+ }
+
+ if (ci->GetSuccessor() == nc)
+ ci->SetSuccessor(NULL);
+
+ /* are these necessary? */
+ for (unsigned j = 0; j < ci->GetAccessCount(); ++j)
+ {
+ ChanServ::ChanAccess *ca = ci->GetAccess(j);
+ NickServ::Account *anc = ca->GetAccount();
+
+ if (anc && anc == nc)
+ {
+ delete ca;
+ break;
+ }
+ }
+
+ for (unsigned j = 0; j < ci->GetAkickCount(); ++j)
+ {
+ AutoKick *ak = ci->GetAkick(j);
+ if (ak->GetAccount() == nc)
+ {
+ delete ak;
+ break;
+ }
+ }
+ }
+ }
+
+ void OnDelChan(ChanServ::Channel *ci) override
+ {
+ /* remove access entries that are this channel */
+
+ for (ChanServ::Channel *c : ci->GetRefs<ChanServ::Channel *>())
+ {
+ for (unsigned j = 0; j < c->GetAccessCount(); ++j)
+ {
+ ChanServ::ChanAccess *a = c->GetAccess(j);
+
+ if (a->Mask().equals_ci(ci->GetName()))
+ {
+ delete a;
+ break;
+ }
+ }
+ }
+
+ if (ci->c)
+ {
+ ci->c->RemoveMode(ci->WhoSends(), "REGISTERED", "", false);
+
+ const Anope::string &require = Config->GetModule(this)->Get<Anope::string>("require");
+ if (!require.empty())
+ ci->c->SetModes(ci->WhoSends(), false, "-%s", require.c_str());
+ }
+ }
+
+ EventReturn OnPreHelp(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ if (!params.empty() || source.c || source.service != *ChanServ)
+ return EVENT_CONTINUE;
+ source.Reply(_("\002%s\002 allows you to register and control various\n"
+ "aspects of channels. %s can often prevent\n"
+ "malicious users from \"taking over\" channels by limiting\n"
+ "who is allowed channel operator privileges. Available\n"
+ "commands are listed below; to use them, type\n"
+ "\002%s%s \037command\037\002. For more information on a\n"
+ "specific command, type \002%s%s HELP \037command\037\002.\n"),
+ ChanServ->nick.c_str(), ChanServ->nick.c_str(), Config->StrictPrivmsg.c_str(), ChanServ->nick.c_str(), Config->StrictPrivmsg.c_str(), ChanServ->nick.c_str(), ChanServ->nick.c_str(), source.command.c_str());
+ return EVENT_CONTINUE;
+ }
+
+ void OnPostHelp(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ if (!params.empty() || source.c || source.service != *ChanServ)
+ return;
+ time_t chanserv_expire = Config->GetModule(this)->Get<time_t>("expire", "14d");
+ if (chanserv_expire >= 86400)
+ source.Reply(_(" \n"
+ "Note that any channel which is not used for %d days\n"
+ "(i.e. which no user on the channel's access list enters\n"
+ "for that period of time) will be automatically dropped."), chanserv_expire / 86400);
+ if (source.IsServicesOper())
+ source.Reply(_(" \n"
+ "Services Operators can also, depending on their access drop\n"
+ "any channel, view (and modify) the access, levels and akick\n"
+ "lists and settings for any channel."));
+ }
+
+ void OnCheckModes(Reference<Channel> &c) override
+ {
+ if (!c)
+ return;
+
+ if (c->ci)
+ c->SetMode(c->ci->WhoSends(), "REGISTERED", "", false);
+ else
+ c->RemoveMode(c->ci->WhoSends(), "REGISTERED", "", false);
+
+ const Anope::string &require = Config->GetModule(this)->Get<Anope::string>("require");
+ if (!require.empty())
+ {
+ if (c->ci)
+ c->SetModes(c->ci->WhoSends(), false, "+%s", require.c_str());
+ else
+ c->SetModes(c->ci->WhoSends(), false, "-%s", require.c_str());
+ }
+ }
+
+ EventReturn OnCanSet(User *u, const ChannelMode *cm) override
+ {
+ if (Config->GetModule(this)->Get<Anope::string>("nomlock").find(cm->mchar) != Anope::string::npos
+ || Config->GetModule(this)->Get<Anope::string>("require").find(cm->mchar) != Anope::string::npos)
+ return EVENT_STOP;
+ return EVENT_CONTINUE;
+ }
+
+ void OnChannelSync(Channel *c) override
+ {
+ bool perm = c->HasMode("PERM") || (c->ci && c->ci->HasFieldS("PERSIST"));
+ if (!perm && !c->botchannel && (c->users.empty() || (c->users.size() == 1 && c->users.begin()->second->user->server == Me)))
+ {
+ this->Hold(c);
+ }
+ }
+
+ void OnLog(::Log *l) override
+ {
+ if (l->type == LOG_CHANNEL)
+ l->bi = ChanServ;
+ }
+
+ void OnExpireTick() override
+ {
+ time_t chanserv_expire = Config->GetModule(this)->Get<time_t>("expire", "14d");
+
+ if (!chanserv_expire || Anope::NoExpire || Anope::ReadOnly)
+ return;
+
+ for (ChanServ::Channel *ci : channel_type.List<ChanServ::Channel *>())
+ {
+ bool expire = false;
+
+ if (Anope::CurTime - ci->GetLastUsed() >= chanserv_expire)
+ {
+ if (ci->c)
+ {
+ time_t last_used = ci->GetLastUsed();
+ for (Channel::ChanUserList::const_iterator cit = ci->c->users.begin(), cit_end = ci->c->users.end(); cit != cit_end && last_used == ci->GetLastUsed(); ++cit)
+ ci->AccessFor(cit->second->user);
+ expire = last_used == ci->GetLastUsed();
+ }
+ else
+ expire = true;
+ }
+
+ EventManager::Get()->Dispatch(&ChanServ::Event::PreChanExpire::OnPreChanExpire, ci, expire);
+
+ if (expire)
+ {
+ ::Log(LOG_NORMAL, "chanserv/expire", ChanServ) << "Expiring channel " << ci->GetName() << " (founder: " << (ci->GetFounder() ? ci->GetFounder()->GetDisplay() : "(none)") << ")";
+ EventManager::Get()->Dispatch(&ChanServ::Event::ChanExpire::OnChanExpire, ci);
+ delete ci;
+ }
+ }
+ }
+
+ EventReturn OnCheckDelete(Channel *c) override
+ {
+ /* Do not delete this channel if ChanServ/a BotServ bot is inhabiting it */
+ if (inhabit.HasExt(c))
+ return EVENT_STOP;
+
+ return EVENT_CONTINUE;
+ }
+
+ void OnPreUplinkSync(Server *serv) override
+ {
+ /* Find all persistent channels and create them, as we are about to finish burst to our uplink */
+ for (ChanServ::Channel *ci : channel_type.List<ChanServ::Channel *>())
+ {
+ if (ci->HasFieldS("PERSIST"))
+ {
+ bool c;
+ ci->c = Channel::FindOrCreate(ci->GetName(), c, ci->GetTimeRegistered());
+
+ if (ModeManager::FindChannelModeByName("PERM") != NULL)
+ {
+ if (c)
+ IRCD->SendChannel(ci->c);
+ ci->c->SetMode(NULL, "PERM");
+ }
+ else
+ {
+ if (!ci->GetBot())
+ ci->WhoSends()->Assign(NULL, ci);
+ if (ci->c->FindUser(ci->GetBot()) == NULL)
+ {
+ Anope::string botmodes = Config->GetModule("botserv")->Get<Anope::string>("botmodes",
+ Config->GetModule("chanserv")->Get<Anope::string>("botmodes"));
+ ChannelStatus status(botmodes);
+ ci->GetBot()->Join(ci->c, &status);
+ }
+ }
+ }
+ }
+
+ }
+
+ void OnChanRegistered(ChanServ::Channel *ci) override
+ {
+ /* Set default chan flags */
+ for (unsigned i = 0; i < defaults.size(); ++i)
+ ci->SetS<bool>(defaults[i].upper(), true);
+
+ if (!ci->c)
+ return;
+ /* Mark the channel as persistent */
+ if (ci->c->HasMode("PERM"))
+ ci->SetS("PERSIST", true);
+ /* Persist may be in def cflags, set it here */
+ else if (ci->HasFieldS("PERSIST"))
+ ci->c->SetMode(NULL, "PERM");
+ }
+
+ void OnJoinChannel(User *u, Channel *c) override
+ {
+ if (always_lower && c->ci && c->creation_time > c->ci->GetTimeRegistered())
+ {
+ ::Log(LOG_DEBUG) << "Changing TS of " << c->name << " from " << c->creation_time << " to " << c->ci->GetTimeRegistered();
+ c->creation_time = c->ci->GetTimeRegistered();
+ IRCD->SendChannel(c);
+ c->Reset();
+ }
+ }
+
+ EventReturn OnChannelModeSet(Channel *c, const MessageSource &setter, ChannelMode *mode, const Anope::string &param) override
+ {
+ if (!always_lower && Anope::CurTime == c->creation_time && c->ci && setter.GetUser() && !setter.GetUser()->server->IsULined())
+ {
+ ChanUserContainer *cu = c->FindUser(setter.GetUser());
+ ChannelMode *cm = ModeManager::FindChannelModeByName("OP");
+ if (cu && cm && !cu->status.HasMode(cm->mchar))
+ {
+ /* Our -o and their mode change crossing, bounce their mode */
+ c->RemoveMode(c->ci->WhoSends(), mode, param);
+ /* We don't set mlocks until after the join has finished processing, it will stack with this change,
+ * so there isn't much for the user to remove except -nt etc which is likely locked anyway.
+ */
+ }
+ }
+
+ return EVENT_CONTINUE;
+ }
+
+ void OnChanInfo(CommandSource &source, ChanServ::Channel *ci, InfoFormatter &info, bool show_all) override
+ {
+ if (!show_all)
+ return;
+
+ time_t chanserv_expire = Config->GetModule(this)->Get<time_t>("expire", "14d");
+ if (!ci->HasFieldS("CS_NO_EXPIRE") && chanserv_expire && !Anope::NoExpire && ci->GetLastUsed() != Anope::CurTime)
+ info[_("Expires")] = Anope::strftime(ci->GetLastUsed() + chanserv_expire, source.GetAccount());
+ }
+
+ void OnSetCorrectModes(User *user, Channel *chan, ChanServ::AccessGroup &access, bool &give_modes, bool &take_modes) override
+ {
+ if (always_lower)
+ // Since we always lower the TS, the other side will remove the modes if the channel ts lowers, so we don't
+ // have to worry about it
+ take_modes = false;
+ else if (ModeManager::FindChannelModeByName("REGISTERED"))
+ // Otherwise if the registered channel mode exists, we should remove modes if the channel is not +r
+ take_modes = !chan->HasMode("REGISTERED");
+ }
+};
+
+MODULE_INIT(ChanServCore)
+
diff --git a/modules/chanserv/main/level.cpp b/modules/chanserv/main/level.cpp
new file mode 100644
index 000000000..b06ff5594
--- /dev/null
+++ b/modules/chanserv/main/level.cpp
@@ -0,0 +1,52 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2015-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "modules/chanserv.h"
+#include "leveltype.h"
+
+ChanServ::Channel *LevelImpl::GetChannel()
+{
+ return Get(&LevelType::channel);
+}
+
+void LevelImpl::SetChannel(ChanServ::Channel *c)
+{
+ Set(&LevelType::channel, c);
+}
+
+Anope::string LevelImpl::GetName()
+{
+ return Get(&LevelType::name);
+}
+
+void LevelImpl::SetName(const Anope::string &n)
+{
+ Set(&LevelType::name, n);
+}
+
+int LevelImpl::GetLevel()
+{
+ return Get(&LevelType::level);
+}
+
+void LevelImpl::SetLevel(const int &i)
+{
+ Set(&LevelType::level, i);
+}
+
diff --git a/modules/chanserv/main/level.h b/modules/chanserv/main/level.h
new file mode 100644
index 000000000..d22dc2831
--- /dev/null
+++ b/modules/chanserv/main/level.h
@@ -0,0 +1,40 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2015-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+class LevelImpl : public ChanServ::Level
+{
+ friend class LevelType;
+
+ ChanServ::Channel *channel = nullptr;
+ Anope::string name;
+ int level = 0;
+
+ public:
+ LevelImpl(Serialize::TypeBase *type) : ChanServ::Level(type) { }
+ LevelImpl(Serialize::TypeBase *type, Serialize::ID id) : ChanServ::Level(type, id) { }
+
+ ChanServ::Channel *GetChannel() override;
+ void SetChannel(ChanServ::Channel *) override;
+
+ Anope::string GetName() override;
+ void SetName(const Anope::string &) override;
+
+ int GetLevel() override;
+ void SetLevel(const int &) override;
+};
diff --git a/modules/chanserv/main/leveltype.h b/modules/chanserv/main/leveltype.h
new file mode 100644
index 000000000..198b9248a
--- /dev/null
+++ b/modules/chanserv/main/leveltype.h
@@ -0,0 +1,35 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2015-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "level.h"
+
+class LevelType : public Serialize::Type<LevelImpl>
+{
+ public:
+ Serialize::ObjectField<LevelImpl, ChanServ::Channel *> channel;
+ Serialize::Field<LevelImpl, Anope::string> name;
+ Serialize::Field<LevelImpl, int> level;
+
+ LevelType(Module *creator) : Serialize::Type<LevelImpl>(creator)
+ , channel(this, "channel", &LevelImpl::channel, true)
+ , name(this, "name", &LevelImpl::name)
+ , level(this, "level", &LevelImpl::level)
+ {
+ }
+};
diff --git a/modules/chanserv/main/mode.cpp b/modules/chanserv/main/mode.cpp
new file mode 100644
index 000000000..38e63d91c
--- /dev/null
+++ b/modules/chanserv/main/mode.cpp
@@ -0,0 +1,52 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2015-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "modules/chanserv.h"
+#include "modetype.h"
+
+ChanServ::Channel *ModeImpl::GetChannel()
+{
+ return Get(&CSModeType::channel);
+}
+
+void ModeImpl::SetChannel(ChanServ::Channel *c)
+{
+ Set(&CSModeType::channel, c);
+}
+
+Anope::string ModeImpl::GetMode()
+{
+ return Get(&CSModeType::mode);
+}
+
+void ModeImpl::SetMode(const Anope::string &m)
+{
+ Set(&CSModeType::mode, m);
+}
+
+Anope::string ModeImpl::GetParam()
+{
+ return Get(&CSModeType::param);
+}
+
+void ModeImpl::SetParam(const Anope::string &p)
+{
+ Set(&CSModeType::param, p);
+}
+
diff --git a/modules/chanserv/main/mode.h b/modules/chanserv/main/mode.h
new file mode 100644
index 000000000..b56360a22
--- /dev/null
+++ b/modules/chanserv/main/mode.h
@@ -0,0 +1,40 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2015-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+class ModeImpl : public ChanServ::Mode
+{
+ friend class CSModeType;
+
+ ChanServ::Channel *channel = nullptr;
+ Anope::string mode, param;
+
+ public:
+ ModeImpl(Serialize::TypeBase *type) : ChanServ::Mode(type) { }
+ ModeImpl(Serialize::TypeBase *type, Serialize::ID id) : ChanServ::Mode(type, id) { }
+
+ ChanServ::Channel *GetChannel() override;
+ void SetChannel(ChanServ::Channel *) override;
+
+ Anope::string GetMode() override;
+ void SetMode(const Anope::string &) override;
+
+ Anope::string GetParam() override;
+ void SetParam(const Anope::string &) override;
+};
+
diff --git a/modules/chanserv/main/modetype.h b/modules/chanserv/main/modetype.h
new file mode 100644
index 000000000..6b1660986
--- /dev/null
+++ b/modules/chanserv/main/modetype.h
@@ -0,0 +1,35 @@
+
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2015-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "mode.h"
+
+class CSModeType : public Serialize::Type<ModeImpl>
+{
+ public:
+ Serialize::ObjectField<ModeImpl, ChanServ::Channel *> channel;
+ Serialize::Field<ModeImpl, Anope::string> mode, param;
+
+ CSModeType(Module *creator) : Serialize::Type<ModeImpl>(creator)
+ , channel(this, "channel", &ModeImpl::channel, true)
+ , mode(this, "mode", &ModeImpl::mode)
+ , param(this, "param", &ModeImpl::param)
+ {
+ }
+};
diff --git a/modules/chanserv/mode.cpp b/modules/chanserv/mode.cpp
new file mode 100644
index 000000000..4ff1ce1d3
--- /dev/null
+++ b/modules/chanserv/mode.cpp
@@ -0,0 +1,1011 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2010-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/chanserv/mode.h"
+#include "modules/chanserv/info.h"
+
+class ModeLockImpl : public ModeLock
+{
+ friend class ModeLockType;
+
+ ChanServ::Channel *channel = nullptr;
+ bool set = false;
+ Anope::string name, param, setter;
+ time_t created = 0;
+
+ public:
+ ModeLockImpl(Serialize::TypeBase *type) : ModeLock(type) { }
+ ModeLockImpl(Serialize::TypeBase *type, Serialize::ID id) : ModeLock(type, id) { }
+
+ ChanServ::Channel *GetChannel() override;
+ void SetChannel(ChanServ::Channel *ci) override;
+
+ bool GetSet() override;
+ void SetSet(const bool &) override;
+
+ Anope::string GetName() override;
+ void SetName(const Anope::string &name) override;
+
+ Anope::string GetParam() override;
+ void SetParam(const Anope::string &p) override;
+
+ Anope::string GetSetter() override;
+ void SetSetter(const Anope::string &s) override;
+
+ time_t GetCreated() override;
+ void SetCreated(const time_t &) override;
+};
+
+class ModeLockType : public Serialize::Type<ModeLockImpl>
+{
+ public:
+ Serialize::ObjectField<ModeLockImpl, ChanServ::Channel *> ci;
+ Serialize::Field<ModeLockImpl, bool> set;
+ Serialize::Field<ModeLockImpl, Anope::string> name, param, setter;
+ Serialize::Field<ModeLockImpl, time_t> created;
+
+ ModeLockType(Module *me) : Serialize::Type<ModeLockImpl>(me)
+ , ci(this, "ci", &ModeLockImpl::channel, true)
+ , set(this, "set", &ModeLockImpl::set)
+ , name(this, "name", &ModeLockImpl::name)
+ , param(this, "param", &ModeLockImpl::param)
+ , setter(this, "setter", &ModeLockImpl::setter)
+ , created(this, "created", &ModeLockImpl::created)
+ {
+ }
+};
+
+ChanServ::Channel *ModeLockImpl::GetChannel()
+{
+ return Get(&ModeLockType::ci);
+}
+
+void ModeLockImpl::SetChannel(ChanServ::Channel *ci)
+{
+ Set(&ModeLockType::ci, ci);
+}
+
+bool ModeLockImpl::GetSet()
+{
+ return Get(&ModeLockType::set);
+}
+
+void ModeLockImpl::SetSet(const bool &b)
+{
+ Set(&ModeLockType::set, b);
+}
+
+Anope::string ModeLockImpl::GetName()
+{
+ return Get(&ModeLockType::name);
+}
+
+void ModeLockImpl::SetName(const Anope::string &name)
+{
+ Set(&ModeLockType::name, name);
+}
+
+Anope::string ModeLockImpl::GetParam()
+{
+ return Get(&ModeLockType::param);
+}
+
+void ModeLockImpl::SetParam(const Anope::string &p)
+{
+ Set(&ModeLockType::name, p);
+}
+
+Anope::string ModeLockImpl::GetSetter()
+{
+ return Get(&ModeLockType::setter);
+}
+
+void ModeLockImpl::SetSetter(const Anope::string &s)
+{
+ Set(&ModeLockType::name, s);
+}
+
+time_t ModeLockImpl::GetCreated()
+{
+ return Get(&ModeLockType::created);
+}
+
+void ModeLockImpl::SetCreated(const time_t &c)
+{
+ Set(&ModeLockType::created, c);
+}
+
+class ModeLocksImpl : public ModeLocks
+{
+ public:
+ using ModeLocks::ModeLocks;
+
+ bool HasMLock(ChanServ::Channel *ci, ChannelMode *mode, const Anope::string &param, bool status) const override
+ {
+ if (!mode)
+ return false;
+
+ for (ModeLock *ml : ci->GetRefs<ModeLock *>())
+ if (ml->GetName() == mode->name && ml->GetSet() == status && ml->GetParam() == param)
+ return true;
+
+ return false;
+ }
+
+ bool SetMLock(ChanServ::Channel *ci, ChannelMode *mode, bool status, const Anope::string &param = "", Anope::string setter = "", time_t created = Anope::CurTime) override
+ {
+ if (!mode)
+ return false;
+
+ RemoveMLock(ci, mode, status, param);
+
+ if (setter.empty())
+ setter = ci->GetFounder() ? ci->GetFounder()->GetDisplay() : "Unknown";
+
+ ModeLock *ml = Serialize::New<ModeLock *>();
+ ml->SetChannel(ci);
+ ml->SetSet(status);
+ ml->SetName(mode->name);
+ ml->SetParam(param);
+ ml->SetSetter(setter);
+ ml->SetCreated(created);
+
+ EventReturn MOD_RESULT = EventManager::Get()->Dispatch(&Event::MLockEvents::OnMLock, ci, ml);
+ if (MOD_RESULT == EVENT_STOP)
+ {
+ delete ml;
+ return false;
+ }
+
+ return true;
+ }
+
+ bool RemoveMLock(ChanServ::Channel *ci, ChannelMode *mode, bool status, const Anope::string &param = "") override
+ {
+ if (!mode)
+ return false;
+
+ for (ModeLock *m : ci->GetRefs<ModeLockImpl *>())
+ if (m->GetName() == mode->name)
+ {
+ // For list or status modes, we must check the parameter
+ if (mode->type == MODE_LIST || mode->type == MODE_STATUS)
+ if (m->GetParam() != param)
+ continue;
+
+ EventReturn MOD_RESULT = EventManager::Get()->Dispatch(&Event::MLockEvents::OnUnMLock, ci, m);
+ if (MOD_RESULT == EVENT_STOP)
+ break;
+
+ delete m;
+ return true;
+ }
+
+ return false;
+ }
+
+ void ClearMLock(ChanServ::Channel *ci) override
+ {
+ for (ModeLock *m : ci->GetRefs<ModeLock *>())
+ delete m;
+ }
+
+ ModeList GetMLock(ChanServ::Channel *ci) const override
+ {
+ return ci->GetRefs<ModeLock *>();
+ }
+
+ std::list<ModeLock *> GetModeLockList(ChanServ::Channel *ci, const Anope::string &name) override
+ {
+ std::list<ModeLock *> mlist;
+ for (ModeLock *m : ci->GetRefs<ModeLock *>())
+ if (m->GetName() == name)
+ mlist.push_back(m);
+ return mlist;
+ }
+
+ ModeLock *GetMLock(ChanServ::Channel *ci, const Anope::string &mname, const Anope::string &param = "") override
+ {
+ for (ModeLock *m : ci->GetRefs<ModeLock *>())
+ if (m->GetName() == mname && m->GetParam() == param)
+ return m;
+
+ return NULL;
+ }
+
+ Anope::string GetMLockAsString(ChanServ::Channel *ci, bool complete) const override
+ {
+ Anope::string pos = "+", neg = "-", params;
+
+ for (ModeLock *ml : ci->GetRefs<ModeLock *>())
+ {
+ ChannelMode *cm = ModeManager::FindChannelModeByName(ml->GetName());
+
+ if (!cm || cm->type == MODE_LIST || cm->type == MODE_STATUS)
+ continue;
+
+ if (ml->GetSet())
+ pos += cm->mchar;
+ else
+ neg += cm->mchar;
+
+ if (complete && ml->GetSet() && !ml->GetParam().empty() && cm->type == MODE_PARAM)
+ params += " " + ml->GetParam();
+ }
+
+ if (pos.length() == 1)
+ pos.clear();
+ if (neg.length() == 1)
+ neg.clear();
+
+ return pos + neg + params;
+ }
+};
+
+class CommandCSMode : public Command
+{
+ ServiceReference<ModeLocks> mlocks;
+
+ bool CanSet(CommandSource &source, ChanServ::Channel *ci, ChannelMode *cm, bool self)
+ {
+ if (!ci || !cm || cm->type != MODE_STATUS)
+ return false;
+
+ return source.AccessFor(ci).HasPriv(cm->name + (self ? "ME" : ""));
+ }
+
+ void DoLock(CommandSource &source, ChanServ::Channel *ci, const std::vector<Anope::string> &params)
+ {
+ User *u = source.GetUser();
+ const Anope::string &subcommand = params[2];
+ const Anope::string &param = params.size() > 3 ? params[3] : "";
+
+ bool override = !source.AccessFor(ci).HasPriv("MODE");
+
+ if (Anope::ReadOnly && !subcommand.equals_ci("LIST"))
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ if ((subcommand.equals_ci("ADD") || subcommand.equals_ci("SET")) && !param.empty())
+ {
+ /* If setting, remove the existing locks */
+ if (subcommand.equals_ci("SET"))
+ for (ModeLock *ml : mlocks->GetMLock(ci))
+ {
+ ChannelMode *cm = ModeManager::FindChannelModeByName(ml->GetName());
+ if (cm && cm->CanSet(source.GetUser()))
+ mlocks->RemoveMLock(ci, cm, ml->GetSet(), ml->GetParam());
+ }
+
+ spacesepstream sep(param);
+ Anope::string modes;
+
+ sep.GetToken(modes);
+
+ Anope::string pos = "+", neg = "-", pos_params, neg_params;
+
+ int adding = 1;
+ bool needreply = true;
+ for (size_t i = 0; i < modes.length(); ++i)
+ {
+ switch (modes[i])
+ {
+ case '+':
+ adding = 1;
+ break;
+ case '-':
+ adding = 0;
+ break;
+ default:
+ needreply = false;
+ ChannelMode *cm = ModeManager::FindChannelModeByChar(modes[i]);
+ if (!cm)
+ {
+ source.Reply(_("Unknown mode character \002{0}\002 ignored."), modes[i]);
+ break;
+ }
+ else if (u && !cm->CanSet(u))
+ {
+ source.Reply(_("You may not (un)lock mode \002{0}\002."), modes[i]);
+ break;
+ }
+
+ Anope::string mode_param;
+ if (((cm->type == MODE_STATUS || cm->type == MODE_LIST) && !sep.GetToken(mode_param)) || (cm->type == MODE_PARAM && adding && !sep.GetToken(mode_param)))
+ source.Reply(_("Missing parameter for mode \002{0}\002."), cm->mchar);
+ else if (cm->type == MODE_LIST && ci->c && IRCD->GetMaxListFor(ci->c) && ci->c->HasMode(cm->name) >= IRCD->GetMaxListFor(ci->c))
+ source.Reply(_("List for mode \002{0}\002 is full."), cm->mchar);
+ else if (ci->GetRefs<ModeLock *>().size() >= Config->GetModule(this->GetOwner())->Get<unsigned>("max", "32"))
+ source.Reply(_("The mode lock list of \002{0}\002 is full."), ci->GetName());
+ else
+ {
+ mlocks->SetMLock(ci, cm, adding, mode_param, source.GetNick());
+
+ if (adding)
+ {
+ pos += cm->mchar;
+ if (!mode_param.empty())
+ pos_params += " " + mode_param;
+ }
+ else
+ {
+ neg += cm->mchar;
+ if (!mode_param.empty())
+ neg_params += " " + mode_param;
+ }
+ }
+ }
+ }
+
+ if (pos == "+")
+ pos.clear();
+ if (neg == "-")
+ neg.clear();
+ Anope::string reply = pos + neg + pos_params + neg_params;
+
+ if (!reply.empty())
+ {
+ source.Reply(_("\002{0}\002 locked on \002{1}\002."), reply, ci->GetName());
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to lock " << reply;
+ }
+ else if (needreply)
+ source.Reply(_("Nothing to do."));
+
+ if (ci->c)
+ ci->c->CheckModes();
+ }
+ else if (subcommand.equals_ci("DEL") && !param.empty())
+ {
+ spacesepstream sep(param);
+ Anope::string modes;
+
+ sep.GetToken(modes);
+
+ int adding = 1;
+ bool needreply = true;
+ for (size_t i = 0; i < modes.length(); ++i)
+ {
+ switch (modes[i])
+ {
+ case '+':
+ adding = 1;
+ break;
+ case '-':
+ adding = 0;
+ break;
+ default:
+ needreply = false;
+ ChannelMode *cm = ModeManager::FindChannelModeByChar(modes[i]);
+ if (!cm)
+ {
+ source.Reply(_("Unknown mode character \002{0}\002 ignored."), modes[i]);
+ break;
+ }
+ else if (u && !cm->CanSet(u))
+ {
+ source.Reply(_("You may not (un)lock mode \002{0}\002."), modes[i]);
+ break;
+ }
+
+ Anope::string mode_param;
+ if (cm->type != MODE_REGULAR && !sep.GetToken(mode_param))
+ source.Reply(_("Missing parameter for mode \002{0}\002."), cm->mchar);
+ else
+ {
+ if (mlocks->RemoveMLock(ci, cm, adding, mode_param))
+ {
+ if (!mode_param.empty())
+ mode_param = " " + mode_param;
+ source.Reply(_("\002{0}{1}{2}\002 has been unlocked from \002{3}\002."), adding == 1 ? '+' : '-', cm->mchar, mode_param, ci->GetName());
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to unlock " << (adding ? '+' : '-') << cm->mchar << mode_param;
+ }
+ else
+ source.Reply(_("\002{0}{1}\002 is not locked on \002{2}\002."), adding == 1 ? '+' : '-', cm->mchar, ci->GetName());
+ }
+ }
+ }
+
+ if (needreply)
+ source.Reply(_("Nothing to do."));
+ }
+ else if (subcommand.equals_ci("LIST"))
+ {
+ ModeLocks::ModeList locks = mlocks->GetMLock(ci);
+ if (locks.empty())
+ {
+ source.Reply(_("Channel \002{0}\002 has no mode locks."), ci->GetName());
+ return;
+ }
+
+ ListFormatter list(source.GetAccount());
+ list.AddColumn(_("Mode")).AddColumn(_("Param")).AddColumn(_("Creator")).AddColumn(_("Created"));
+
+ for (ModeLock *ml : locks)
+ {
+ ChannelMode *cm = ModeManager::FindChannelModeByName(ml->GetName());
+ if (!cm)
+ continue;
+
+ ListFormatter::ListEntry entry;
+ entry["Mode"] = Anope::printf("%c%c", ml->GetSet() ? '+' : '-', cm->mchar);
+ entry["Param"] = ml->GetParam();
+ entry["Creator"] = ml->GetSetter();
+ entry["Created"] = Anope::strftime(ml->GetCreated(), NULL, true);
+ list.AddEntry(entry);
+ }
+
+ source.Reply(_("Mode locks for \002{0}\002:"), ci->GetName());
+
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+ }
+ else
+ this->OnSyntaxError(source, subcommand);
+ }
+
+ void DoSet(CommandSource &source, ChanServ::Channel *ci, const std::vector<Anope::string> &params)
+ {
+ User *u = source.GetUser();
+
+ bool has_access = source.AccessFor(ci).HasPriv("MODE") || source.HasPriv("chanserv/administration");
+ bool can_override = source.HasPriv("chanserv/administration");
+
+ spacesepstream sep(params.size() > 3 ? params[3] : "");
+ Anope::string modes = params[2], param;
+
+ bool override = !source.AccessFor(ci).HasPriv("MODE") && source.HasPriv("chanserv/administration");
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to set " << params[2] << (params.size() > 3 ? " " + params[3] : "");
+
+ int adding = -1;
+ for (size_t i = 0; i < modes.length(); ++i)
+ {
+ switch (modes[i])
+ {
+ case '+':
+ adding = 1;
+ break;
+ case '-':
+ adding = 0;
+ break;
+ case '*':
+ if (adding == -1 || !has_access)
+ break;
+ for (unsigned j = 0; j < ModeManager::GetChannelModes().size() && ci->c; ++j)
+ {
+ ChannelMode *cm = ModeManager::GetChannelModes()[j];
+
+ if (!u || cm->CanSet(u) || can_override)
+ {
+ if (cm->type == MODE_REGULAR || (!adding && cm->type == MODE_PARAM))
+ {
+ if (adding)
+ ci->c->SetMode(NULL, cm);
+ else
+ ci->c->RemoveMode(NULL, cm);
+ }
+ }
+ }
+ break;
+ default:
+ if (adding == -1)
+ break;
+ ChannelMode *cm = ModeManager::FindChannelModeByChar(modes[i]);
+ if (!cm || (u && !cm->CanSet(u) && !can_override))
+ continue;
+ switch (cm->type)
+ {
+ case MODE_REGULAR:
+ if (!has_access)
+ break;
+ if (adding)
+ ci->c->SetMode(NULL, cm);
+ else
+ ci->c->RemoveMode(NULL, cm);
+ break;
+ case MODE_PARAM:
+ if (!has_access)
+ break;
+ if (adding && !sep.GetToken(param))
+ break;
+ if (adding)
+ ci->c->SetMode(NULL, cm, param);
+ else
+ ci->c->RemoveMode(NULL, cm);
+ break;
+ case MODE_STATUS:
+ {
+ if (!sep.GetToken(param))
+ param = source.GetNick();
+
+ ChanServ::AccessGroup u_access = source.AccessFor(ci);
+
+ if (param.find_first_of("*?") != Anope::string::npos)
+ {
+ if (!this->CanSet(source, ci, cm, false) && !can_override)
+ {
+ source.Reply(_("You do not have access to set mode \002{0}\002."), cm->mchar);
+ break;
+ }
+
+ for (Channel::ChanUserList::const_iterator it = ci->c->users.begin(), it_end = ci->c->users.end(); it != it_end;)
+ {
+ ChanUserContainer *uc = it->second;
+ ++it;
+
+ ChanServ::AccessGroup targ_access = ci->AccessFor(uc->user);
+
+ if (uc->user->IsProtected() || (ci->HasFieldS("PEACE") && targ_access >= u_access && !can_override))
+ {
+ source.Reply(_("You do not have the access to change the modes of \002{0}\002."), uc->user->nick.c_str());
+ continue;
+ }
+
+ if (Anope::Match(uc->user->GetMask(), param))
+ {
+ if (adding)
+ ci->c->SetMode(NULL, cm, uc->user->GetUID());
+ else
+ ci->c->RemoveMode(NULL, cm, uc->user->GetUID());
+ }
+ }
+ }
+ else
+ {
+ User *target = User::Find(param, true);
+ if (target == NULL)
+ {
+ source.Reply(_("User \002{0}\002 isn't currently online."), param);
+ break;
+ }
+
+ if (!this->CanSet(source, ci, cm, source.GetUser() == target) && !can_override)
+ {
+ source.Reply(_("You do not have access to set mode \002{0}\002."), cm->mchar);
+ break;
+ }
+
+ if (source.GetUser() != target)
+ {
+ ChanServ::AccessGroup targ_access = ci->AccessFor(target);
+ if (ci->HasFieldS("PEACE") && targ_access >= u_access && !can_override)
+ {
+ source.Reply(_("You do not have the access to change the modes of \002{0}\002"), target->nick);
+ break;
+ }
+ else if (target->IsProtected())
+ {
+ source.Reply(_("Access denied. \002{0}\002 is protected and can not have their modes changed."), target->nick);
+ break;
+ }
+ }
+
+ if (adding)
+ ci->c->SetMode(NULL, cm, target->GetUID());
+ else
+ ci->c->RemoveMode(NULL, cm, target->GetUID());
+ }
+ break;
+ }
+ case MODE_LIST:
+ if (!has_access)
+ break;
+ if (!sep.GetToken(param))
+ break;
+ if (adding)
+ {
+ if (IRCD->GetMaxListFor(ci->c) && ci->c->HasMode(cm->name) < IRCD->GetMaxListFor(ci->c))
+ ci->c->SetMode(NULL, cm, param);
+ }
+ else
+ {
+ std::vector<Anope::string> v = ci->c->GetModeList(cm->name);
+ for (unsigned j = 0; j < v.size(); ++j)
+ if (Anope::Match(v[j], param))
+ ci->c->RemoveMode(NULL, cm, v[j]);
+ }
+ }
+ }
+ }
+ }
+
+ void DoClear(CommandSource &source, ChanServ::Channel *ci, const std::vector<Anope::string> &params)
+ {
+ const Anope::string &param = params.size() > 2 ? params[2] : "";
+
+ if (param.empty())
+ {
+ std::vector<Anope::string> new_params;
+ new_params.push_back(params[0]);
+ new_params.push_back("SET");
+ new_params.push_back("-*");
+ this->DoSet(source, ci, new_params);
+ return;
+ }
+
+ ChannelMode *cm;
+ if (param.length() == 1)
+ cm = ModeManager::FindChannelModeByChar(param[0]);
+ else
+ {
+ cm = ModeManager::FindChannelModeByName(param.upper());
+ if (!cm)
+ cm = ModeManager::FindChannelModeByName(param.substr(0, param.length() - 1).upper());
+ }
+
+ if (!cm)
+ {
+ source.Reply(_("There is no such mode \002{0}\002."), param);
+ return;
+ }
+
+ if (cm->type != MODE_STATUS && cm->type != MODE_LIST)
+ {
+ source.Reply(_("Mode \002{0}\002 is not a status or list mode."), param);
+ return;
+ }
+
+ std::vector<Anope::string> new_params;
+ new_params.push_back(params[0]);
+ new_params.push_back("SET");
+ new_params.push_back("-" + stringify(cm->mchar));
+ new_params.push_back("*");
+ this->DoSet(source, ci, new_params);
+ }
+
+ public:
+ CommandCSMode(Module *creator) : Command(creator, "chanserv/mode", 2, 4)
+ {
+ this->SetDesc(_("Control modes and mode locks on a channel"));
+ this->SetSyntax(_("\037channel\037 LOCK {ADD|DEL|SET|LIST} [\037what\037]"));
+ this->SetSyntax(_("\037channel\037 SET \037modes\037"));
+ this->SetSyntax(_("\037channel\037 CLEAR [\037what\037]"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &chan = params[0];
+ const Anope::string &subcommand = params[1];
+
+ ChanServ::Channel *ci = ChanServ::Find(chan);
+ if (!ci)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
+ return;
+ }
+
+ if (subcommand.equals_ci("LOCK") && params.size() > 2)
+ {
+ if (!source.AccessFor(ci).HasPriv("MODE") && !source.HasPriv("chanserv/administration"))
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "MODE", ci->GetName());
+ else
+ this->DoLock(source, ci, params);
+ }
+ else if (!ci->c)
+ source.Reply(_("Channel \002{0}\002 doesn't exist."), ci->GetName());
+ else if (subcommand.equals_ci("SET") && params.size() > 2)
+ this->DoSet(source, ci, params);
+ else if (subcommand.equals_ci("CLEAR"))
+ {
+ if (!source.AccessFor(ci).HasPriv("MODE") && !source.HasPriv("chanserv/administration"))
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "MODE", ci->GetName());
+ else
+ this->DoClear(source, ci, params);
+ }
+ else
+ this->OnSyntaxError(source, "");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Controls mode locks and allows changing modes on a channel."
+ "\n"
+ "The \002{0} LOCK\002 command allows you to add, delete, and view mode locks on a channel."
+ " If a mode is locked on or off, services will not allow that mode to be changed."
+ " The \002SET\002 command will clear all existing mode locks and set the new one given, while \002ADD\002 and \002DEL\002 modify the existing mode lock.\n"
+ "\n"
+ "Example:\n"
+ " {0} #channel LOCK ADD +bmnt *!*@*aol*\n"
+ "\n"
+ "The \002{0} SET\002 command allows you to set modes through services. Wildcards * and ? may be given as parameters for list and status modes.\n"
+ "Example:\n"
+ " {0} #channel SET +v *\n"
+ " Sets voice status to all users in the channel.\n"
+ "\n"
+ " {0} #channel SET -b ~c:*\n"
+ " Clears all extended bans that start with ~c:\n"
+ "\n"
+ "The \002{0} CLEAR\002 command is an easy way to clear modes on a channel. \037what\037 may be any mode name. Examples include bans, excepts, inviteoverrides, ops, halfops, and voices."
+ " If \037what\037 is not given then all basic modes are removed."),
+ source.command.upper());
+ return true;
+ }
+};
+
+static Anope::map<std::pair<bool, Anope::string> > modes;
+
+class CommandCSModes : public Command
+{
+ public:
+ CommandCSModes(Module *creator) : Command(creator, "chanserv/modes", 1, 2)
+ {
+ this->SetSyntax(_("\037channel\037 [\037user\037]"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &chan = params[0];
+ User *u = source.GetUser(),
+ *targ = params.size() > 1 ? User::Find(params[1], true) : u;
+
+ if (!targ)
+ {
+ source.Reply(_("User \002{0}\002 isn't currently online."), params.size() > 1 ? params[1] : source.GetNick());
+ return;
+ }
+
+ ChanServ::Channel *ci = ChanServ::Find(chan);
+ if (!ci)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
+ return;
+ }
+
+ if (!ci->c)
+ {
+ source.Reply(_("Channel \002%s\002 doesn't exist."), ci->GetName());
+ return;
+ }
+
+ ChanServ::AccessGroup u_access = source.AccessFor(ci), targ_access = ci->AccessFor(targ);
+ const std::pair<bool, Anope::string> &m = modes[source.command];
+
+ bool can_override = source.HasPriv("chanserv/administration");
+ bool override = false;
+
+ if (m.second.empty())
+ return; // Configuration issue
+
+ const Anope::string &want = u == targ ? m.second + "ME" : m.second;
+ if (!u_access.HasPriv(want))
+ {
+ if (!can_override)
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), want, ci->GetName());
+ return;
+ }
+ else
+ override = true;
+ }
+
+ if (!override && !m.first && u != targ && (targ->IsProtected() || (ci->HasFieldS("PEACE") && targ_access >= u_access)))
+ {
+ if (!can_override)
+ {
+ source.Reply(_("Access denied. \002{0}\002 has the same or more privileges than you on \002{1}\002."), targ->nick, ci->GetName());
+ return;
+ }
+ else
+ override = true;
+ }
+
+ if (!ci->c->FindUser(targ))
+ {
+ source.Reply(_("User \002{0}\002 is not on channel \002{1}\002."), targ->nick, ci->GetName());
+ return;
+ }
+
+ if (m.first)
+ ci->c->SetMode(NULL, m.second, targ->GetUID());
+ else
+ ci->c->RemoveMode(NULL, m.second, targ->GetUID());
+
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "on " << targ->nick;
+ }
+
+ const Anope::string GetDesc(CommandSource &source) const override
+ {
+ const std::pair<bool, Anope::string> &m = modes[source.command];
+ if (!m.second.empty())
+ {
+ if (m.first)
+ return Anope::printf(Language::Translate(source.GetAccount(), _("Gives you or the specified nick %s status on a channel")), m.second.c_str());
+ else
+ return Anope::printf(Language::Translate(source.GetAccount(), _("Removes %s status from you or the specified nick on a channel")), m.second.c_str());
+ }
+ else
+ return "";
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ const std::pair<bool, Anope::string> &m = modes[source.command];
+ if (m.second.empty())
+ return false;
+
+ if (m.first)
+ source.Reply(_("Gives {0} status to the selected \037user\037 on \037channel\037. If \037user\037 is not given, it will give the status to you."),
+ m.second.upper());
+ else
+ source.Reply(_("Removes {0} status from the selected \037user\037 on \037channel\037. If \037user\037 is not given, it will remove the status from you."),
+ m.second.upper());
+ source.Reply(" ");
+ source.Reply(_("Use of this command requires the \002{1}(ME)\002 privilege on \037channel\037."), m.second.upper());
+
+ return true;
+ }
+};
+
+class CSMode : public Module
+ , public EventHook<Event::CheckModes>
+ , public EventHook<Event::ChanRegistered>
+ , public EventHook<Event::ChanInfo>
+{
+ CommandCSMode commandcsmode;
+ CommandCSModes commandcsmodes;
+ ModeLocksImpl modelock;
+ ModeLockType modelock_type;
+
+ public:
+ CSMode(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , EventHook<Event::CheckModes>(this)
+ , EventHook<Event::ChanRegistered>(this)
+ , EventHook<Event::ChanInfo>(this)
+ , commandcsmode(this)
+ , commandcsmodes(this)
+ , modelock(this)
+ , modelock_type(this)
+ {
+ }
+
+ void OnReload(Configuration::Conf *conf) override
+ {
+ modes.clear();
+
+ for (int i = 0; i < conf->CountBlock("command"); ++i)
+ {
+ Configuration::Block *block = conf->GetBlock("command", i);
+
+ const Anope::string &cname = block->Get<Anope::string>("name"),
+ &cmd = block->Get<Anope::string>("command");
+
+ if (cname.empty() || cmd != "chanserv/modes")
+ continue;
+
+ const Anope::string &set = block->Get<Anope::string>("set"),
+ &unset = block->Get<Anope::string>("unset");
+
+ if (set.empty() && unset.empty())
+ continue;
+
+ modes[cname] = std::make_pair(!set.empty(), !set.empty() ? set : unset);
+ }
+ }
+
+ void OnCheckModes(Reference<Channel> &c) override
+ {
+ if (!c || !c->ci)
+ return;
+
+ ModeLocks::ModeList locks = modelock.GetMLock(c->ci);
+ for (ModeLock *ml : locks)
+ {
+ ChannelMode *cm = ModeManager::FindChannelModeByName(ml->GetName());
+ if (!cm)
+ continue;
+
+ if (cm->type == MODE_REGULAR)
+ {
+ if (!c->HasMode(cm->name) && ml->GetSet())
+ c->SetMode(NULL, cm, "", false);
+ else if (c->HasMode(cm->name) && !ml->GetSet())
+ c->RemoveMode(NULL, cm, "", false);
+ }
+ else if (cm->type == MODE_PARAM)
+ {
+ /* If the channel doesn't have the mode, or it does and it isn't set correctly */
+ if (ml->GetSet())
+ {
+ Anope::string param;
+ c->GetParam(cm->name, param);
+
+ if (!c->HasMode(cm->name) || (!param.empty() && !ml->GetParam().empty() && !param.equals_cs(ml->GetParam())))
+ c->SetMode(NULL, cm, ml->GetParam(), false);
+ }
+ else
+ {
+ if (c->HasMode(cm->name))
+ c->RemoveMode(NULL, cm, "", false);
+ }
+ }
+ else if (cm->type == MODE_LIST || cm->type == MODE_STATUS)
+ {
+ if (ml->GetSet())
+ c->SetMode(NULL, cm, ml->GetParam(), false);
+ else
+ c->RemoveMode(NULL, cm, ml->GetParam(), false);
+ }
+ }
+ }
+
+ void OnChanRegistered(ChanServ::Channel *ci) override
+ {
+ Anope::string mlock;
+ spacesepstream sep(Config->GetModule(this)->Get<Anope::string>("mlock", "+nt"));
+ if (sep.GetToken(mlock))
+ {
+ bool add = true;
+ for (unsigned i = 0; i < mlock.length(); ++i)
+ {
+ if (mlock[i] == '+')
+ {
+ add = true;
+ continue;
+ }
+
+ if (mlock[i] == '-')
+ {
+ add = false;
+ continue;
+ }
+
+ ChannelMode *cm = ModeManager::FindChannelModeByChar(mlock[i]);
+ if (!cm)
+ continue;
+
+ Anope::string param;
+ if (cm->type == MODE_PARAM)
+ {
+ ChannelModeParam *cmp = anope_dynamic_static_cast<ChannelModeParam *>(cm);
+ if (add || !cmp->minus_no_arg)
+ {
+ sep.GetToken(param);
+ if (param.empty() || !cmp->IsValid(param))
+ continue;
+ }
+ }
+ else if (cm->type != MODE_REGULAR)
+ {
+ sep.GetToken(param);
+ if (param.empty())
+ continue;
+ }
+
+ modelock.SetMLock(ci, cm, add, param);
+ }
+ }
+ }
+
+ void OnChanInfo(CommandSource &source, ChanServ::Channel *ci, InfoFormatter &info, bool show_hidden) override
+ {
+ if (!show_hidden)
+ return;
+
+ const Anope::string &m = modelock.GetMLockAsString(ci, true);
+ if (!m.empty())
+ info[_("Mode lock")] = m;
+ }
+};
+
+MODULE_INIT(CSMode)
diff --git a/modules/chanserv/register.cpp b/modules/chanserv/register.cpp
new file mode 100644
index 000000000..47356b507
--- /dev/null
+++ b/modules/chanserv/register.cpp
@@ -0,0 +1,174 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+
+class CommandCSRegister : public Command
+{
+ public:
+ CommandCSRegister(Module *creator) : Command(creator, "chanserv/register", 1, 2)
+ {
+ this->SetDesc(_("Register a channel"));
+ this->SetSyntax(_("\037channel\037 [\037description\037]"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &chan = params[0];
+ const Anope::string &chdesc = params.size() > 1 ? params[1] : "";
+
+ User *u = source.GetUser();
+ NickServ::Account *nc = source.nc;
+
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Sorry, channel registration is temporarily disabled."));
+ return;
+ }
+
+ if (nc->HasFieldS("UNCONFIRMED"))
+ {
+ source.Reply(_("You must confirm your account before you can register a channel."));
+ return;
+ }
+
+ if (chan[0] == '&')
+ {
+ source.Reply(_("Local channels can not be registered."));
+ return;
+ }
+
+ if (chan[0] != '#')
+ {
+ source.Reply(_("Please use the symbol of \002#\002 when attempting to register."));
+ return;
+ }
+
+ if (!IRCD->IsChannelValid(chan))
+ {
+ source.Reply(_("Channel \002{0}\002 is not a valid channel."), chan);
+ return;
+ }
+
+ Channel *c = Channel::Find(params[0]);
+ if (!c && u)
+ {
+ source.Reply(_("Channel \002{0}\002 doesn't exist."), chan);
+ return;
+ }
+
+ ChanServ::Channel *ci = ChanServ::Find(chan);
+ if (ci)
+ {
+ source.Reply(_("Channel \002{0}\002 is already registered!"), chan);
+ return;
+ }
+
+ if (c && u && !c->HasUserStatus(u, "OP"))
+ {
+ source.Reply(_("You must be a channel operator to register the channel."));
+ return;
+ }
+
+ unsigned maxregistered = Config->GetModule("chanserv")->Get<unsigned>("maxregistered");
+ if (maxregistered && nc->GetChannelCount() >= maxregistered && !source.HasPriv("chanserv/no-register-limit"))
+ {
+ if (nc->GetChannelCount() > maxregistered)
+ source.Reply(_("Sorry, you have already exceeded your limit of \002{0}\002 channels."), maxregistered);
+ else
+ source.Reply(_("Sorry, you have already reached your limit of \002{0}\002 channels."), maxregistered);
+ return;
+ }
+
+ ci = Serialize::New<ChanServ::Channel *>();
+ if (ci == nullptr)
+ return;
+
+ ci->SetName(chan);
+ ci->SetFounder(nc);
+ ci->SetDesc(chdesc);
+ ci->SetTimeRegistered(Anope::CurTime);
+ ci->SetLastUsed(Anope::CurTime);
+ ci->SetBanType(2);
+
+ ci->c = c; // XXX? this isnt set on reconstrubted objects?
+ c->ci = ci;
+
+ if (c && !c->topic.empty())
+ {
+ ci->SetLastTopic(c->topic);
+ ci->SetLastTopicSetter(c->topic_setter);
+ ci->SetLastTopicTime(c->topic_time);
+ }
+ else
+ {
+ ci->SetLastTopicSetter(source.service->nick);
+ }
+
+ Log(LOG_COMMAND, source, this, ci);
+ source.Reply(_("Channel \002{0}\002 registered under your account: \002{1}\002"), chan, nc->GetDisplay());
+
+ /* Implement new mode lock */
+ if (c)
+ {
+ c->CheckModes();
+ if (u)
+ c->SetCorrectModes(u, true);
+
+ EventManager::Get()->Dispatch(&Event::ChanRegistered::OnChanRegistered, ci);
+ }
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ this->SendSyntax(source);
+ source.Reply(" ");
+ source.Reply(_("Registers a channel, which sets you as the founder and prevents other users from gaining unauthorized access in the channel."
+ " To use this command, you must first be a channel operator on \037channel\037."
+ " The description, which is optional, is a general description of the channel's purpose.\n"
+ "\n"
+ "When you register a channel, you are recorded as the founder of the channel."
+ " The channel founder is allowed to change all of the channel settings for the channel,"
+ " and will automatically be given channel operator status when entering the channel."));
+
+ ServiceBot *bi;
+ Anope::string cmd;
+ CommandInfo *help = source.service->FindCommand("generic/help");
+ if (Command::FindCommandFromService("chanserv/access", bi, cmd) && help)
+ source.Reply(_("\n"
+ "See the \002{0}\002 command (\002{1}{2} {3} {0}\002) for information on giving a subset of these privileges to other users."),
+ cmd, Config->StrictPrivmsg, bi->nick, help->cname);
+
+ return true;
+ }
+};
+
+
+class CSRegister : public Module
+{
+ CommandCSRegister commandcsregister;
+
+ public:
+ CSRegister(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandcsregister(this)
+ {
+ }
+};
+
+MODULE_INIT(CSRegister)
diff --git a/modules/commands/cs_seen.cpp b/modules/chanserv/seen.cpp
index 80bc8e548..fc2d92597 100644
--- a/modules/commands/cs_seen.cpp
+++ b/modules/chanserv/seen.cpp
@@ -1,14 +1,24 @@
-/* cs_seen: provides a seen command by tracking all users
+/*
+ * Anope IRC Services
*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2003-2011 Anope Team <team@anope.org>
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
*
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
+#warning "this is disabled"
+#if 0
#include "module.h"
enum TypeInfo
@@ -22,7 +32,7 @@ static SeenInfo *FindInfo(const Anope::string &nick);
typedef Anope::hash_map<SeenInfo *> database_map;
database_map database;
-struct SeenInfo : Serializable
+struct SeenInfo : Serialize::Object
{
Anope::string nick;
Anope::string vhost;
@@ -32,7 +42,7 @@ struct SeenInfo : Serializable
Anope::string message; // for part/kick/quit
time_t last; // the time when the user was last seen
- SeenInfo() : Serializable("SeenInfo")
+ SeenInfo() : Serialize::Object("SeenInfo")
{
}
@@ -43,7 +53,8 @@ struct SeenInfo : Serializable
database.erase(iter);
}
- void Serialize(Serialize::Data &data) const anope_override
+#if 0
+ void Serialize(Serialize::Data &data) const override
{
data["nick"] << nick;
data["vhost"] << vhost;
@@ -57,7 +68,7 @@ struct SeenInfo : Serializable
static Serializable* Unserialize(Serializable *obj, Serialize::Data &data)
{
Anope::string snick;
-
+
data["nick"] >> snick;
SeenInfo *s;
@@ -85,6 +96,7 @@ struct SeenInfo : Serializable
database[s->nick] = s;
return s;
}
+#endif
};
static SeenInfo *FindInfo(const Anope::string &nick)
@@ -98,7 +110,7 @@ static SeenInfo *FindInfo(const Anope::string &nick)
static bool ShouldHide(const Anope::string &channel, User *u)
{
Channel *targetchan = Channel::Find(channel);
- const ChannelInfo *targetchan_ci = targetchan ? *targetchan->ci : ChannelInfo::Find(channel);
+ const ChanServ::Channel *targetchan_ci = targetchan ? *targetchan->ci : ChanServ::Find(channel);
if (targetchan && targetchan->HasMode("SECRET"))
return true;
@@ -119,7 +131,7 @@ class CommandOSSeen : public Command
this->SetSyntax(_("CLEAR \037time\037"));
}
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
{
if (params[0].equals_ci("STATS"))
{
@@ -165,8 +177,17 @@ class CommandOSSeen : public Command
this->SendSyntax(source);
}
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
{
+<<<<<<< HEAD
+ source.Reply(_("The \002STATS\002 command prints out statistics about stored nicks and memory usage.\n"
+ "The \002CLEAR\002 command lets you clean the database by removing all entries from the entries from the database that were added within \037time\037.\n"
+ "\n"
+ "Example:\n"
+ " {0} CLEAR 30m\n"
+ " Will remove all entries that were added within the last 30 minutes."),
+ source.command);
+=======
this->SendSyntax(source);
source.Reply(" ");
source.Reply(_("The \002STATS\002 command prints out statistics about stored nicks and memory usage."));
@@ -176,6 +197,7 @@ class CommandOSSeen : public Command
"Example:\n"
" %s CLEAR 30m\n"
" Will remove all entries that were added within the last 30 minutes."), source.command.c_str());
+>>>>>>> 2.0
return true;
}
};
@@ -191,24 +213,24 @@ class CommandSeen : public Command
return;
}
- BotInfo *bi = BotInfo::Find(params[0], true);
+ ServiceBot *bi = ServiceBot::Find(params[0], true);
if (bi)
{
- if (bi == source.c->ci->bi)
+ if (bi == source.c->ci->GetBot())
source.Reply(_("You found me, %s!"), source.GetNick().c_str());
else
source.Reply(_("%s is a network service."), bi->nick.c_str());
return;
}
- NickAlias *na = NickAlias::Find(params[0]);
+ NickServ::Nick *na = NickServ::FindNick(params[0]);
if (!na)
{
source.Reply(_("I don't know who %s is."), params[0].c_str());
return;
}
- if (source.GetAccount() == na->nc)
+ if (source.GetAccount() == na->GetAccount())
{
source.Reply(_("Looking for yourself, eh %s?"), source.GetNick().c_str());
return;
@@ -227,32 +249,27 @@ class CommandSeen : public Command
ChanUserContainer *uc = it->second;
User *u = uc->user;
- if (u->Account() == na->nc)
+ if (u->Account() == na->GetAccount())
{
source.Reply(_("%s is on the channel right now (as %s)!"), params[0].c_str(), u->nick.c_str());
return;
}
}
- AccessGroup ag = source.c->ci->AccessFor(na->nc);
+ ChanServ::AccessGroup ag = source.c->ci->AccessFor(na->GetAccount());
time_t last = 0;
- for (unsigned int i = 0; i < ag.paths.size(); ++i)
+ for (unsigned i = 0; i < ag.size(); ++i)
{
- ChanAccess::Path &p = ag.paths[i];
-
- if (p.empty())
- continue;
-
- ChanAccess *a = p[p.size() - 1];
+ ChanServ::ChanAccess *a = ag[i];
- if (a->GetAccount() == na->nc && a->last_seen > last)
- last = a->last_seen;
+ if (a->GetAccount() == na->GetAccount() && a->GetLastSeen() > last)
+ last = a->GetLastSeen();
}
if (last > Anope::CurTime || !last)
- source.Reply(_("I've never seen %s on this channel."), na->nick.c_str());
+ source.Reply(_("I've never seen %s on this channel."), na->GetNick().c_str());
else
- source.Reply(_("%s was last seen here %s ago."), na->nick.c_str(), Anope::Duration(Anope::CurTime - last, source.GetAccount()).c_str());
+ source.Reply(_("%s was last seen here %s ago."), na->GetNick().c_str(), Anope::Duration(Anope::CurTime - last, source.GetAccount()).c_str());
}
public:
@@ -262,7 +279,7 @@ class CommandSeen : public Command
this->SetSyntax(_("\037nick\037"));
}
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
{
const Anope::string &target = params[0];
@@ -271,26 +288,26 @@ class CommandSeen : public Command
if (target.length() > Config->GetBlock("networkinfo")->Get<unsigned>("nicklen"))
{
- source.Reply(_("Nick too long, max length is %u characters."), Config->GetBlock("networkinfo")->Get<unsigned>("nicklen"));
+ source.Reply(_("Nick too long, max length is \002{0}\002 characters."), Config->GetBlock("networkinfo")->Get<unsigned>("nicklen"));
return;
}
- if (BotInfo::Find(target, true) != NULL)
+ if (ServiceBot::Find(target, true) != NULL)
{
- source.Reply(_("%s is a client on services."), target.c_str());
+ source.Reply(_("\002{0}\002 is a service bot."), target);
return;
}
if (target.equals_ci(source.GetNick()))
{
- source.Reply(_("You might see yourself in the mirror, %s."), source.GetNick().c_str());
+ source.Reply(_("You might see yourself in the mirror, \002{0}\002."), source.GetNick());
return;
}
SeenInfo *info = FindInfo(target);
if (!info)
{
- source.Reply(_("Sorry, I have not seen %s."), target.c_str());
+ source.Reply(_("Sorry, I have not seen \002{0}\002."), target);
return;
}
@@ -306,8 +323,8 @@ class CommandSeen : public Command
if (info->type == NEW)
{
- source.Reply(_("%s (%s) was last seen connecting %s ago (%s)%s"),
- target.c_str(), info->vhost.c_str(), timebuf.c_str(), timebuf2.c_str(), onlinestatus.c_str());
+ source.Reply(_("\002{0}\002 (\002{1}\002) was last seen connecting \002{2}\002 ago (\002{3}\002){4}"),
+ target, info->vhost, timebuf, timebuf2, onlinestatus);
}
else if (info->type == NICK_TO)
{
@@ -317,75 +334,82 @@ class CommandSeen : public Command
else
onlinestatus = Anope::printf(_(", but %s mysteriously dematerialized."), info->nick2.c_str());
- source.Reply(_("%s (%s) was last seen changing nick to %s %s ago%s"),
- target.c_str(), info->vhost.c_str(), info->nick2.c_str(), timebuf.c_str(), onlinestatus.c_str());
+ source.Reply(_("\002{0}\002 (\002{1}\002) was last seen changing nick to \002{2}\002 \002{3}\002 ago{4}"),
+ target, info->vhost, info->nick2, timebuf, onlinestatus);
}
else if (info->type == NICK_FROM)
{
- source.Reply(_("%s (%s) was last seen changing nick from %s to %s %s ago%s"),
- target.c_str(), info->vhost.c_str(), info->nick2.c_str(), target.c_str(), timebuf.c_str(), onlinestatus.c_str());
+ source.Reply(_("\002{0}\002 (\002{1}\002) was last seen changing nick from \002{2}\002 to \002{3} {4}\002 ago{5}"),
+ target, info->vhost, info->nick2, target, timebuf, onlinestatus);
}
else if (info->type == JOIN)
{
if (ShouldHide(info->channel, u2))
- source.Reply(_("%s (%s) was last seen joining a secret channel %s ago%s"),
- target.c_str(), info->vhost.c_str(), timebuf.c_str(), onlinestatus.c_str());
+ source.Reply(_("\002{0}\002 (\002{1}\002) was last seen joining a secret channel \002{2}\002 ago{3}"),
+ target, info->vhost, timebuf, onlinestatus);
else
- source.Reply(_("%s (%s) was last seen joining %s %s ago%s"),
- target.c_str(), info->vhost.c_str(), info->channel.c_str(), timebuf.c_str(), onlinestatus.c_str());
+ source.Reply(_("\002{0}\002 (\002{1}\002) was last seen joining \002{2} {3}\002 ago{4}"),
+ target, info->vhost, info->channel, timebuf, onlinestatus);
}
else if (info->type == PART)
{
if (ShouldHide(info->channel, u2))
- source.Reply(_("%s (%s) was last seen parting a secret channel %s ago%s"),
- target.c_str(), info->vhost.c_str(), timebuf.c_str(), onlinestatus.c_str());
+ source.Reply(_("\002{0}\002 (\002{1}\002) was last seen parting a secret channel \002{2}\002 ago{3}"),
+ target, info->vhost, timebuf, onlinestatus);
else
- source.Reply(_("%s (%s) was last seen parting %s %s ago%s"),
- target.c_str(), info->vhost.c_str(), info->channel.c_str(), timebuf.c_str(), onlinestatus.c_str());
+ source.Reply(_("\002{0}\002 (\002{1}\002) was last seen parting \002{2} {3}\002 ago{4}"),
+ target, info->vhost, info->channel, timebuf, onlinestatus);
}
else if (info->type == QUIT)
{
- source.Reply(_("%s (%s) was last seen quitting (%s) %s ago (%s)."),
- target.c_str(), info->vhost.c_str(), info->message.c_str(), timebuf.c_str(), timebuf2.c_str());
+ source.Reply(_("\002{0}\002 (\002{1}\002) was last seen quitting (\002{2}\002) \002{3}\002 ago (\002{4}\\2)."),
+ target, info->vhost, info->message, timebuf, timebuf2);
}
else if (info->type == KICK)
{
if (ShouldHide(info->channel, u2))
- source.Reply(_("%s (%s) was kicked from a secret channel %s ago%s"),
- target.c_str(), info->vhost.c_str(), timebuf.c_str(), onlinestatus.c_str());
+ source.Reply(_("\002{0}\002 (\002{1}\002) was kicked from a secret channel \002{2}\002 ago{3}"),
+ target, info->vhost, timebuf, onlinestatus);
else
- source.Reply(_("%s (%s) was kicked from %s (\"%s\") %s ago%s"),
- target.c_str(), info->vhost.c_str(), info->channel.c_str(), info->message.c_str(), timebuf.c_str(), onlinestatus.c_str());
+ source.Reply(_("\002{0}\002 (\002{1}\002) was kicked from \002{2}\002 (\"{3}\") {4} ago{5}"),
+ target, info->vhost, info->channel, info->message, timebuf, onlinestatus);
}
}
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
{
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Checks for the last time \037nick\037 was seen joining, leaving,\n"
- "or changing nick on the network and tells you when and, depending\n"
- "on channel or user settings, where it was."));
+ source.Reply(_("Checks for the last time \037nick\037 was seen joining, leaving, or changing nick on the network and tells you when and, depending on channel or user settings, where it was."));
return true;
}
};
class CSSeen : public Module
+ , public EventHook<Event::ExpireTick>
+ , public EventHook<Event::UserConnect>
+ , public EventHook<Event::UserNickChange>
+ , public EventHook<Event::UserQuit>
+ , public EventHook<Event::JoinChannel>
+ , public EventHook<Event::PartChannel>
+ , public EventHook<Event::PreUserKicked>
{
- Serialize::Type seeninfo_type;
+ //Serialize::TypeBase seeninfo_type;
CommandSeen commandseen;
CommandOSSeen commandosseen;
+
public:
- CSSeen(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), seeninfo_type("SeenInfo", SeenInfo::Unserialize), commandseen(this), commandosseen(this)
+ CSSeen(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ // , seeninfo_type("SeenInfo", SeenInfo::Unserialize)
+ , commandseen(this)
+ , commandosseen(this)
{
}
- void OnReload(Configuration::Conf *conf) anope_override
+ void OnReload(Configuration::Conf *conf) override
{
simple = conf->GetModule(this)->Get<bool>("simple");
}
- void OnExpireTick() anope_override
+ void OnExpireTick() override
{
size_t previous_size = database.size();
time_t purgetime = Config->GetModule(this)->Get<time_t>("purgetime");
@@ -405,36 +429,37 @@ class CSSeen : public Module
Log(LOG_DEBUG) << "cs_seen: Purged database, checked " << previous_size << " nicks and removed " << (previous_size - database.size()) << " old entries.";
}
- void OnUserConnect(User *u, bool &exempt) anope_override
+ void OnUserConnect(User *u, bool &exempt) override
{
if (!u->Quitting())
UpdateUser(u, NEW, u->nick, "", "", "");
}
- void OnUserNickChange(User *u, const Anope::string &oldnick) anope_override
+ void OnUserNickChange(User *u, const Anope::string &oldnick) override
{
UpdateUser(u, NICK_TO, oldnick, u->nick, "", "");
UpdateUser(u, NICK_FROM, u->nick, oldnick, "", "");
}
- void OnUserQuit(User *u, const Anope::string &msg) anope_override
+ void OnUserQuit(User *u, const Anope::string &msg) override
{
UpdateUser(u, QUIT, u->nick, "", "", msg);
}
- void OnJoinChannel(User *u, Channel *c) anope_override
+ void OnJoinChannel(User *u, Channel *c) override
{
UpdateUser(u, JOIN, u->nick, "", c->name, "");
}
- void OnPartChannel(User *u, Channel *c, const Anope::string &channel, const Anope::string &msg) anope_override
+ void OnPartChannel(User *u, Channel *c, const Anope::string &channel, const Anope::string &msg) override
{
UpdateUser(u, PART, u->nick, "", channel, msg);
}
- void OnPreUserKicked(const MessageSource &source, ChanUserContainer *cu, const Anope::string &msg) anope_override
+ EventReturn OnPreUserKicked(const MessageSource &source, ChanUserContainer *cu, const Anope::string &msg) override
{
UpdateUser(cu->user, KICK, cu->user->nick, source.GetSource(), cu->chan->name, msg);
+ return EVENT_CONTINUE;
}
private:
@@ -457,3 +482,4 @@ class CSSeen : public Module
};
MODULE_INIT(CSSeen)
+#endif
diff --git a/modules/chanserv/set.cpp b/modules/chanserv/set.cpp
new file mode 100644
index 000000000..0d3e6e77e
--- /dev/null
+++ b/modules/chanserv/set.cpp
@@ -0,0 +1,1291 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/chanserv/mode.h"
+#include "modules/chanserv.h"
+#include "modules/chanserv/info.h"
+#include "modules/chanserv/set.h"
+
+class CommandCSSet : public Command
+{
+ ServiceReference<ModeLocks> mlocks;
+
+ public:
+ CommandCSSet(Module *creator) : Command(creator, "chanserv/set", 2, 3)
+ {
+ this->SetDesc(_("Set channel options and information"));
+ this->SetSyntax(_("\037option\037 \037channel\037 \037parameters\037"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ this->OnSyntaxError(source, "");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ this->SendSyntax(source);
+ source.Reply(" ");
+ source.Reply(_("Allows the channel founder to set various channel options and other information.\n"
+ "\n"
+ "Available options:"));
+ Anope::string this_name = source.command;
+ bool hide_privileged_commands = Config->GetBlock("options")->Get<bool>("hideprivilegedcommands"),
+ hide_registered_commands = Config->GetBlock("options")->Get<bool>("hideregisteredcommands");
+ for (CommandInfo::map::const_iterator it = source.service->commands.begin(), it_end = source.service->commands.end(); it != it_end; ++it)
+ {
+ const Anope::string &c_name = it->first;
+ const CommandInfo &info = it->second;
+ if (c_name.find_ci(this_name + " ") == 0)
+ {
+ ServiceReference<Command> c(info.name);
+
+ // XXX dup
+ if (!c)
+ continue;
+ else if (hide_registered_commands && !c->AllowUnregistered() && !source.GetAccount())
+ continue;
+ else if (hide_privileged_commands && !info.permission.empty() && !source.HasCommand(info.permission))
+ continue;
+
+ source.command = it->first;
+ c->OnServHelp(source);
+ }
+ }
+
+ CommandInfo *help = source.service->FindCommand("generic/help");
+ if (help)
+ source.Reply(_("Type \002{0}{1} {2} {3} \037option\037\002 for more information on a particular option."),
+ Config->StrictPrivmsg, source.service->nick, help->cname, this_name);
+
+ return true;
+ }
+};
+
+class CommandCSSetAutoOp : public Command
+{
+ public:
+ CommandCSSetAutoOp(Module *creator, const Anope::string &cname = "chanserv/set/autoop") : Command(creator, cname, 2, 2)
+ {
+ this->SetDesc(_("Should services automatically give status to users"));
+ this->SetSyntax(_("\037channel\037 {ON | OFF}"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &chan = params[0];
+
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ ChanServ::Channel *ci = ChanServ::Find(chan);
+ if (ci == NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
+ return;
+ }
+
+ EventReturn MOD_RESULT = EventManager::Get()->Dispatch(&Event::SetChannelOption::OnSetChannelOption, source, this, ci, params[1]);
+ if (MOD_RESULT == EVENT_STOP)
+ return;
+
+ if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "SET", ci->GetName());
+ return;
+ }
+
+ if (params[1].equals_ci("ON"))
+ {
+ Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable autoop";
+ ci->UnsetS<bool>("NOAUTOOP");
+ source.Reply(_("Services will now automatically give modes to users in \002{0}\002."), ci->GetName());
+ }
+ else if (params[1].equals_ci("OFF"))
+ {
+ Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable autoop";
+ ci->SetS<bool>("NOAUTOOP", true);
+ source.Reply(_("Services will no longer automatically give modes to users in \002{0}\002."), ci->GetName());
+ }
+ else
+ this->OnSyntaxError(source, "AUTOOP");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
+ {
+ source.Reply(_("Enables or disables AUTOOP for a \037channel\037. When disabled, users who join \037channel\037 will not automatically gain any status from {0}."), source.service->nick);
+ return true;
+ }
+};
+
+class CommandCSSetBanType : public Command
+{
+ public:
+ CommandCSSetBanType(Module *creator, const Anope::string &cname = "chanserv/set/bantype") : Command(creator, cname, 2, 2)
+ {
+ this->SetDesc(_("Set how Services make bans on the channel"));
+ this->SetSyntax(_("\037channel\037 \037bantype\037"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &chan = params[0];
+
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ ChanServ::Channel *ci = ChanServ::Find(chan);
+ if (ci == NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
+ return;
+ }
+
+ EventReturn MOD_RESULT;
+ MOD_RESULT = EventManager::Get()->Dispatch(&Event::SetChannelOption::OnSetChannelOption, source, this, ci, params[1]);
+ if (MOD_RESULT == EVENT_STOP)
+ return;
+
+ if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "SET", ci->GetName());
+ return;
+ }
+
+ try
+ {
+ int16_t new_type = convertTo<int16_t>(params[1]);
+ if (new_type < 0 || new_type > 3)
+ throw ConvertException("Invalid range");
+ Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to change the ban type to " << new_type;
+ ci->SetBanType(new_type);
+ source.Reply(_("Ban type for channel \002{0}\002 is now \002#{1}\002."), ci->GetName(), new_type);
+ }
+ catch (const ConvertException &)
+ {
+ source.Reply(_("\002{0}\002 is not a valid ban type."), params[1]);
+ }
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
+ {
+ source.Reply(_("Sets the ban type that will be used by services whenever they need to ban someone from your channel.\n"
+ "\n"
+ "Bantype is a number between 0 and 3 that means:\n"
+ "\n"
+ "0: ban in the form *!user@host\n"
+ "1: ban in the form *!*user@host\n"
+ "2: ban in the form *!*@host\n"
+ "3: ban in the form *!*user@*.domain"));
+ return true;
+ }
+};
+
+class CommandCSSetDescription : public Command
+{
+ public:
+ CommandCSSetDescription(Module *creator, const Anope::string &cname = "chanserv/set/description") : Command(creator, cname, 1, 2)
+ {
+ this->SetDesc(_("Set the channel description"));
+ this->SetSyntax(_("\037channel\037 [\037description\037]"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &chan = params[0];
+ const Anope::string &param = params.size() > 1 ? params[1] : "";
+
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ ChanServ::Channel *ci = ChanServ::Find(chan);
+ if (ci == NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
+ return;
+ }
+
+ EventReturn MOD_RESULT;
+ MOD_RESULT = EventManager::Get()->Dispatch(&Event::SetChannelOption::OnSetChannelOption, source, this, ci, param);
+ if (MOD_RESULT == EVENT_STOP)
+ return;
+
+ if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "SET", ci->GetName());
+ return;
+ }
+
+ ci->SetDesc(param);
+ if (!param.empty())
+ {
+ Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to change the description to " << ci->GetDesc();
+ source.Reply(_("Description of \002{0}\002 changed to \002{1}\002."), ci->GetName(), ci->GetDesc());
+ }
+ else
+ {
+ Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to unset the description";
+ source.Reply(_("Description of \002{0}\002 unset."), ci->GetName());
+ }
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
+ {
+ source.Reply(_("Sets the description for the channel, which shows up with the \002LIST\002 and \002INFO\002 commands."));
+ return true;
+ }
+};
+
+class CommandCSSetFounder : public Command
+{
+ public:
+ CommandCSSetFounder(Module *creator, const Anope::string &cname = "chanserv/set/founder") : Command(creator, cname, 2, 2)
+ {
+ this->SetDesc(_("Set the founder of a channel"));
+ this->SetSyntax(_("\037channel\037 \037user\037"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &chan = params[0];
+ const Anope::string &param = params[1];
+
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ ChanServ::Channel *ci = ChanServ::Find(chan);
+ if (ci == NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
+ return;
+ }
+
+ EventReturn MOD_RESULT;
+ MOD_RESULT = EventManager::Get()->Dispatch(&Event::SetChannelOption::OnSetChannelOption, source, this, ci, param);
+ if (MOD_RESULT == EVENT_STOP)
+ return;
+
+ if (MOD_RESULT != EVENT_ALLOW && (ci->HasFieldS("SECUREFOUNDER") ? !source.IsFounder(ci) : !source.AccessFor(ci).HasPriv("FOUNDER")) && source.permission.empty() && !source.HasPriv("chanserv/administration"))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "FOUNDER", ci->GetName());
+ return;
+ }
+
+ NickServ::Nick *na = NickServ::FindNick(param);
+ if (!na)
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), param);
+ return;
+ }
+
+ NickServ::Account *nc = na->GetAccount();
+ unsigned max_reg = Config->GetModule("chanserv")->Get<unsigned>("maxregistered");
+ if (max_reg && nc->GetChannelCount() >= max_reg && !source.HasPriv("chanserv/no-register-limit"))
+ {
+ source.Reply(_("\002{0}\002 has too many channels registered."), na->GetNick());
+ return;
+ }
+
+ Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to change the founder from " << (ci->GetFounder() ? ci->GetFounder()->GetDisplay() : "(none)") << " to " << nc->GetDisplay();
+
+ ci->SetFounder(nc);
+
+ source.Reply(_("Founder of \002{0}\002 changed to \002{1}\002."), ci->GetName(), na->GetNick());
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
+ {
+ source.Reply(_("Changes the founder of \037channel\037 to \037user\037. Using this command will cause you to lose your founder access to \037channel\037, and cannot be undone."
+ "\n"
+ "Use of this command requires being the founder or \037channel\037 or having the \002{0}\002 privilege, if secure founder is enabled or not, respectively."));
+ return true;
+ }
+};
+
+class CommandCSSetKeepModes : public Command
+{
+ public:
+ CommandCSSetKeepModes(Module *creator, const Anope::string &cname = "chanserv/set/keepmodes") : Command(creator, cname, 2, 2)
+ {
+ this->SetDesc(_("Retain modes when channel is not in use"));
+ this->SetSyntax(_("\037channel\037 {ON | OFF}"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &chan = params[0];
+ const Anope::string &param = params[1];
+
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ ChanServ::Channel *ci = ChanServ::Find(chan);
+ if (ci == NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
+ return;
+ }
+
+ EventReturn MOD_RESULT;
+ MOD_RESULT = EventManager::Get()->Dispatch(&Event::SetChannelOption::OnSetChannelOption, source, this, ci, param);
+ if (MOD_RESULT == EVENT_STOP)
+ return;
+
+ if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "SET", ci->GetName());
+ return;
+ }
+
+ if (param.equals_ci("ON"))
+ {
+ Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable keep modes";
+ ci->SetS<bool>("CS_KEEP_MODES", true);
+ source.Reply(_("Keep modes for \002{0}\002 is now \002on\002."), ci->GetName());
+ if (ci->c)
+ for (const std::pair<Anope::string, Anope::string> &p : ci->c->GetModes())
+ {
+ ChanServ::Mode *mode = Serialize::New<ChanServ::Mode *>();
+ mode->SetChannel(ci);
+ mode->SetMode(p.first);
+ mode->SetParam(p.second);
+ }
+ }
+ else if (param.equals_ci("OFF"))
+ {
+ Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable keep modes";
+ ci->UnsetS<bool>("CS_KEEP_MODES");
+ source.Reply(_("Keep modes for \002{0}\002 is now \002off\002."), ci->GetName());
+ for (ChanServ::Mode *m : ci->GetRefs<ChanServ::Mode *>())
+ m->Delete();
+ }
+ else
+ this->OnSyntaxError(source, "KEEPMODES");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
+ {
+ source.Reply(_("Enables or disables keepmodes for \037channel\037. If keepmodes is enabled, services will remember modes set on the channel and re-set them the next time the channel is created."));
+ return true;
+ }
+};
+
+class CommandCSSetPeace : public Command
+{
+ public:
+ CommandCSSetPeace(Module *creator, const Anope::string &cname = "chanserv/set/peace") : Command(creator, cname, 2, 2)
+ {
+ this->SetDesc(_("Regulate the use of critical commands"));
+ this->SetSyntax(_("\037channel\037 {ON | OFF}"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &chan = params[0];
+ const Anope::string &param = params[1];
+
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ ChanServ::Channel *ci = ChanServ::Find(chan);
+ if (ci == NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
+ return;
+ }
+
+ EventReturn MOD_RESULT;
+ MOD_RESULT = EventManager::Get()->Dispatch(&Event::SetChannelOption::OnSetChannelOption, source, this, ci, param);
+ if (MOD_RESULT == EVENT_STOP)
+ return;
+
+ if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "SET", ci->GetName());
+ return;
+ }
+
+ if (param.equals_ci("ON"))
+ {
+ Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable peace";
+ ci->SetS<bool>("PEACE", true);
+ source.Reply(_("Peace option for \002{0}\002 is now \002on\002."), ci->GetName());
+ }
+ else if (param.equals_ci("OFF"))
+ {
+ Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable peace";
+ ci->UnsetS<bool>("PEACE");
+ source.Reply(_("Peace option for \002{0}\002 is now \002off\002."), ci->GetName());
+ }
+ else
+ this->OnSyntaxError(source, "PEACE");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
+ {
+ source.Reply(_("Enables or disables the \002peace\002 option for \037channel\037."
+ " When \002peace\002 is set, a user won't be able to kick, ban or remove a channel status of a user that has a level superior or equal to his via services."),
+ source.service->nick);
+ return true;
+ }
+};
+
+inline static Anope::string BotModes()
+{
+ return Config->GetModule("botserv")->Get<Anope::string>("botmodes",
+ Config->GetModule("chanserv")->Get<Anope::string>("botmodes", "o")
+ );
+}
+
+class CommandCSSetPersist : public Command
+{
+ ServiceReference<ModeLocks> mlocks;
+
+ public:
+ CommandCSSetPersist(Module *creator, const Anope::string &cname = "chanserv/set/persist") : Command(creator, cname, 2, 2)
+ {
+ this->SetDesc(_("Set the channel as permanent"));
+ this->SetSyntax(_("\037channel\037 {ON | OFF}"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &chan = params[0];
+ const Anope::string &param = params[1];
+
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ ChanServ::Channel *ci = ChanServ::Find(chan);
+ if (ci == NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
+ return;
+ }
+
+ EventReturn MOD_RESULT;
+ MOD_RESULT = EventManager::Get()->Dispatch(&Event::SetChannelOption::OnSetChannelOption, source, this, ci, param);
+ if (MOD_RESULT == EVENT_STOP)
+ return;
+
+ if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "SET", ci->GetName());
+ return;
+ }
+
+ ChannelMode *cm = ModeManager::FindChannelModeByName("PERM");
+
+ if (params[1].equals_ci("ON"))
+ {
+ if (!ci->HasFieldS("PERSIST"))
+ {
+ ci->SetS<bool>("PERSIST", true);
+
+ /* Channel doesn't exist, create it */
+ if (!ci->c)
+ {
+ bool created;
+ Channel *c = Channel::FindOrCreate(ci->GetName(), created);
+ if (ci->GetBot())
+ {
+ ChannelStatus status(BotModes());
+ ci->GetBot()->Join(c, &status);
+ }
+ if (created)
+ c->Sync();
+ }
+
+ /* Set the perm mode */
+ if (cm)
+ {
+ if (ci->c && !ci->c->HasMode("PERM"))
+ ci->c->SetMode(NULL, cm);
+ /* Add it to the channels mlock */
+ if (mlocks)
+ mlocks->SetMLock(ci, cm, true, "", source.GetNick());
+ }
+ /* No botserv bot, no channel mode, give them ChanServ.
+ * Yes, this works fine with no BotServ.
+ */
+ else if (!ci->GetBot())
+ {
+ ServiceBot *ChanServ = Config->GetClient("ChanServ");
+ if (!ChanServ)
+ {
+ source.Reply(_("ChanServ is required to enable persist on this network."));
+ return;
+ }
+
+ ChanServ->Assign(NULL, ci);
+ if (!ci->c->FindUser(ChanServ))
+ {
+ ChannelStatus status(BotModes());
+ ChanServ->Join(ci->c, &status);
+ }
+ }
+ }
+
+ Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable persist";
+ source.Reply(_("Channel \002{0}\002 is now persistent."), ci->GetName());
+ }
+ else if (params[1].equals_ci("OFF"))
+ {
+ if (ci->HasFieldS("PERSIST"))
+ {
+ ci->UnsetS<bool>("PERSIST");
+
+ ServiceBot *ChanServ = Config->GetClient("ChanServ"),
+ *BotServ = Config->GetClient("BotServ");
+
+ /* Unset perm mode */
+ if (cm)
+ {
+ if (ci->c && ci->c->HasMode("PERM"))
+ ci->c->RemoveMode(NULL, cm);
+ /* Remove from mlock */
+ if (mlocks)
+ mlocks->RemoveMLock(ci, cm, true);
+ }
+ /* No channel mode, no BotServ, but using ChanServ as the botserv bot
+ * which was assigned when persist was set on
+ */
+ else if (!cm && !BotServ && ci->GetBot())
+ {
+ if (!ChanServ)
+ {
+ source.Reply(_("ChanServ is required to enable persist on this network."));
+ return;
+ }
+
+ /* Unassign bot */
+ ChanServ->UnAssign(NULL, ci);
+ }
+ }
+
+ Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable persist";
+ source.Reply(_("Channel \002{0}\002 is no longer persistent."), ci->GetName());
+ }
+ else
+ this->OnSyntaxError(source, "PERSIST");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
+ {
+ source.Reply(_("Enables or disables the persistent channel setting."
+ " When persistent is set, the service bot will remain in the channel when it has emptied of users."));
+ return true;
+ }
+};
+
+class CommandCSSetRestricted : public Command
+{
+ public:
+ CommandCSSetRestricted(Module *creator, const Anope::string &cname = "chanserv/set/restricted") : Command(creator, cname, 2, 2)
+ {
+ this->SetDesc(_("Restrict access to the channel"));
+ this->SetSyntax(_("\037channel\037 {ON | OFF}"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &chan = params[0];
+ const Anope::string &param = params[1];
+
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ ChanServ::Channel *ci = ChanServ::Find(chan);
+ if (ci == NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
+ return;
+ }
+
+ EventReturn MOD_RESULT;
+ MOD_RESULT = EventManager::Get()->Dispatch(&Event::SetChannelOption::OnSetChannelOption, source, this, ci, param);
+ if (MOD_RESULT == EVENT_STOP)
+ return;
+
+ if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "SET", ci->GetName());
+ return;
+ }
+
+ if (param.equals_ci("ON"))
+ {
+ Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable restricted";
+ ci->SetS<bool>("RESTRICTED", true);
+ source.Reply(_("Restricted access option for \002{0}\002 is now \002on\002."), ci->GetName());
+ }
+ else if (param.equals_ci("OFF"))
+ {
+ Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable restricted";
+ ci->UnsetS<bool>("RESTRICTED");
+ source.Reply(_("Restricted access option for \002{0}\002 is now \002off\002."), ci->GetName());
+ }
+ else
+ this->OnSyntaxError(source, "RESTRICTED");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
+ {
+ source.Reply(_("Enables or disables the \002restricted access\002 option for \037channel\037. When \002restricted access\002 is set, users not on the access list will not be permitted to join the channel."));
+ return true;
+ }
+};
+
+class CommandCSSetSecure : public Command
+{
+ public:
+ CommandCSSetSecure(Module *creator, const Anope::string &cname = "chanserv/set/secure") : Command(creator, cname, 2, 2)
+ {
+ this->SetDesc(_("Activate security features"));
+ this->SetSyntax(_("\037channel\037 {ON | OFF}"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &chan = params[0];
+ const Anope::string &param = params[1];
+
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ ChanServ::Channel *ci = ChanServ::Find(chan);
+ if (ci == NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
+ return;
+ }
+
+ EventReturn MOD_RESULT;
+ MOD_RESULT = EventManager::Get()->Dispatch(&Event::SetChannelOption::OnSetChannelOption, source, this, ci, param);
+ if (MOD_RESULT == EVENT_STOP)
+ return;
+
+ if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "SET", ci->GetName());
+ return;
+ }
+
+ if (param.equals_ci("ON"))
+ {
+ Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable secure";
+ ci->SetS<bool>("CS_SECURE", true);
+ source.Reply(_("Secure option for \002{0}\002 is now \002on\002."), ci->GetName());
+ }
+ else if (param.equals_ci("OFF"))
+ {
+ Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable secure";
+ ci->UnsetS<bool>("CS_SECURE");
+ source.Reply(_("Secure option for \002{0}\002 is now \002off\002."), ci->GetName());
+ }
+ else
+ this->OnSyntaxError(source, "SECURE");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
+ {
+ source.Reply(_("Enables or disables security features for a channel."
+ " When \002secure\002 is set, only users who have logged in (eg. not recognized based on their hostmask)"
+ " will be given access to channels from account-based access entries"));
+ return true;
+ }
+};
+
+class CommandCSSetSecureFounder : public Command
+{
+ public:
+ CommandCSSetSecureFounder(Module *creator, const Anope::string &cname = "chanserv/set/securefounder") : Command(creator, cname, 2, 2)
+ {
+ this->SetDesc(_("Stricter control of channel founder status"));
+ this->SetSyntax(_("\037channel\037 {ON | OFF}"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &chan = params[0];
+ const Anope::string &param = params[1];
+
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ ChanServ::Channel *ci = ChanServ::Find(chan);
+ if (ci == NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
+ return;
+ }
+
+ EventReturn MOD_RESULT;
+ MOD_RESULT = EventManager::Get()->Dispatch(&Event::SetChannelOption::OnSetChannelOption, source, this, ci, param);
+ if (MOD_RESULT == EVENT_STOP)
+ return;
+
+ if (MOD_RESULT != EVENT_ALLOW && (ci->HasFieldS("SECUREFOUNDER") ? !source.IsFounder(ci) : !source.AccessFor(ci).HasPriv("FOUNDER")) && source.permission.empty() && !source.HasPriv("chanserv/administration"))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "FOUNDER", ci->GetName());
+ return;
+ }
+
+ if (param.equals_ci("ON"))
+ {
+ Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable secure founder";
+ ci->SetS<bool>("SECUREFOUNDER", true);
+ source.Reply(_("Secure founder option for \002{0}\002 is now \002on\002."), ci->GetName());
+ }
+ else if (param.equals_ci("OFF"))
+ {
+ Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable secure founder";
+ ci->UnsetS<bool>("SECUREFOUNDER");
+ source.Reply(_("Secure founder option for \002{0}\002 is now \002off\002."), ci->GetName());
+ }
+ else
+ this->OnSyntaxError(source, "SECUREFOUNDER");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
+ {
+ this->SendSyntax(source);
+ source.Reply(" ");
+ source.Reply(_("Enables or disables the \002secure founder\002 option for a channel."
+ " When \002secure founder\002 is set, only the real founder will be able to drop the channel, change its founder, and change its successor."
+ " Otherwise, anyone with the \002{0}\002 privilege will be able to use these commands."),
+ "FOUNDER");
+ return true;
+ }
+};
+
+class CommandCSSetSecureOps : public Command
+{
+ public:
+ CommandCSSetSecureOps(Module *creator, const Anope::string &cname = "chanserv/set/secureops") : Command(creator, cname, 2, 2)
+ {
+ this->SetDesc(_("Stricter control of chanop status"));
+ this->SetSyntax(_("\037channel\037 {ON | OFF}"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &chan = params[0];
+ const Anope::string &param = params[1];
+
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ ChanServ::Channel *ci = ChanServ::Find(chan);
+ if (ci == NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
+ return;
+ }
+
+ EventReturn MOD_RESULT;
+ MOD_RESULT = EventManager::Get()->Dispatch(&Event::SetChannelOption::OnSetChannelOption, source, this, ci, param);
+ if (MOD_RESULT == EVENT_STOP)
+ return;
+
+ if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "SET", ci->GetName());
+ return;
+ }
+
+ if (param.equals_ci("ON"))
+ {
+ Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable secure ops";
+ ci->SetS<bool>("SECUREOPS", true);
+ source.Reply(_("Secure ops option for \002{0}\002 is now \002on\002."), ci->GetName());
+ }
+ else if (param.equals_ci("OFF"))
+ {
+ Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable secure ops";
+ ci->UnsetS<bool>("SECUREOPS");
+ source.Reply(_("Secure ops option for \002{0}\002 is now \002off\002."), ci->GetName());
+ }
+ else
+ this->OnSyntaxError(source, "SECUREOPS");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
+ {
+ source.Reply(_("Enables or disables the \002secure ops\002 option for \037channel\037."
+ " When \002secure ops\002 is set, users will not be allowed to have channel operator status if they do not have the privileges for it."));
+ return true;
+ }
+};
+
+class CommandCSSetSignKick : public Command
+{
+ public:
+ CommandCSSetSignKick(Module *creator, const Anope::string &cname = "chanserv/set/signkick") : Command(creator, cname, 2, 2)
+ {
+ this->SetDesc(_("Sign kicks that are done with the KICK command"));
+ this->SetSyntax(_("\037channel\037 {ON | LEVEL | OFF}"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &chan = params[0];
+ const Anope::string &param = params[1];
+
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ ChanServ::Channel *ci = ChanServ::Find(chan);
+ if (ci == NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
+ return;
+ }
+
+ EventReturn MOD_RESULT;
+ MOD_RESULT = EventManager::Get()->Dispatch(&Event::SetChannelOption::OnSetChannelOption, source, this, ci, param);
+ if (MOD_RESULT == EVENT_STOP)
+ return;
+
+ if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "SET", ci->GetName());
+ return;
+ }
+
+ if (param.equals_ci("ON"))
+ {
+ ci->SetS<bool>("SIGNKICK", true);
+ ci->UnsetS<bool>("SIGNKICK_LEVEL");
+ source.Reply(_("Signed kick option for \002{0}\002 is now \002on\002."), ci->GetName());
+ Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable sign kick";
+ }
+ else if (param.equals_ci("LEVEL"))
+ {
+ ci->SetS<bool>("SIGNKICK_LEVEL", true);
+ ci->UnsetS<bool>("SIGNKICK");
+ source.Reply(_("Signed kick option for \002{0}\002 is now \002on\002, but depends of the privileges of the user that is using the command."), ci->GetName());
+ Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable sign kick level";
+ }
+ else if (param.equals_ci("OFF"))
+ {
+ ci->UnsetS<bool>("SIGNKICK");
+ ci->UnsetS<bool>("SIGNKICK_LEVEL");
+ source.Reply(_("Signed kick option for \002{0}\002 is now \002off\002."), ci->GetName());
+ Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable sign kick";
+ }
+ else
+ this->OnSyntaxError(source, "SIGNKICK");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
+ {
+ source.Reply(_("Enables or disables \002signed kicks\002 for \037channel\037."
+ " When \002signed kicks\002 is enabled, kicks issued through services will have the nickname of the user who performed the kick included in the kick reason."
+ " If you use \002LEVEL\002 setting, then only users who do not have the \002{0}\002 privilege will have their kicks signed."),
+ "SIGNKICK");
+ return true;
+ }
+};
+
+class CommandCSSetSuccessor : public Command
+{
+ public:
+ CommandCSSetSuccessor(Module *creator, const Anope::string &cname = "chanserv/set/successor") : Command(creator, cname, 1, 2)
+ {
+ this->SetDesc(_("Set the successor for a channel"));
+ this->SetSyntax(_("\037channel\037 \037nick\037"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &chan = params[0];
+ const Anope::string &param = params[1];
+
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ ChanServ::Channel *ci = ChanServ::Find(chan);
+ if (ci == NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
+ return;
+ }
+
+ EventReturn MOD_RESULT;
+ MOD_RESULT = EventManager::Get()->Dispatch(&Event::SetChannelOption::OnSetChannelOption, source, this, ci, param);
+ if (MOD_RESULT == EVENT_STOP)
+ return;
+
+ if (MOD_RESULT != EVENT_ALLOW && (ci->HasFieldS("SECUREFOUNDER") ? !source.IsFounder(ci) : !source.AccessFor(ci).HasPriv("FOUNDER")) && source.permission.empty() && !source.HasPriv("chanserv/administration"))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "FOUNDER", ci->GetName());
+ return;
+ }
+
+ NickServ::Account *nc;
+
+ if (!param.empty())
+ {
+ NickServ::Nick *na = NickServ::FindNick(param);
+
+ if (!na)
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), param);
+ return;
+ }
+
+ if (na->GetAccount() == ci->GetFounder())
+ {
+ source.Reply(_("\002{0}\002 cannot be the successor of channel \002{1}\002 as they are the founder."), na->GetNick(), ci->GetName());
+ return;
+ }
+
+ nc = na->GetAccount();
+ }
+ else
+ nc = NULL;
+
+ Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to change the successor from " << (ci->GetSuccessor() ? ci->GetSuccessor()->GetDisplay() : "(none)") << " to " << (nc ? nc->GetDisplay() : "(none)");
+
+ ci->SetSuccessor(nc);
+
+ if (nc)
+ source.Reply(_("Successor for \002{0}\002 changed to \002{1}\002."), ci->GetName(), nc->GetDisplay());
+ else
+ source.Reply(_("Successor for \002{0}\002 unset."), ci->GetName());
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
+ {
+ source.Reply(_("Changes the successor of \037channel\037."
+ " The successor of a channel is automatically given ownership of the channel if the founder's account drops of expires."
+ " If the success has too many registered channels or there is no successor, the channel may instead be given to one of the users on the channel access list with the most privileges."));
+ return true;
+ }
+};
+
+class CommandCSSetNoexpire : public Command
+{
+ public:
+ CommandCSSetNoexpire(Module *creator) : Command(creator, "chanserv/saset/noexpire", 2, 2)
+ {
+ this->SetDesc(_("Prevent the channel from expiring"));
+ this->SetSyntax(_("\037channel\037 {ON | OFF}"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &chan = params[0];
+ const Anope::string &param = params[1];
+
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ ChanServ::Channel *ci = ChanServ::Find(chan);
+ if (ci == NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
+ return;
+ }
+
+ if (source.permission.empty() && !source.AccessFor(ci).HasPriv("SET"))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "SET", ci->GetName());
+ return;
+ }
+
+ if (param.equals_ci("ON"))
+ {
+ Log(LOG_ADMIN, source, this, ci) << "to enable noexpire";
+ ci->SetS<bool>("CS_NO_EXPIRE", true);
+ source.Reply(_("Channel \002{0} will not\002 expire."), ci->GetName());
+ }
+ else if (param.equals_ci("OFF"))
+ {
+ Log(LOG_ADMIN, source, this, ci) << "to disable noexpire";
+ ci->UnsetS<bool>("CS_NO_EXPIRE");
+ source.Reply(_("Channel \002{0} will\002 expire."), ci->GetName());
+ }
+ else
+ this->OnSyntaxError(source, "NOEXPIRE");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
+ {
+ source.Reply(_("Sets whether the given \037channel\037 will expire. Setting \002 noexpire\002 to \002on\002 prevents the channel from expiring."));
+ return true;
+ }
+};
+
+class CSSet : public Module
+ , public EventHook<Event::ChanRegistered>
+ , public EventHook<Event::ChannelSync>
+ , public EventHook<Event::CheckKick>
+ , public EventHook<Event::DelChan>
+ , public EventHook<Event::ChannelModeSet>
+ , public EventHook<Event::ChannelModeUnset>
+ , public EventHook<Event::JoinChannel>
+ , public EventHook<Event::SetCorrectModes>
+ , public EventHook<ChanServ::Event::PreChanExpire>
+ , public EventHook<Event::ChanInfo>
+{
+ Serialize::Field<ChanServ::Channel, bool> noautoop, peace, securefounder,
+ restricted, secure, secureops, signkick, signkick_level, noexpire, keep_modes, persist;
+
+ CommandCSSet commandcsset;
+ CommandCSSetAutoOp commandcssetautoop;
+ CommandCSSetBanType commandcssetbantype;
+ CommandCSSetDescription commandcssetdescription;
+ CommandCSSetFounder commandcssetfounder;
+ CommandCSSetKeepModes commandcssetkeepmodes;
+ CommandCSSetPeace commandcssetpeace;
+ CommandCSSetPersist commandcssetpersist;
+ CommandCSSetRestricted commandcssetrestricted;
+ CommandCSSetSecure commandcssetsecure;
+ CommandCSSetSecureFounder commandcssetsecurefounder;
+ CommandCSSetSecureOps commandcssetsecureops;
+ CommandCSSetSignKick commandcssetsignkick;
+ CommandCSSetSuccessor commandcssetsuccessor;
+ CommandCSSetNoexpire commandcssetnoexpire;
+
+ ExtensibleRef<bool> inhabit;
+
+ bool persist_lower_ts;
+
+ public:
+ CSSet(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , EventHook<Event::ChanRegistered>(this)
+ , EventHook<Event::ChannelSync>(this)
+ , EventHook<Event::CheckKick>(this)
+ , EventHook<Event::DelChan>(this)
+ , EventHook<Event::ChannelModeSet>(this)
+ , EventHook<Event::ChannelModeUnset>(this)
+ , EventHook<Event::JoinChannel>(this)
+ , EventHook<Event::SetCorrectModes>(this)
+ , EventHook<ChanServ::Event::PreChanExpire>(this)
+ , EventHook<Event::ChanInfo>(this)
+
+ , noautoop(this, "NOAUTOOP")
+ , peace(this, "PEACE")
+ , securefounder(this, "SECUREFOUNDER")
+ , restricted(this, "RESTRICTED")
+ , secure(this, "CS_SECURE")
+ , secureops(this, "SECUREOPS")
+ , signkick(this, "SIGNKICK")
+ , signkick_level(this, "SIGNKICK_LEVEL")
+ , noexpire(this, "CS_NO_EXPIRE")
+ , keep_modes(this, "CS_KEEP_MODES")
+ , persist(this, "PERSIST")
+
+ , commandcsset(this)
+ , commandcssetautoop(this)
+ , commandcssetbantype(this)
+ , commandcssetdescription(this)
+ , commandcssetfounder(this)
+ , commandcssetkeepmodes(this)
+ , commandcssetpeace(this)
+ , commandcssetpersist(this)
+ , commandcssetrestricted(this)
+ , commandcssetsecure(this)
+ , commandcssetsecurefounder(this)
+ , commandcssetsecureops(this)
+ , commandcssetsignkick(this)
+ , commandcssetsuccessor(this)
+ , commandcssetnoexpire(this)
+
+ , inhabit("inhabit")
+ {
+ }
+
+ void OnReload(Configuration::Conf *conf) override
+ {
+ persist_lower_ts = conf->GetModule(this)->Get<bool>("persist_lower_ts");
+ }
+
+ void OnChanRegistered(ChanServ::Channel *ci) override
+ {
+ ci->SetBanType(Config->GetModule(this)->Get<int>("defbantype", "2"));
+ }
+
+ void OnChannelSync(Channel *c) override
+ {
+ if (c->ci && keep_modes.HasExt(c->ci))
+ for (ChanServ::Mode *m : c->ci->GetRefs<ChanServ::Mode *>())
+ c->SetMode(c->ci->WhoSends(), m->GetMode(), m->GetParam());
+ }
+
+ EventReturn OnCheckKick(User *u, Channel *c, Anope::string &mask, Anope::string &reason) override
+ {
+ if (!c->ci || !restricted.HasExt(c->ci) || c->MatchesList(u, "EXCEPT"))
+ return EVENT_CONTINUE;
+
+ if (c->ci->AccessFor(u).empty() && (!c->ci->GetFounder() || u->Account() != c->ci->GetFounder()))
+ return EVENT_STOP;
+
+ return EVENT_CONTINUE;
+ }
+
+ void OnDelChan(ChanServ::Channel *ci) override
+ {
+ if (ci->c && persist.HasExt(ci))
+ ci->c->RemoveMode(ci->WhoSends(), "PERM", "", false);
+ persist.Unset(ci);
+ }
+
+ EventReturn OnChannelModeSet(Channel *c, const MessageSource &setter, ChannelMode *mode, const Anope::string &param) override
+ {
+ if (c->ci)
+ {
+ /* Channel mode +P or so was set, mark this channel as persistent */
+ if (mode->name == "PERM")
+ persist.Set(c->ci, true);
+
+ if (mode->type != MODE_STATUS && !c->syncing && Me->IsSynced() && (!inhabit || !inhabit->HasExt(c)))
+ {
+ ChanServ::Mode *m = Serialize::New<ChanServ::Mode *>();
+ if (m != nullptr)
+ {
+ m->SetChannel(c->ci);
+ m->SetMode(mode->name);
+ m->SetParam(param);
+ }
+ }
+ }
+
+ return EVENT_CONTINUE;
+ }
+
+ EventReturn OnChannelModeUnset(Channel *c, const MessageSource &setter, ChannelMode *mode, const Anope::string &param) override
+ {
+ if (mode->name == "PERM")
+ {
+ if (c->ci)
+ persist.Unset(c->ci);
+ }
+
+ if (c->ci && mode->type != MODE_STATUS && !c->syncing && Me->IsSynced() && (!inhabit || !inhabit->HasExt(c)))
+ for (ChanServ::Mode *m : c->ci->GetRefs<ChanServ::Mode *>())
+ if (m->GetMode() == mode->name && m->GetParam().equals_ci(param))
+ m->Delete();
+
+ return EVENT_CONTINUE;
+ }
+
+ void OnJoinChannel(User *u, Channel *c) override
+ {
+ if (persist_lower_ts && c->ci && persist.HasExt(c->ci) && c->creation_time > c->ci->GetTimeRegistered())
+ {
+ Log(LOG_DEBUG) << "Changing TS of " << c->name << " from " << c->creation_time << " to " << c->ci->GetTimeRegistered();
+ c->creation_time = c->ci->GetTimeRegistered();
+ IRCD->SendChannel(c);
+ c->Reset();
+ }
+ }
+
+ void OnSetCorrectModes(User *user, Channel *chan, ChanServ::AccessGroup &access, bool &give_modes, bool &take_modes) override
+ {
+ if (chan->ci)
+ {
+ if (noautoop.HasExt(chan->ci))
+ give_modes = false;
+ if (secureops.HasExt(chan->ci))
+ // This overrides what chanserv does because it is loaded after chanserv
+ take_modes = true;
+ }
+ }
+
+ void OnPreChanExpire(ChanServ::Channel *ci, bool &expire) override
+ {
+ if (noexpire.HasExt(ci))
+ expire = false;
+ }
+
+ void OnChanInfo(CommandSource &source, ChanServ::Channel *ci, InfoFormatter &info, bool show_all) override
+ {
+ if (!show_all)
+ return;
+
+ if (peace.HasExt(ci))
+ info.AddOption(_("Peace"));
+ if (restricted.HasExt(ci))
+ info.AddOption(_("Restricted access"));
+ if (secure.HasExt(ci))
+ info.AddOption(_("Security"));
+ if (securefounder.HasExt(ci))
+ info.AddOption(_("Secure founder"));
+ if (secureops.HasExt(ci))
+ info.AddOption(_("Secure ops"));
+ if (signkick.HasExt(ci) || signkick_level.HasExt(ci))
+ info.AddOption(_("Signed kicks"));
+ if (persist.HasExt(ci))
+ info.AddOption(_("Persistent"));
+ if (noexpire.HasExt(ci))
+ info.AddOption(_("No expire"));
+ if (keep_modes.HasExt(ci))
+ info.AddOption(_("Keep modes"));
+ if (noautoop.HasExt(ci))
+ info.AddOption(_("No auto-op"));
+ }
+};
+
+MODULE_INIT(CSSet)
diff --git a/modules/chanserv/set_misc.cpp b/modules/chanserv/set_misc.cpp
new file mode 100644
index 000000000..b2c0dc50c
--- /dev/null
+++ b/modules/chanserv/set_misc.cpp
@@ -0,0 +1,227 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2010-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "module.h"
+#include "modules/chanserv/set_misc.h"
+#include "modules/chanserv/info.h"
+#include "modules/chanserv/set.h"
+
+static Anope::map<Anope::string> descriptions;
+
+class CSMiscDataImpl : public CSMiscData
+{
+ friend class CSMiscDataType;
+
+ ChanServ::Channel *channel = nullptr;
+ Anope::string name, data;
+
+ public:
+ CSMiscDataImpl(Serialize::TypeBase *type) : CSMiscData(type) { }
+ CSMiscDataImpl(Serialize::TypeBase *type, Serialize::ID id) : CSMiscData(type, id) { }
+
+ ChanServ::Channel *GetChannel() override;
+ void SetChannel(ChanServ::Channel *s) override;
+
+ Anope::string GetName() override;
+ void SetName(const Anope::string &n) override;
+
+ Anope::string GetData() override;
+ void SetData(const Anope::string &d) override;
+};
+
+class CSMiscDataType : public Serialize::Type<CSMiscDataImpl>
+{
+ public:
+ Serialize::ObjectField<CSMiscDataImpl, ChanServ::Channel *> owner;
+ Serialize::Field<CSMiscDataImpl, Anope::string> name, data;
+
+ CSMiscDataType(Module *me) : Serialize::Type<CSMiscDataImpl>(me)
+ , owner(this, "owner", &CSMiscDataImpl::channel, true)
+ , name(this, "name", &CSMiscDataImpl::name)
+ , data(this, "data", &CSMiscDataImpl::data)
+ {
+ }
+};
+
+ChanServ::Channel *CSMiscDataImpl::GetChannel()
+{
+ return Get(&CSMiscDataType::owner);
+}
+
+void CSMiscDataImpl::SetChannel(ChanServ::Channel *s)
+{
+ Set(&CSMiscDataType::owner, s);
+}
+
+Anope::string CSMiscDataImpl::GetName()
+{
+ return Get(&CSMiscDataType::name);
+}
+
+void CSMiscDataImpl::SetName(const Anope::string &n)
+{
+ Set(&CSMiscDataType::name, n);
+}
+
+Anope::string CSMiscDataImpl::GetData()
+{
+ return Get(&CSMiscDataType::data);
+}
+
+void CSMiscDataImpl::SetData(const Anope::string &d)
+{
+ Set(&CSMiscDataType::data, d);
+}
+
+class CommandCSSetMisc : public Command
+{
+ Anope::string GetAttribute(const Anope::string &command)
+ {
+ size_t sp = command.rfind(' ');
+ if (sp != Anope::string::npos)
+ return command.substr(sp + 1);
+ return command;
+ }
+
+ public:
+ CommandCSSetMisc(Module *creator, const Anope::string &cname = "chanserv/set/misc") : Command(creator, cname, 1, 2)
+ {
+ this->SetSyntax(_("\037channel\037 [\037parameters\037]"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &chan = params[0];
+ const Anope::string &param = params[1];
+
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ ChanServ::Channel *ci = ChanServ::Find(chan);
+ if (ci == NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
+ return;
+ }
+
+ EventReturn MOD_RESULT;
+ MOD_RESULT = EventManager::Get()->Dispatch(&Event::SetChannelOption::OnSetChannelOption, source, this, ci, param);
+ if (MOD_RESULT == EVENT_STOP)
+ return;
+
+ if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "SET", ci->GetName());
+ return;
+ }
+
+ Anope::string scommand = GetAttribute(source.command);
+
+ /* remove existing */
+ for (CSMiscData *data : ci->GetRefs<CSMiscData *>())
+ if (data->GetName() == scommand)
+ {
+ data->Delete();
+ break;
+ }
+
+ if (!param.empty())
+ {
+ CSMiscData *data = Serialize::New<CSMiscData *>();
+ data->SetChannel(ci);
+ data->SetName(scommand);
+ data->SetData(param);
+
+ Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to change it to " << param;
+ source.Reply(_("\002{0}\002 for \002{1}\002 set to \002{2}\002."), scommand, ci->GetName(), param);
+ }
+ else
+ {
+ Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to unset it";
+ source.Reply(_("\002{0}\002 for \002{1}\002 unset."), scommand, ci->GetName());
+ }
+ }
+
+ void OnServHelp(CommandSource &source) override
+ {
+ if (descriptions.count(source.command))
+ {
+ this->SetDesc(descriptions[source.command]);
+ Command::OnServHelp(source);
+ }
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ if (descriptions.count(source.command))
+ {
+ source.Reply("%s", Language::Translate(source.nc, descriptions[source.command].c_str()));
+ return true;
+ }
+ return false;
+ }
+};
+
+class CSSetMisc : public Module
+ , public EventHook<Event::ChanInfo>
+{
+ CommandCSSetMisc commandcssetmisc;
+ CSMiscDataType type;
+
+ public:
+ CSSetMisc(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , EventHook<Event::ChanInfo>(this)
+ , commandcssetmisc(this)
+ , type(this)
+ {
+ }
+
+ void OnReload(Configuration::Conf *conf) override
+ {
+ descriptions.clear();
+
+ for (int i = 0; i < conf->CountBlock("command"); ++i)
+ {
+ Configuration::Block *block = conf->GetBlock("command", i);
+
+ if (block->Get<Anope::string>("command") != "chanserv/set/misc")
+ continue;
+
+ Anope::string cname = block->Get<Anope::string>("name");
+ Anope::string desc = block->Get<Anope::string>("misc_description");
+
+ if (cname.empty() || desc.empty())
+ continue;
+
+ descriptions[cname] = desc;
+ }
+ }
+
+ void OnChanInfo(CommandSource &source, ChanServ::Channel *ci, InfoFormatter &info, bool) override
+ {
+ for (CSMiscData *data : ci->GetRefs<CSMiscData *>())
+ info[data->GetName()] = data->GetData();
+ }
+};
+
+MODULE_INIT(CSSetMisc)
diff --git a/modules/chanserv/status.cpp b/modules/chanserv/status.cpp
new file mode 100644
index 000000000..3e4b669d3
--- /dev/null
+++ b/modules/chanserv/status.cpp
@@ -0,0 +1,123 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/chanserv/akick.h"
+
+class CommandCSStatus : public Command
+{
+public:
+ CommandCSStatus(Module *creator) : Command(creator, "chanserv/status", 1, 2)
+ {
+ this->SetDesc(_("Find a user's status on a channel"));
+ this->SetSyntax(_("\037channel\037 [\037user\037]"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &channel = params[0];
+
+ ChanServ::Channel *ci = ChanServ::Find(channel);
+ if (ci == NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), channel);
+ return;
+ }
+
+ if (!source.AccessFor(ci).HasPriv("ACCESS_CHANGE") && !source.HasPriv("chanserv/auspex"))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "ACCESS_CHANGE", ci->GetName());
+ return;
+ }
+
+ Anope::string nick = source.GetNick();
+ if (params.size() > 1)
+ nick = params[1];
+
+ ChanServ::AccessGroup ag;
+ User *u = User::Find(nick, true);
+ NickServ::Nick *na = NULL;
+ if (u != NULL)
+ ag = ci->AccessFor(u);
+ else
+ {
+ na = NickServ::FindNick(nick);
+ if (na != NULL)
+ ag = ci->AccessFor(na->GetAccount());
+ }
+
+ if (ag.super_admin)
+ source.Reply(_("\002{0}\002 is a super administrator."), nick);
+ else if (ag.founder)
+ source.Reply(_("\002{0}\002 is the founder of \002{1}\002."), nick, ci->GetName());
+ else if (ag.empty())
+ source.Reply(_("\002{0}\002 has no access on \002{1}\002."), nick, ci->GetName());
+ else
+ {
+ source.Reply(_("Access for \002{0}\002 on \002{1}\002:"), nick, ci->GetName());
+
+ for (unsigned i = 0; i < ag.size(); ++i)
+ {
+ ChanServ::ChanAccess *acc = ag[i];
+
+ source.Reply(_("\002{0}\002 matches access entry \002{1}\002, which has privilege \002{2}\002."), nick, acc->Mask(), acc->AccessSerialize());
+ }
+ }
+
+ for (unsigned j = 0, end = ci->GetAkickCount(); j < end; ++j)
+ {
+ AutoKick *ak = ci->GetAkick(j);
+
+ if (ak->GetAccount())
+ {
+ if (na && ak->GetAccount() == na->GetAccount())
+ source.Reply(_("\002{0}\002 is on the auto kick list of \002{1}\002 ({2})."), na->GetAccount()->GetDisplay(), ci->GetName(), ak->GetReason());
+ }
+ else if (u != NULL)
+ {
+ Entry akick_mask("", ak->GetMask());
+ if (akick_mask.Matches(u))
+ source.Reply(_("\002{0}\002 matches auto kick entry \002{1}\002 on \002{2}\002 ({3})."), u->nick, ak->GetMask(), ci->GetName(), ak->GetReason());
+ }
+ }
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("This command tells you what access \037user\037 has on \037channel\037."
+ "It will also tell you which access and auto kick entries match \037user\037.\n"
+ "\n"
+ "Use of this command requires the \002{0}\002 privilege on \037channel\037."),
+ "ACCESS_CHANGE");
+ return true;
+ }
+};
+
+class CSStatus : public Module
+{
+ CommandCSStatus commandcsstatus;
+
+ public:
+ CSStatus(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandcsstatus(this)
+ {
+ }
+};
+
+MODULE_INIT(CSStatus)
diff --git a/modules/cs_statusupdate.cpp b/modules/chanserv/statusupdate.cpp
index ffdbec3e7..0d3a25d0f 100644
--- a/modules/cs_statusupdate.cpp
+++ b/modules/chanserv/statusupdate.cpp
@@ -1,32 +1,46 @@
/*
+ * Anope IRC Services
*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2011-2016 Anope Team <team@anope.org>
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "module.h"
class StatusUpdate : public Module
+ , public EventHook<Event::AccessAdd>
+ , public EventHook<Event::AccessDel>
{
public:
StatusUpdate(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , EventHook<Event::AccessAdd>(this)
+ , EventHook<Event::AccessDel>(this)
{
}
- void OnAccessAdd(ChannelInfo *ci, CommandSource &, ChanAccess *access) anope_override
+ void OnAccessAdd(ChanServ::Channel *ci, CommandSource &, ChanServ::ChanAccess *access) override
{
if (ci->c)
for (Channel::ChanUserList::iterator it = ci->c->users.begin(), it_end = ci->c->users.end(); it != it_end; ++it)
{
User *user = it->second->user;
- ChannelInfo *next;
- if (user->server != Me && access->Matches(user, user->Account(), next))
+ if (user->server != Me && access->Matches(user, user->Account()))
{
- AccessGroup ag = ci->AccessFor(user);
+ ChanServ::AccessGroup ag = ci->AccessFor(user);
for (unsigned i = 0; i < ModeManager::GetStatusChannelModesByRank().size(); ++i)
{
@@ -39,17 +53,17 @@ class StatusUpdate : public Module
}
}
- void OnAccessDel(ChannelInfo *ci, CommandSource &, ChanAccess *access) anope_override
+ // XXX this relies on the access entry already being removed from the list?
+ void OnAccessDel(ChanServ::Channel *ci, CommandSource &, ChanServ::ChanAccess *access) override
{
if (ci->c)
for (Channel::ChanUserList::iterator it = ci->c->users.begin(), it_end = ci->c->users.end(); it != it_end; ++it)
{
User *user = it->second->user;
- ChannelInfo *next;
- if (user->server != Me && access->Matches(user, user->Account(), next))
+ if (user->server != Me && access->Matches(user, user->Account()))
{
- AccessGroup ag = ci->AccessFor(user);
+ ChanServ::AccessGroup ag = ci->AccessFor(user);
for (unsigned i = 0; i < ModeManager::GetStatusChannelModesByRank().size(); ++i)
{
diff --git a/modules/chanserv/suspend.cpp b/modules/chanserv/suspend.cpp
new file mode 100644
index 000000000..c825c35f7
--- /dev/null
+++ b/modules/chanserv/suspend.cpp
@@ -0,0 +1,350 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/chanserv/suspend.h"
+#include "modules/chanserv/drop.h"
+#include "modules/chanserv.h"
+#include "modules/chanserv/info.h"
+
+class CSSuspendInfoImpl : public CSSuspendInfo
+{
+ friend class CSSuspendType;
+
+ ChanServ::Channel *channel = nullptr;
+ Anope::string by, reason;
+ time_t when = 0, expires = 0;
+
+ public:
+ CSSuspendInfoImpl(Serialize::TypeBase *type) : CSSuspendInfo(type) { }
+ CSSuspendInfoImpl(Serialize::TypeBase *type, Serialize::ID id) : CSSuspendInfo(type, id) { }
+
+ ChanServ::Channel *GetChannel() override;
+ void SetChannel(ChanServ::Channel *s) override;
+
+ Anope::string GetBy() override;
+ void SetBy(const Anope::string &by) override;
+
+ Anope::string GetReason() override;
+ void SetReason(const Anope::string &reason) override;
+
+ time_t GetWhen() override;
+ void SetWhen(const time_t &w) override;
+
+ time_t GetExpires() override;
+ void SetExpires(const time_t &e) override;
+};
+
+class CSSuspendType : public Serialize::Type<CSSuspendInfoImpl>
+{
+ public:
+ Serialize::ObjectField<CSSuspendInfoImpl, ChanServ::Channel *> channel;
+ Serialize::Field<CSSuspendInfoImpl, Anope::string> by, reason;
+ Serialize::Field<CSSuspendInfoImpl, time_t> when, expires;
+
+ CSSuspendType(Module *me) : Serialize::Type<CSSuspendInfoImpl>(me)
+ , channel(this, "chan", &CSSuspendInfoImpl::channel, true)
+ , by(this, "by", &CSSuspendInfoImpl::by)
+ , reason(this, "reason", &CSSuspendInfoImpl::reason)
+ , when(this, "time", &CSSuspendInfoImpl::when)
+ , expires(this, "expires", &CSSuspendInfoImpl::expires)
+ {
+ }
+};
+
+ChanServ::Channel *CSSuspendInfoImpl::GetChannel()
+{
+ return Get(&CSSuspendType::channel);
+}
+
+void CSSuspendInfoImpl::SetChannel(ChanServ::Channel *s)
+{
+ Set(&CSSuspendType::channel, s);
+}
+
+Anope::string CSSuspendInfoImpl::GetBy()
+{
+ return Get(&CSSuspendType::by);
+}
+
+void CSSuspendInfoImpl::SetBy(const Anope::string &by)
+{
+ Set(&CSSuspendType::by, by);
+}
+
+Anope::string CSSuspendInfoImpl::GetReason()
+{
+ return Get(&CSSuspendType::reason);
+}
+
+void CSSuspendInfoImpl::SetReason(const Anope::string &reason)
+{
+ Set(&CSSuspendType::reason, reason);
+}
+
+time_t CSSuspendInfoImpl::GetWhen()
+{
+ return Get(&CSSuspendType::when);
+}
+
+void CSSuspendInfoImpl::SetWhen(const time_t &w)
+{
+ Set(&CSSuspendType::when, w);
+}
+
+time_t CSSuspendInfoImpl::GetExpires()
+{
+ return Get(&CSSuspendType::expires);
+}
+
+void CSSuspendInfoImpl::SetExpires(const time_t &e)
+{
+ Set(&CSSuspendType::expires, e);
+}
+
+class CommandCSSuspend : public Command
+{
+ public:
+ CommandCSSuspend(Module *creator) : Command(creator, "chanserv/suspend", 2, 3)
+ {
+ this->SetDesc(_("Prevent a channel from being used preserving channel data and settings"));
+ this->SetSyntax(_("\037channel\037 [+\037expiry\037] [\037reason\037]"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &chan = params[0];
+ Anope::string expiry = params[1];
+ Anope::string reason = params.size() > 2 ? params[2] : "";
+ time_t expiry_secs = Config->GetModule(this->GetOwner())->Get<time_t>("expire");
+
+ if (!expiry.empty() && expiry[0] != '+')
+ {
+ reason = expiry + " " + reason;
+ reason.trim();
+ expiry.clear();
+ }
+ else
+ {
+ expiry_secs = Anope::DoTime(expiry);
+ if (expiry_secs == -1)
+ {
+ source.Reply(_("Invalid expiry time \002{0}\002."), expiry);
+ return;
+ }
+ }
+
+ if (Anope::ReadOnly)
+ source.Reply(_("Services are in read-only mode. Any changes made may not persist."));
+
+ ChanServ::Channel *ci = ChanServ::Find(chan);
+ if (ci == NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
+ return;
+ }
+
+ CSSuspendInfo *si = ci->GetRef<CSSuspendInfo *>();
+ if (si)
+ {
+ source.Reply(_("\002{0}\002 is already suspended."), ci->GetName());
+ return;
+ }
+
+ si = Serialize::New<CSSuspendInfo *>();
+ si->SetChannel(ci);
+ si->SetBy(source.GetNick());
+ si->SetReason(reason);
+ si->SetWhen(Anope::CurTime);
+ si->SetExpires(expiry_secs ? expiry_secs + Anope::CurTime : 0);
+
+ if (ci->c)
+ {
+ std::vector<User *> users;
+
+ for (Channel::ChanUserList::iterator it = ci->c->users.begin(), it_end = ci->c->users.end(); it != it_end; ++it)
+ {
+ ChanUserContainer *uc = it->second;
+ User *user = uc->user;
+ if (!user->HasMode("OPER") && user->server != Me)
+ users.push_back(user);
+ }
+
+ for (unsigned i = 0; i < users.size(); ++i)
+ ci->c->Kick(NULL, users[i], "%s", !reason.empty() ? reason.c_str() : Language::Translate(users[i], _("This channel has been suspended.")));
+ }
+
+ Log(LOG_ADMIN, source, this, ci) << "(" << (!reason.empty() ? reason : "No reason") << "), expires on " << (expiry_secs ? Anope::strftime(Anope::CurTime + expiry_secs) : "never");
+ source.Reply(_("Channel \002{0}\002 is now suspended."), ci->GetName());
+
+ EventManager::Get()->Dispatch(&Event::ChanSuspend::OnChanSuspend, ci);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Disallows anyone from using the given channel."
+ " All channel settings are preserved while the channel is suspended."
+ "If \037expiry\037 is given the channel will be unsuspended after that period of time."));
+ return true;
+ }
+};
+
+class CommandCSUnSuspend : public Command
+{
+ public:
+ CommandCSUnSuspend(Module *creator) : Command(creator, "chanserv/unsuspend", 1, 1)
+ {
+ this->SetDesc(_("Releases a suspended channel"));
+ this->SetSyntax(_("\037channel\037"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &chan = params[0];
+
+ if (Anope::ReadOnly)
+ source.Reply(_("Services are in read-only mode. Any changes made may not persist."));
+
+ ChanServ::Channel *ci = ChanServ::Find(chan);
+ if (ci == NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
+ return;
+ }
+
+ CSSuspendInfo *si = ci->GetRef<CSSuspendInfo *>();
+ if (!si)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't suspended."), ci->GetName());
+ return;
+ }
+
+ Log(LOG_ADMIN, source, this, ci) << "which was suspended by " << si->GetBy() << " for: " << (!si->GetReason().empty() ? si->GetReason() : "No reason");
+
+ si->Delete();
+
+ source.Reply(_("Channel \002%s\002 is now released."), ci->GetName().c_str());
+
+ EventManager::Get()->Dispatch(&Event::ChanUnsuspend::OnChanUnsuspend, ci);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Releases a suspended channel. All data and settings are preserved from before the suspension."));
+ return true;
+ }
+};
+
+class CSSuspend : public Module
+ , public EventHook<Event::ChanInfo>
+ , public EventHook<ChanServ::Event::PreChanExpire>
+ , public EventHook<Event::CheckKick>
+ , public EventHook<Event::ChanDrop>
+{
+ CommandCSSuspend commandcssuspend;
+ CommandCSUnSuspend commandcsunsuspend;
+ std::vector<Anope::string> show;
+ CSSuspendType cst;
+
+ struct trim
+ {
+ Anope::string operator()(Anope::string s) const
+ {
+ return s.trim();
+ }
+ };
+
+ bool Show(CommandSource &source, const Anope::string &what) const
+ {
+ return source.IsOper() || std::find(show.begin(), show.end(), what) != show.end();
+ }
+
+ public:
+ CSSuspend(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , EventHook<Event::ChanInfo>(this)
+ , EventHook<ChanServ::Event::PreChanExpire>(this)
+ , EventHook<Event::CheckKick>(this)
+ , EventHook<Event::ChanDrop>(this)
+ , commandcssuspend(this)
+ , commandcsunsuspend(this)
+ , cst(this)
+ {
+ }
+
+ void OnChanInfo(CommandSource &source, ChanServ::Channel *ci, InfoFormatter &info, bool show_hidden) override
+ {
+ CSSuspendInfo *si = ci->GetRef<CSSuspendInfo *>();
+ if (!si)
+ return;
+
+ if (show_hidden || Show(source, "suspended"))
+ info[_("Suspended")] = _("This channel is \002suspended\002.");
+ if (!si->GetBy().empty() && (show_hidden || Show(source, "by")))
+ info[_("Suspended by")] = si->GetBy();
+ if (!si->GetReason().empty() && (show_hidden || Show(source, "reason")))
+ info[_("Suspend reason")] = si->GetReason();
+ if (si->GetWhen() && (show_hidden || Show(source, "on")))
+ info[_("Suspended on")] = Anope::strftime(si->GetWhen(), source.GetAccount());
+ if (si->GetExpires() && (show_hidden || Show(source, "expires")))
+ info[_("Suspension expires")] = Anope::strftime(si->GetExpires(), source.GetAccount());
+ }
+
+ void OnPreChanExpire(ChanServ::Channel *ci, bool &expire) override
+ {
+ CSSuspendInfo *si = ci->GetRef<CSSuspendInfo *>();
+ if (!si)
+ return;
+
+ expire = false;
+
+ if (!si->GetExpires())
+ return;
+
+ if (si->GetExpires() < Anope::CurTime)
+ {
+ ci->SetLastUsed(Anope::CurTime);
+ si->Delete();
+
+ Log(this) << "Expiring suspend for " << ci->GetName();
+ }
+ }
+
+ EventReturn OnCheckKick(User *u, Channel *c, Anope::string &mask, Anope::string &reason) override
+ {
+ if (u->HasMode("OPER") || !c->ci || !c->ci->GetRef<CSSuspendInfo *>())
+ return EVENT_CONTINUE;
+
+ reason = Language::Translate(u, _("This channel may not be used."));
+ return EVENT_STOP;
+ }
+
+ EventReturn OnChanDrop(CommandSource &source, ChanServ::Channel *ci) override
+ {
+ CSSuspendInfo *si = ci->GetRef<CSSuspendInfo *>();
+ if (si && !source.HasCommand("chanserv/drop"))
+ {
+ source.Reply(_("Channel \002{0}\002 is currently suspended."), ci->GetName());
+ return EVENT_STOP;
+ }
+
+ return EVENT_CONTINUE;
+ }
+};
+
+MODULE_INIT(CSSuspend)
diff --git a/modules/chanserv/sync.cpp b/modules/chanserv/sync.cpp
new file mode 100644
index 000000000..9f9ba4ff7
--- /dev/null
+++ b/modules/chanserv/sync.cpp
@@ -0,0 +1,85 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2011-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+
+class CommandCSSync : public Command
+{
+ public:
+ CommandCSSync(Module *creator) : Command(creator, "chanserv/sync", 1, 1)
+ {
+ this->SetDesc(_("Sync users channel modes"));
+ this->SetSyntax(_("\037channel\037"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &chan = params[0];
+
+ ChanServ::Channel *ci = ChanServ::Find(chan);
+ if (ci == NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
+ return;
+ }
+
+ if (ci->c == NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 doesn't exist."), ci->GetName());
+ return;
+ }
+
+ if (!source.AccessFor(ci).HasPriv("ACCESS_CHANGE") && !source.HasPriv("chanserv/administration"))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "ACCESS_CHANGE", ci->GetName());
+ return;
+ }
+
+ bool override = !source.AccessFor(ci).HasPriv("ACCESS_CHANGE") && source.HasPriv("chanserv/administration");
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci);
+
+ for (Channel::ChanUserList::iterator it = ci->c->users.begin(), it_end = ci->c->users.end(); it != it_end; ++it)
+ ci->c->SetCorrectModes(it->second->user, true);
+
+ source.Reply(_("All user modes on \002{0}\002 have been synced."));
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &params) override
+ {
+ source.Reply(_("Syncs all channel status modes on all users on \037channel\037 with the modes they should have based on the channel access list.\n"
+ "\n"
+ "Use of this command requires the \002{0}\002 privilege on \037channel\037."),
+ "ACCESS_CHNAGE");
+ return true;
+ }
+};
+
+class CSSync : public Module
+{
+ CommandCSSync commandcssync;
+
+ public:
+ CSSync(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandcssync(this)
+ {
+
+ }
+};
+
+MODULE_INIT(CSSync)
diff --git a/modules/chanserv/topic.cpp b/modules/chanserv/topic.cpp
new file mode 100644
index 000000000..fe9d2a80f
--- /dev/null
+++ b/modules/chanserv/topic.cpp
@@ -0,0 +1,289 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/chanserv/mode.h"
+#include "modules/chanserv/info.h"
+#include "modules/chanserv/set.h"
+
+class CommandCSSetKeepTopic : public Command
+{
+ public:
+ CommandCSSetKeepTopic(Module *creator, const Anope::string &cname = "chanserv/set/keeptopic") : Command(creator, cname, 2, 2)
+ {
+ this->SetDesc(_("Retain topic when channel is not in use"));
+ this->SetSyntax(_("\037channel\037 {ON | OFF}"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &chan = params[0];
+ const Anope::string &param = params[1];
+
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ ChanServ::Channel *ci = ChanServ::Find(chan);
+ if (ci == NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
+ return;
+ }
+
+ EventReturn MOD_RESULT;
+ MOD_RESULT = EventManager::Get()->Dispatch(&Event::SetChannelOption::OnSetChannelOption, source, this, ci, param);
+ if (MOD_RESULT == EVENT_STOP)
+ return;
+
+ if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "SET", ci->GetName());
+ return;
+ }
+
+ if (param.equals_ci("ON"))
+ {
+ Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable keeptopic";
+ ci->SetS<bool>("KEEPTOPIC", true);
+ source.Reply(_("Topic retention option for \002{0}\002 is now \002on\002."), ci->GetName());
+ }
+ else if (param.equals_ci("OFF"))
+ {
+ Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable keeptopic";
+ ci->UnsetS<bool>("KEEPTOPIC");
+ source.Reply(_("Topic retention option for \002{0}\002 is now \002off\002."), ci->GetName());
+ }
+ else
+ this->OnSyntaxError(source, "KEEPTOPIC");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
+ {
+ source.Reply(_("Enables or disables the \002topic retention\002 option for a \037channel\037."
+ " When \002topic retention\002 is set, the topic for the channel will be remembered by {0} even after the last user leaves the channel, and will be restored the next time the channel is created."),
+ source.service->nick);
+ return true;
+ }
+};
+
+class CommandCSTopic : public Command
+{
+ ExtensibleRef<bool> topiclock;
+
+ void Lock(CommandSource &source, ChanServ::Channel *ci, const std::vector<Anope::string> &params)
+ {
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ EventReturn MOD_RESULT;
+ MOD_RESULT = EventManager::Get()->Dispatch(&Event::SetChannelOption::OnSetChannelOption, source, this, ci, "topiclock on");
+ if (MOD_RESULT == EVENT_STOP)
+ return;
+
+ topiclock->Set(ci, true);
+ source.Reply(_("Topic lock option for \002{0}\002 is now \002on\002."), ci->GetName());
+ }
+
+ void Unlock(CommandSource &source, ChanServ::Channel *ci, const std::vector<Anope::string> &params)
+ {
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ EventReturn MOD_RESULT;
+ MOD_RESULT = EventManager::Get()->Dispatch(&Event::SetChannelOption::OnSetChannelOption, source, this, ci, "topiclock off");
+ if (MOD_RESULT == EVENT_STOP)
+ return;
+
+ topiclock->Unset(ci);
+ source.Reply(_("Topic lock option for \002{0}\002 is now \002off\002."), ci->GetName());
+ }
+
+ void Set(CommandSource &source, ChanServ::Channel *ci, const Anope::string &topic)
+ {
+ bool has_topiclock = topiclock->HasExt(ci);
+ topiclock->Unset(ci);
+ ci->c->ChangeTopic(source.GetNick(), topic, Anope::CurTime);
+ if (has_topiclock)
+ topiclock->Set(ci, true);
+
+ bool override = !source.AccessFor(ci).HasPriv("TOPIC");
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << (!topic.empty() ? "to change the topic to: " : "to unset the topic") << (!topic.empty() ? topic : "");
+ }
+
+ void Append(CommandSource &source, ChanServ::Channel *ci, const std::vector<Anope::string> &params)
+ {
+ const Anope::string &topic = params[2];
+
+ Anope::string new_topic;
+ if (!ci->c->topic.empty())
+ {
+ new_topic = ci->c->topic + " " + topic;
+ ci->SetLastTopic("");
+ }
+ else
+ new_topic = topic;
+
+ this->Set(source, ci, new_topic);
+ }
+
+ public:
+ CommandCSTopic(Module *creator) : Command(creator, "chanserv/topic", 2, 3),
+ topiclock("TOPICLOCK")
+ {
+ this->SetDesc(_("Manipulate the topic of the specified channel"));
+ this->SetSyntax(_("\037channel\037 [SET] [\037topic\037]"));
+ this->SetSyntax(_("\037channel\037 APPEND \037topic\037"));
+ this->SetSyntax(_("\037channel\037 [UNLOCK|LOCK]"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &channel = params[0];
+ const Anope::string &subcmd = params[1];
+
+ ChanServ::Channel *ci = ChanServ::Find(channel);
+ if (ci == NULL)
+ source.Reply(_("Channel \002{0}\002 isn't registered."), channel);
+ else if (!source.AccessFor(ci).HasPriv("TOPIC") && !source.HasCommand("chanserv/topic"))
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "TOPIC", ci->GetName());
+ else if (subcmd.equals_ci("LOCK"))
+ this->Lock(source, ci, params);
+ else if (subcmd.equals_ci("UNLOCK"))
+ this->Unlock(source, ci, params);
+ else if (!ci->c)
+ source.Reply(_("Channel \002{0}\002 doesn't exist."), ci->GetName());
+ else if (subcmd.equals_ci("APPEND") && params.size() > 2)
+ this->Append(source, ci, params);
+ else
+ {
+ Anope::string topic;
+ if (subcmd.equals_ci("SET"))
+ {
+ topic = params.size() > 2 ? params[2] : "";
+ }
+ else
+ {
+ topic = subcmd;
+ if (params.size() > 2)
+ topic += " " + params[2];
+ }
+ this->Set(source, ci, topic);
+ }
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Allows manipulating the topic of the specified channel."
+ " The \002SET\002 command changes the topic of the channel to the given topic or unsets the topic if no topic is given."
+ " The \002APPEND\002 command appends the given topic to the existing topic.\n"
+ "\n"
+ "\002LOCK\002 and \002UNLOCK\002 may be used to enable and disable topic lock."
+ " When topic lock is set, the channel topic will be unchangeable by users who do not have the \002TOPIC\002 privilege.\n"
+ "\n"
+ "Use of this command requires the \002{0}\002 privilege on \037channel\037."),
+ "TOPIC");
+ return true;
+ }
+};
+
+class CSTopic : public Module
+ , public EventHook<Event::ChannelSync>
+ , public EventHook<Event::TopicUpdated>
+ , public EventHook<Event::ChanInfo>
+{
+ CommandCSTopic commandcstopic;
+ CommandCSSetKeepTopic commandcssetkeeptopic;
+
+ Serialize::Field<ChanServ::Channel, bool> topiclock, keeptopic;
+
+ ServiceReference<ModeLocks> mlocks;
+
+ public:
+ CSTopic(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , EventHook<Event::ChannelSync>(this)
+ , EventHook<Event::TopicUpdated>(this)
+ , EventHook<Event::ChanInfo>(this)
+ , commandcstopic(this)
+ , commandcssetkeeptopic(this)
+ , topiclock(this, "TOPICLOCK")
+ , keeptopic(this, "KEEPTOPIC")
+ {
+
+ }
+
+ void OnChannelSync(Channel *c) override
+ {
+ if (c->ci)
+ {
+ /* Update channel topic */
+ if ((topiclock.HasExt(c->ci) || keeptopic.HasExt(c->ci)) && c->ci->GetLastTopic() != c->topic)
+ {
+ c->ChangeTopic(!c->ci->GetLastTopicSetter().empty() ? c->ci->GetLastTopicSetter() : c->ci->WhoSends()->nick, c->ci->GetLastTopic(), c->ci->GetLastTopicTime() ? c->ci->GetLastTopicTime() : Anope::CurTime);
+ }
+ }
+ }
+
+ void OnTopicUpdated(User *source, Channel *c, const Anope::string &user, const Anope::string &topic) override
+ {
+ if (!c->ci)
+ return;
+
+ /* We only compare the topics here, not the time or setter. This is because some (old) IRCds do not
+ * allow us to set the topic as someone else, meaning we have to bump the TS and change the setter to us.
+ * This desyncs what is really set with what we have stored, and we end up resetting the topic often when
+ * it is not required
+ */
+ if (topiclock.HasExt(c->ci) && c->ci->GetLastTopic() != c->topic && (!source || !c->ci->AccessFor(source).HasPriv("TOPIC")))
+ {
+ c->ChangeTopic(c->ci->GetLastTopicSetter(), c->ci->GetLastTopic(), c->ci->GetLastTopicTime());
+ }
+ else
+ {
+ c->ci->SetLastTopic(c->topic);
+ c->ci->SetLastTopicSetter(c->topic_setter);
+ c->ci->SetLastTopicTime(c->topic_ts);
+ }
+ }
+
+ void OnChanInfo(CommandSource &source, ChanServ::Channel *ci, InfoFormatter &info, bool show_all) override
+ {
+ if (keeptopic.HasExt(ci))
+ info.AddOption(_("Topic retention"));
+ if (topiclock.HasExt(ci))
+ info.AddOption(_("Topic lock"));
+
+ ModeLock *secret = mlocks ? mlocks->GetMLock(ci, "SECRET") : nullptr;
+ if (!ci->GetLastTopic().empty() && (show_all || ((!secret || secret->GetSet() == false) && (!ci->c || !ci->c->HasMode("SECRET")))))
+ {
+ info[_("Last topic")] = ci->GetLastTopic();
+ info[_("Topic set by")] = ci->GetLastTopicSetter();
+ }
+ }
+};
+
+MODULE_INIT(CSTopic)
diff --git a/modules/commands/cs_unban.cpp b/modules/chanserv/unban.cpp
index 64ecc7151..c4ee62e21 100644
--- a/modules/commands/cs_unban.cpp
+++ b/modules/chanserv/unban.cpp
@@ -1,12 +1,20 @@
-/* ChanServ core functions
+/*
+ * Anope IRC Services
*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
*
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "module.h"
@@ -20,7 +28,7 @@ class CommandCSUnban : public Command
this->SetSyntax(_("\037channel\037 [\037nick\037]"));
}
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
{
ChannelMode *cm = ModeManager::FindChannelModeByName("BAN");
if (!cm)
@@ -34,14 +42,10 @@ class CommandCSUnban : public Command
if (!source.GetUser())
return;
- std::deque<ChannelInfo *> queue;
- source.GetAccount()->GetChannelReferences(queue);
-
unsigned count = 0;
- for (unsigned i = 0; i < queue.size(); ++i)
- {
- ChannelInfo *ci = queue[i];
+ for (ChanServ::Channel *ci : source.GetAccount()->GetRefs<ChanServ::Channel *>())
+ {
if (!ci->c || !source.AccessFor(ci).HasPriv("UNBAN"))
continue;
@@ -56,22 +60,23 @@ class CommandCSUnban : public Command
return;
}
- ChannelInfo *ci = ChannelInfo::Find(params[0]);
+ const Anope::string &chan = params[0];
+ ChanServ::Channel *ci = ChanServ::Find(chan);
if (ci == NULL)
{
- source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
return;
}
if (ci->c == NULL)
{
- source.Reply(CHAN_X_NOT_IN_USE, ci->name.c_str());
+ source.Reply(_("Channel \002{0}\002 doesn't exist."), ci->GetName());
return;
}
if (!source.AccessFor(ci).HasPriv("UNBAN") && !source.HasPriv("chanserv/kick"))
{
- source.Reply(ACCESS_DENIED);
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "UNBAN", ci->GetName());
return;
}
@@ -81,7 +86,8 @@ class CommandCSUnban : public Command
if (!u2)
{
- source.Reply(NICK_X_NOT_IN_USE, params[1].c_str());
+ if (params.size() > 1)
+ source.Reply(_("User \002{0}\002 isn't currently online."), params[1]);
return;
}
@@ -91,22 +97,18 @@ class CommandCSUnban : public Command
for (unsigned i = 0; i < modes.size(); ++i)
ci->c->Unban(u2, modes[i]->name, source.GetUser() == u2);
if (u2 == source.GetUser())
- source.Reply(_("You have been unbanned from \002%s\002."), ci->c->name.c_str());
+ source.Reply(_("You have been unbanned from \002{0}\002."), ci->c->name);
else
- source.Reply(_("\002%s\002 has been unbanned from \002%s\002."), u2->nick.c_str(), ci->c->name.c_str());
+ source.Reply(_("\002{0}\002 has been unbanned from \002{1}\002."), u2->nick, ci->c->name);
}
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
{
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Tells %s to remove all bans preventing you or the given\n"
- "user from entering the given channel. If no channel is\n"
- "given, all bans affecting you in channels you have access\n"
- "in are removed.\n"
- " \n"
- "By default, limited to AOPs or those with level 5 access and above\n"
- "on the channel."), source.service->nick.c_str());
+ source.Reply(_("Tells {0} to remove all bans preventing you or the given \037user\037 from entering \037channel\037."
+ " If no channel is given, all bans affecting you in channels you have access in are removed.\n"
+ "\n"
+ "Use of this command requires the \002{1}\002 privilege on \037channel\037."),
+ source.service->nick, "UNBAN");
return true;
}
};
@@ -116,8 +118,8 @@ class CSUnban : public Module
CommandCSUnban commandcsunban;
public:
- CSUnban(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandcsunban(this)
+ CSUnban(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandcsunban(this)
{
}
diff --git a/modules/commands/cs_updown.cpp b/modules/chanserv/updown.cpp
index ab3ab60ce..baa540163 100644
--- a/modules/commands/cs_updown.cpp
+++ b/modules/chanserv/updown.cpp
@@ -1,12 +1,20 @@
-/* ChanServ core functions
+/*
+ * Anope IRC Services
*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2011-2016 Anope Team <team@anope.org>
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
*
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "module.h"
@@ -22,7 +30,7 @@ class CommandCSUp : public Command
bool giving = true;
/* whether or not we have given a mode */
bool given = false;
- AccessGroup u_access = c->ci->AccessFor(u);
+ ChanServ::AccessGroup u_access = c->ci->AccessFor(u);
for (unsigned i = 0; i < ModeManager::GetStatusChannelModesByRank().size(); ++i)
{
@@ -46,10 +54,10 @@ class CommandCSUp : public Command
CommandCSUp(Module *creator) : Command(creator, "chanserv/up", 0, 2)
{
this->SetDesc(_("Updates a selected nicks status on a channel"));
- this->SetSyntax(_("[\037channel\037 [\037nick\037]]"));
+ this->SetSyntax(_("[\037channel\037 [\037user\037]]"));
}
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
{
if (params.empty())
{
@@ -64,42 +72,52 @@ class CommandCSUp : public Command
}
else
{
- const Anope::string &channel = params[0];
+ const Anope::string &chan = params[0];
const Anope::string &nick = params.size() > 1 ? params[1] : source.GetNick();
- Channel *c = Channel::Find(channel);
-
- if (c == NULL)
+ ChanServ::Channel *ci = ChanServ::Find(chan);
+ if (ci == NULL)
{
- source.Reply(CHAN_X_NOT_IN_USE, channel.c_str());
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
return;
}
- else if (!c->ci)
+
+ if (ci->c == NULL)
{
- source.Reply(CHAN_X_NOT_REGISTERED, channel.c_str());
+ source.Reply(_("Channel \002{0}\002 doesn't exist."), ci->GetName());
return;
}
User *u = User::Find(nick, true);
User *srcu = source.GetUser();
+ Channel *c = ci->c;
bool override = false;
if (u == NULL)
{
- source.Reply(NICK_X_NOT_IN_USE, nick.c_str());
+ source.Reply(_("User \002{0}\002 isn't currently online."), nick);
return;
}
- else if (srcu && !srcu->FindChannel(c))
+
+ if (srcu && !srcu->FindChannel(c))
{
source.Reply(_("You must be in \002%s\002 to use this command."), c->name.c_str());
return;
}
- else if (!u->FindChannel(c))
+
+ if (!u->FindChannel(c))
{
- source.Reply(NICK_X_NOT_ON_CHAN, nick.c_str(), channel.c_str());
+ source.Reply(_("You must be on channel \002{0}\002 to use this command."), c->name);
return;
}
- else if (source.GetUser() && u != source.GetUser() && c->ci->HasExt("PEACE"))
+
+ if (!u->FindChannel(c))
+ {
+ source.Reply(_("\002{0}\002 is not on channel \002{1}\002."), u->nick, c->name);
+ return;
+ }
+
+ if (source.GetUser() && u != source.GetUser() && c->ci->HasFieldS("PEACE"))
{
if (c->ci->AccessFor(u) >= c->ci->AccessFor(source.GetUser()))
{
@@ -107,7 +125,7 @@ class CommandCSUp : public Command
override = true;
else
{
- source.Reply(ACCESS_DENIED);
+ source.Reply(_("Access denied. \002{0}\002 has more privileges than you on \002{1}\002."), u->nick, ci->GetName());
return;
}
}
@@ -119,13 +137,11 @@ class CommandCSUp : public Command
}
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
{
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Updates a selected nicks status modes on a channel. If \037nick\037 is\n"
- "omitted then your status is updated. If \037channel\037 is omitted then\n"
- "your channel status is updated on every channel you are in."));
+ source.Reply(_("Updates the status modes of \037user\037 on \037channel\037."
+ " If \037user\037 is omitted then your status is updated."
+ " If \037channel\037 is omitted then your status is updated on every channel you are in."));
return true;
}
};
@@ -147,7 +163,7 @@ class CommandCSDown : public Command
this->SetSyntax(_("[\037channel\037 [\037nick\037]]"));
}
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
{
if (params.empty())
{
@@ -165,39 +181,50 @@ class CommandCSDown : public Command
const Anope::string &channel = params[0];
const Anope::string &nick = params.size() > 1 ? params[1] : source.GetNick();
- Channel *c = Channel::Find(channel);
-
- if (c == NULL)
+ ChanServ::Channel *ci = ChanServ::Find(channel);
+ if (ci == NULL)
{
- source.Reply(CHAN_X_NOT_IN_USE, channel.c_str());
+ source.Reply(_("Channel \002{0}\002 isn't registered."), channel);
return;
}
- else if (!c->ci)
+
+ if (ci->c == NULL)
{
- source.Reply(CHAN_X_NOT_REGISTERED, channel.c_str());
+ source.Reply(_("Channel \002{0}\002 doesn't exist."), ci->GetName());
return;
}
User *u = User::Find(nick, true);
+ Channel *c = ci->c;
+
User *srcu = source.GetUser();
bool override = false;
if (u == NULL)
{
- source.Reply(NICK_X_NOT_IN_USE, nick.c_str());
+ source.Reply(_("User \002{0}\002 isn't currently online."), nick);
+ return;
+ }
+
+ if (srcu && !srcu->FindChannel(c))
+ {
+ source.Reply(_("You must be on channel \002{0}\002 to use this command."), c->name);
return;
}
- else if (srcu && !srcu->FindChannel(c))
+
+ if (srcu && !srcu->FindChannel(c))
{
source.Reply(_("You must be in \002%s\002 to use this command."), c->name.c_str());
return;
}
- else if (!u->FindChannel(c))
+
+ if (!u->FindChannel(c))
{
- source.Reply(NICK_X_NOT_ON_CHAN, nick.c_str(), channel.c_str());
+ source.Reply(_("\002%s\002 is not on channel %s."), u->nick, c->name);
return;
}
- else if (source.GetUser() && u != source.GetUser() && c->ci->HasExt("PEACE"))
+
+ if (source.GetUser() && u != source.GetUser() && c->ci->HasFieldS("PEACE"))
{
if (c->ci->AccessFor(u) >= c->ci->AccessFor(source.GetUser()))
{
@@ -205,7 +232,7 @@ class CommandCSDown : public Command
override = true;
else
{
- source.Reply(ACCESS_DENIED);
+ source.Reply(_("Access denied. \002{0}\002 has more privileges than you on \002{1}\002."), u->nick, ci->GetName());
return;
}
}
@@ -216,13 +243,11 @@ class CommandCSDown : public Command
}
}
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
{
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Removes a selected nicks status modes on a channel. If \037nick\037 is\n"
- "omitted then your status is removed. If \037channel\037 is omitted then\n"
- "your channel status is removed on every channel you are in."));
+ source.Reply(_("Removes a selected nicks status modes on a channel."
+ " If \037nick\037 is ommited then your status is removed."
+ " If \037channel\037 is ommited then channel status is removed on every channel you are in."));
return true;
}
};
@@ -233,8 +258,9 @@ class CSUpDown : public Module
CommandCSDown commandcsdown;
public:
- CSUpDown(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandcsup(this), commandcsdown(this)
+ CSUpDown(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandcsup(this)
+ , commandcsdown(this)
{
}
diff --git a/modules/chanserv/xop.cpp b/modules/chanserv/xop.cpp
new file mode 100644
index 000000000..74feaca7e
--- /dev/null
+++ b/modules/chanserv/xop.cpp
@@ -0,0 +1,668 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+/* Dependencies: anope_chanserv.main */
+
+#include "module.h"
+#include "modules/chanserv.h"
+#include "modules/chanserv/access.h"
+#include "modules/chanserv/main/chanaccess.h"
+#include "main/chanaccesstype.h"
+
+namespace
+{
+ std::vector<Anope::string> order;
+ std::map<Anope::string, std::vector<Anope::string> > permissions;
+}
+
+class XOPChanAccessImpl : public XOPChanAccess
+{
+ friend class XOPChanAccessType;
+
+ Anope::string type;
+
+ public:
+ using XOPChanAccess::XOPChanAccess;
+
+ const Anope::string &GetType() override;
+ void SetType(const Anope::string &) override;
+
+ bool HasPriv(const Anope::string &priv) override
+ {
+ for (std::vector<Anope::string>::iterator it = std::find(order.begin(), order.end(), this->GetType()); it != order.end(); ++it)
+ {
+ const std::vector<Anope::string> &privs = permissions[*it];
+ if (std::find(privs.begin(), privs.end(), priv) != privs.end())
+ return true;
+ }
+ return false;
+ }
+
+ Anope::string AccessSerialize() override
+ {
+ return this->GetType();
+ }
+
+ void AccessUnserialize(const Anope::string &data) override
+ {
+ this->SetType(data);
+ }
+
+ static Anope::string DetermineLevel(ChanServ::ChanAccess *access)
+ {
+ if (access->GetSerializableType()->GetName() == NAME)
+ {
+ XOPChanAccess *xaccess = anope_dynamic_static_cast<XOPChanAccess *>(access);
+ return xaccess->GetType();
+ }
+
+ std::map<Anope::string, int> count;
+
+ for (std::map<Anope::string, std::vector<Anope::string> >::const_iterator it = permissions.begin(), it_end = permissions.end(); it != it_end; ++it)
+ {
+ int &c = count[it->first];
+ const std::vector<Anope::string> &perms = it->second;
+ for (unsigned i = 0; i < perms.size(); ++i)
+ if (access->HasPriv(perms[i]))
+ ++c;
+ }
+
+ Anope::string max;
+ int maxn = 0;
+ for (std::map<Anope::string, int>::iterator it = count.begin(), it_end = count.end(); it != it_end; ++it)
+ if (it->second > maxn)
+ {
+ maxn = it->second;
+ max = it->first;
+ }
+
+ return max;
+ }
+
+};
+
+class XOPChanAccessType : public ChanAccessType<XOPChanAccessImpl>
+{
+ public:
+ Serialize::Field<XOPChanAccessImpl, Anope::string> type;
+
+ XOPChanAccessType(Module *me) : ChanAccessType<XOPChanAccessImpl>(me)
+ , type(this, "type", &XOPChanAccessImpl::type)
+ {
+ Serialize::SetParent(XOPChanAccess::NAME, ChanServ::ChanAccess::NAME);
+ }
+};
+
+const Anope::string &XOPChanAccessImpl::GetType()
+{
+ return Get(&XOPChanAccessType::type);
+}
+
+void XOPChanAccessImpl::SetType(const Anope::string &i)
+{
+ Object::Set(&XOPChanAccessType::type, i);
+}
+
+class CommandCSXOP : public Command
+{
+ private:
+ void DoAdd(CommandSource &source, ChanServ::Channel *ci, const std::vector<Anope::string> &params)
+ {
+ Anope::string mask = params.size() > 2 ? params[2] : "";
+
+ if (mask.empty())
+ {
+ this->OnSyntaxError(source, "ADD");
+ return;
+ }
+
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Sorry, channel %s list modification is temporarily disabled."), source.command.c_str());
+ return;
+ }
+
+ ChanServ::AccessGroup access = source.AccessFor(ci);
+ ChanServ::ChanAccess *highest = access.Highest();
+ bool override = false;
+
+ std::vector<Anope::string>::iterator cmd_it = std::find(order.begin(), order.end(), source.command.upper()),
+ access_it = highest ? std::find(order.begin(), order.end(), XOPChanAccessImpl::DetermineLevel(highest)) : order.end();
+
+ if (!access.founder && (!access.HasPriv("ACCESS_CHANGE") || cmd_it <= access_it))
+ {
+ if (source.HasPriv("chanserv/access/modify"))
+ override = true;
+ else
+ {
+ source.Reply(_("Access denied. You do not have the \002{0}\002 privilege on \002{1}\002."), "ACCESS_CHANGE", ci->GetName());
+ return;
+ }
+ }
+
+ NickServ::Nick *na = nullptr;
+ ChanServ::Channel *targ_ci = nullptr;
+
+ if (IRCD->IsChannelValid(mask))
+ {
+ if (Config->GetModule("chanserv")->Get<bool>("disallow_channel_access"))
+ {
+ source.Reply(_("Channels may not be on access lists."));
+ return;
+ }
+
+ targ_ci = ChanServ::Find(mask);
+ if (targ_ci == NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), mask);
+ return;
+ }
+
+ if (ci == targ_ci)
+ {
+ source.Reply(_("You can't add a channel to its own access list."));
+ return;
+ }
+
+ mask = targ_ci->GetName();
+ }
+ else
+ {
+ na = NickServ::FindNick(mask);
+
+ if (!na && Config->GetModule("chanserv")->Get<bool>("disallow_hostmask_access"))
+ {
+ source.Reply(_("Masks and unregistered users may not be on access lists."));
+ return;
+ }
+
+ if (mask.find_first_of("!*@") == Anope::string::npos && !na)
+ {
+ User *targ = User::Find(mask, true);
+ if (targ != NULL)
+ mask = "*!*@" + targ->GetDisplayedHost();
+ else
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), mask);
+ return;
+ }
+ }
+
+ if (na)
+ mask = na->GetNick();
+ }
+
+ for (unsigned i = 0; i < ci->GetAccessCount(); ++i)
+ {
+ ChanServ::ChanAccess *a = ci->GetAccess(i);
+
+ if ((na && na->GetAccount() == a->GetAccount()) || mask.equals_ci(a->Mask()))
+ {
+ if ((!highest || *a >= *highest) && !access.founder && !source.HasPriv("chanserv/access/modify"))
+ {
+ source.Reply(_("Access denied. You do not have enough privileges on \002{0}\002 to lower the access of \002{1}\002."), ci->GetName(), a->Mask());
+ return;
+ }
+
+ a->Delete();
+ break;
+ }
+ }
+
+ unsigned access_max = Config->GetModule("chanserv")->Get<unsigned>("accessmax", "1024");
+ if (access_max && ci->GetAccessCount() >= access_max)
+ {
+ source.Reply(_("Sorry, you can only have %d access entries on a channel, including access entries from other channels."), access_max);
+ return;
+ }
+
+ XOPChanAccess *acc = Serialize::New<XOPChanAccess *>();
+ if (na)
+ acc->SetObj(na->GetAccount());
+ else if (targ_ci)
+ acc->SetObj(targ_ci);
+ acc->SetChannel(ci);
+ acc->SetMask(mask);
+ acc->SetCreator(source.GetNick());
+ acc->SetType(source.command.upper());
+ acc->SetLastSeen(0);
+ acc->SetCreated(Anope::CurTime);
+
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to add " << mask;
+
+ EventManager::Get()->Dispatch(&Event::AccessAdd::OnAccessAdd, ci, source, acc);
+ source.Reply(_("\002%s\002 added to %s %s list."), acc->Mask(), ci->GetName().c_str(), source.command.c_str());
+ }
+
+ void DoDel(CommandSource &source, ChanServ::Channel *ci, const std::vector<Anope::string> &params)
+ {
+ NickServ::Account *nc = source.nc;
+ Anope::string mask = params.size() > 2 ? params[2] : "";
+
+ if (mask.empty())
+ {
+ this->OnSyntaxError(source, "DEL");
+ return;
+ }
+
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Sorry, channel %s list modification is temporarily disabled."), source.command.c_str());
+ return;
+ }
+
+ if (!ci->GetAccessCount())
+ {
+ source.Reply(_("%s %s list is empty."), ci->GetName().c_str(), source.command.c_str());
+ return;
+ }
+
+ ChanServ::AccessGroup access = source.AccessFor(ci);
+ ChanServ::ChanAccess *highest = access.Highest();
+ bool override = false;
+
+ if (!isdigit(mask[0]) && mask.find_first_of("#!*@") == Anope::string::npos && !NickServ::FindNick(mask))
+ {
+ User *targ = User::Find(mask, true);
+ if (targ != NULL)
+ mask = "*!*@" + targ->GetDisplayedHost();
+ else
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), mask);
+ return;
+ }
+ }
+
+ std::vector<Anope::string>::iterator cmd_it = std::find(order.begin(), order.end(), source.command.upper()),
+ access_it = highest ? std::find(order.begin(), order.end(), XOPChanAccessImpl::DetermineLevel(highest)) : order.end();
+
+ if (!mask.equals_ci(nc->GetDisplay()) && !access.founder && (!access.HasPriv("ACCESS_CHANGE") || cmd_it <= access_it))
+ {
+ if (source.HasPriv("chanserv/access/modify"))
+ override = true;
+ else
+ {
+ source.Reply(_("Access denied. You do not have the \002{0}\002 privilege on \002{1}\002."), "ACCESS_CHANGE", ci->GetName());
+ return;
+ }
+ }
+
+ /* Special case: is it a number/list? Only do search if it isn't. */
+ if (isdigit(mask[0]) && mask.find_first_not_of("1234567890,-") == Anope::string::npos)
+ {
+ unsigned int deleted = 0;
+ Anope::string nicks;
+
+ NumberList(mask, true,
+ [&](unsigned int number)
+ {
+ if (!number || number > ci->GetAccessCount())
+ return;
+
+ ChanServ::ChanAccess *caccess = ci->GetAccess(number - 1);
+
+ if (caccess->GetSerializableType()->GetName() != "XOPChanAccess" || source.command.upper() != caccess->AccessSerialize())
+ return;
+
+ ++deleted;
+ if (!nicks.empty())
+ nicks += ", " + caccess->Mask();
+ else
+ nicks = caccess->Mask();
+
+ EventManager::Get()->Dispatch(&Event::AccessDel::OnAccessDel, ci, source, caccess);
+ delete caccess;
+ },
+ [&]()
+ {
+ if (!deleted)
+ source.Reply(_("No matching entries on %s %s list."), ci->GetName().c_str(), source.command.c_str());
+ else
+ {
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to delete " << nicks;
+
+ if (deleted == 1)
+ source.Reply(_("Deleted one entry from %s %s list."), ci->GetName().c_str(), source.command.c_str());
+ else
+ source.Reply(_("Deleted %d entries from %s %s list."), deleted, ci->GetName().c_str(), source.command.c_str());
+ }
+ });
+ }
+ else
+ {
+ for (unsigned i = 0; i < ci->GetAccessCount(); ++i)
+ {
+ ChanServ::ChanAccess *a = ci->GetAccess(i);
+
+ if (a->GetSerializableType()->GetName() != "XOPChanAccess" || source.command.upper() != a->AccessSerialize())
+ continue;
+
+ if (a->Mask().equals_ci(mask))
+ {
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to delete " << a->Mask();
+
+ source.Reply(_("\002%s\002 deleted from %s %s list."), a->Mask().c_str(), ci->GetName().c_str(), source.command.c_str());
+
+ EventManager::Get()->Dispatch(&Event::AccessDel::OnAccessDel, ci, source, a);
+ delete a;
+
+ return;
+ }
+ }
+
+ source.Reply(_("\002%s\002 not found on %s %s list."), mask.c_str(), ci->GetName().c_str(), source.command.c_str());
+ }
+ }
+
+ void DoList(CommandSource &source, ChanServ::Channel *ci, const std::vector<Anope::string> &params)
+ {
+
+ const Anope::string &nick = params.size() > 2 ? params[2] : "";
+
+ ChanServ::AccessGroup access = source.AccessFor(ci);
+
+ if (!access.HasPriv("ACCESS_LIST") && !source.HasPriv("chanserv/access/list"))
+ {
+ source.Reply(_("Access denied. You do not have the \002{0}\002 privilege on \002{1}\002."), "ACCESS_LIST", ci->GetName());
+ return;
+ }
+
+ if (!ci->GetAccessCount())
+ {
+ source.Reply(_("%s %s list is empty."), ci->GetName().c_str(), source.command.c_str());
+ return;
+ }
+
+ ListFormatter list(source.GetAccount());
+ list.AddColumn(_("Number")).AddColumn(_("Mask"));
+
+ if (!nick.empty() && nick.find_first_not_of("1234567890,-") == Anope::string::npos)
+ {
+ NumberList(nick, false,
+ [&](unsigned int number)
+ {
+ if (!number || number > ci->GetAccessCount())
+ return;
+
+ ChanServ::ChanAccess *a = ci->GetAccess(number - 1);
+
+ if (a->GetSerializableType()->GetName() != "XOPChanAccess" || source.command.upper() != a->AccessSerialize())
+ return;
+
+ ListFormatter::ListEntry entry;
+ entry["Number"] = stringify(number);
+ entry["Mask"] = a->Mask();
+ list.AddEntry(entry);
+ },
+ []{});
+ }
+ else
+ {
+ for (unsigned i = 0, end = ci->GetAccessCount(); i < end; ++i)
+ {
+ ChanServ::ChanAccess *a = ci->GetAccess(i);
+
+ if (a->GetSerializableType()->GetName() != "XOPChanAccess" || source.command.upper() != a->AccessSerialize())
+ continue;
+ if (!nick.empty() && !Anope::Match(a->Mask(), nick))
+ continue;
+
+ ListFormatter::ListEntry entry;
+ entry["Number"] = stringify(i + 1);
+ entry["Mask"] = a->Mask();
+ list.AddEntry(entry);
+ }
+ }
+
+ if (list.IsEmpty())
+ source.Reply(_("No matching entries on %s access list."), ci->GetName().c_str());
+ else
+ {
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+ source.Reply(_("%s list for %s"), source.command.c_str(), ci->GetName().c_str());
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+ }
+ }
+
+ void DoClear(CommandSource &source, ChanServ::Channel *ci)
+ {
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Sorry, channel %s list modification is temporarily disabled."), source.command.c_str());
+ return;
+ }
+
+ if (!ci->GetAccessCount())
+ {
+ source.Reply(_("%s %s list is empty."), ci->GetName().c_str(), source.command.c_str());
+ return;
+ }
+
+ if (!source.AccessFor(ci).HasPriv("FOUNDER") && !source.HasPriv("chanserv/access/modify"))
+ {
+ source.Reply(_("Access denied. You do not have the \002{0}\002 privilege on \002{1}\002."), "FOUNDER", ci->GetName());
+ return;
+ }
+
+ bool override = !source.AccessFor(ci).HasPriv("FOUNDER");
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to clear the access list";
+
+ for (unsigned i = ci->GetAccessCount(); i > 0; --i)
+ {
+ ChanServ::ChanAccess *access = ci->GetAccess(i - 1);
+
+ if (access->GetSerializableType()->GetName() != "XOPChanAccess" || source.command.upper() != access->AccessSerialize())
+ continue;
+
+ delete access;
+ }
+
+ EventManager::Get()->Dispatch(&Event::AccessClear::OnAccessClear, ci, source);
+
+ source.Reply(_("Channel %s %s list has been cleared."), ci->GetName().c_str(), source.command.c_str());
+ }
+
+ public:
+ CommandCSXOP(Module *modname) : Command(modname, "chanserv/xop", 2, 4)
+ {
+ this->SetSyntax(_("\037channel\037 ADD \037mask\037"));
+ this->SetSyntax(_("\037channel\037 DEL {\037mask\037 | \037entry-num\037 | \037list\037}"));
+ this->SetSyntax(_("\037channel\037 LIST [\037mask\037 | \037list\037]"));
+ this->SetSyntax(_("\037channel\037 CLEAR"));
+ }
+
+ const Anope::string GetDesc(CommandSource &source) const override
+ {
+ return Anope::printf(Language::Translate(source.GetAccount(), _("Modify the list of %s users")), source.command.upper().c_str());
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ ChanServ::Channel *ci = ChanServ::Find(params[0]);
+ if (ci == NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), params[0]);
+ return;
+ }
+
+ const Anope::string &cmd = params[1];
+
+ if (cmd.equals_ci("ADD"))
+ return this->DoAdd(source, ci, params);
+ else if (cmd.equals_ci("DEL"))
+ return this->DoDel(source, ci, params);
+ else if (cmd.equals_ci("LIST"))
+ return this->DoList(source, ci, params);
+ else if (cmd.equals_ci("CLEAR"))
+ return this->DoClear(source, ci);
+ else
+ this->OnSyntaxError(source, "");
+ }
+
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ if (subcommand.equals_ci("ADD"))
+ {
+ source.Reply(_("The \002{0} ADD\002 command adds \037mask\037 to the {0} list of \037channel\037."
+ " If you have the privilege to use this command, you may use it if you have more privileges than it grants."),
+ source.command);
+
+ source.Reply(_("Use of this command requires the \002{0}\002 privilege on \037channel\037.\n"
+ "Example:\n"
+ " {1} #anope ADD Adam"
+ " Adds \"Adam\" to the access list of \"#anope\" with the privilege set {1}"),
+ "ACCESS_CHANGE", source.command);
+ }
+ else if (subcommand.equals_ci("DEL"))
+ source.Reply(_("The \002{0} DEL\002 command removes the given \037mask\037 from the {0} list of \037channel\037."
+ " If a list of entry numbers is given, those entries are deleted.\n"
+ "\n"
+ "Use of this command requires the \002{1}\002 privilege on \037channel\037.\n"
+ "Example:\n"
+ " {0} #anope DEL Cronus"
+ " Removes \"Cronus\" from the {0} list of \"#anope\""),
+ source.command, "ACCESS_CHANGE");
+ else if (subcommand.equals_ci("LIST"))
+ source.Reply(_("The \002{0} LIST\002 command displays the {0} list of \037channel\037."
+ " If a wildcard mask is given, only those entries matching the mask are displayed."
+ " If a list of entry numbers is given, only those entries are shown.\n"
+ "\n"
+ "Use of this command requires the \002{1}\002 privilege on \037channel\037.\n"
+ "\n"
+ "Example:"
+ " {0} #anope LIST 2-5,7-9\n"
+ " List entries numbered 2 through 5 and 7 through 9 on the {0} of \"#anope\""),
+ source.command, "ACCESS_LIST");
+ else if (subcommand.equals_ci("CLEAR"))
+ source.Reply(_("The \002{0} CLEAR\002 command clears the {0} list of \037channel\037."
+ "\n"
+ "Use of this command requires the \002{1}\002 privilege on \037channel\037."),
+ source.command, "FOUNDER");
+
+ else
+ {
+ source.Reply(_("Maintains the \002{0} list\002 for a channel."
+ " Users who match an access entry on the {0} list receive the following privileges:\n"
+ "\n"),
+ source.command);
+
+ Anope::string buf;
+ for (unsigned i = 0; i < permissions[source.command].size(); ++i)
+ buf += "," + permissions[source.command][i];
+
+ source.Reply(buf);
+
+ CommandInfo *help = source.service->FindCommand("generic/help");
+ if (help)
+ {
+ source.Reply(_("\n"
+ "The \002ADD\002 command adds \037mask\037 to the {0} list.\n"
+ "Use of this command requires the \002{change}\002 privilege on \037channel\037.\n"
+ "\002{msg}{service} {help} {command} ADD\002 for more information.\n"
+ "\n"
+ "The \002DEL\002 command removes \037mask\037 from the {0} list.\n"
+ "Use of this command requires the \002{change}\002 privilege on \037channel\037.\n"
+ "\002{msg}{service} {help} {command} DEL\002 for more information.\n"
+ "\n"
+ "The \002LIST\002 command shows the {0} list for \037channel\037.\n"
+ "Use of this commands requires the \002{list}\002 privilege on \037channel\037.\n"
+ "\002{msg}{service} {help} {command} LIST\002 for more information.\n"
+ "\n"
+ "The \002CLEAR\002 command clears the {0} list."
+ "Use of this command requires the \002{founder}\002 privilege on \037channel\037.\n"
+ "\002{msg}{service} {help} {command} CLEAR\002 for more information.)"),
+ source.command, "change"_kw = "ACCESS_CHANGE", "list"_kw = "ACCESS_LIST", "help"_kw = help->cname);
+
+ Anope::string access_cmd, flags_cmd;
+ ServiceBot *access_bi, *flags_bi;
+ Command::FindCommandFromService("chanserv/access", access_bi, access_cmd);
+ Command::FindCommandFromService("chanserv/flags", flags_bi, access_cmd);
+ if (!access_cmd.empty() || !flags_cmd.empty())
+ {
+ source.Reply(_("\n"
+ "Alternative methods of modifying channel access lists are available:\n"));
+ if (!access_cmd.empty())
+ source.Reply(_("See \002{0}{1} {2} {3}\002 for more information about the access list."), Config->StrictPrivmsg, access_bi->nick, help->cname, access_cmd);
+ if (!flags_cmd.empty())
+ source.Reply(_("See \002{0}{1} {2} {3}\002 for more information about the flags system."), Config->StrictPrivmsg, flags_bi->nick, help->cname, flags_cmd);
+ }
+ }
+ }
+ return true;
+ }
+};
+
+class CSXOP : public Module
+{
+ CommandCSXOP commandcsxop;
+ XOPChanAccessType xopaccesstype;
+
+ public:
+ CSXOP(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandcsxop(this)
+ , xopaccesstype(this)
+ {
+ this->SetPermanent(true);
+
+ }
+
+ void OnReload(Configuration::Conf *conf) override
+ {
+ order.clear();
+ permissions.clear();
+
+ for (int i = 0; i < conf->CountBlock("privilege"); ++i)
+ {
+ Configuration::Block *block = conf->GetBlock("privilege", i);
+ const Anope::string &pname = block->Get<Anope::string>("name");
+
+ ChanServ::Privilege *p = ChanServ::service ? ChanServ::service->FindPrivilege(pname) : nullptr;
+ if (p == NULL)
+ continue;
+
+ const Anope::string &xop = block->Get<Anope::string>("xop");
+ if (pname.empty() || xop.empty())
+ continue;
+
+ permissions[xop].push_back(pname);
+ }
+
+ for (int i = 0; i < conf->CountBlock("command"); ++i)
+ {
+ Configuration::Block *block = conf->GetBlock("command", i);
+ const Anope::string &cname = block->Get<Anope::string>("name"),
+ &cserv = block->Get<Anope::string>("command");
+ if (cname.empty() || cserv != "chanserv/xop")
+ continue;
+
+ order.push_back(cname);
+ }
+ }
+};
+
+template<> void ModuleInfo<CSXOP>(ModuleDef *def)
+{
+ def->Depends("chanserv");
+}
+
+MODULE_INIT(CSXOP)
diff --git a/modules/commands/bs_assign.cpp b/modules/commands/bs_assign.cpp
deleted file mode 100644
index 0d1989fe9..000000000
--- a/modules/commands/bs_assign.cpp
+++ /dev/null
@@ -1,256 +0,0 @@
-/* BotServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class CommandBSAssign : public Command
-{
- public:
- CommandBSAssign(Module *creator) : Command(creator, "botserv/assign", 2, 2)
- {
- this->SetDesc(_("Assigns a bot to a channel"));
- this->SetSyntax(_("\037channel\037 \037nick\037"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &chan = params[0];
- const Anope::string &nick = params[1];
-
- if (Anope::ReadOnly)
- {
- source.Reply(_("Sorry, bot assignment is temporarily disabled."));
- return;
- }
-
- ChannelInfo *ci = ChannelInfo::Find(params[0]);
- if (ci == NULL)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
- return;
- }
-
- BotInfo *bi = BotInfo::Find(nick, true);
- if (!bi)
- {
- source.Reply(BOT_DOES_NOT_EXIST, nick.c_str());
- return;
- }
-
- AccessGroup access = source.AccessFor(ci);
- if (ci->HasExt("BS_NOBOT") || (!access.HasPriv("ASSIGN") && !source.HasPriv("botserv/administration")))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- if (bi->oper_only && !source.HasPriv("botserv/administration"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- if (ci->bi == bi)
- {
- source.Reply(_("Bot \002%s\002 is already assigned to channel \002%s\002."), ci->bi->nick.c_str(), chan.c_str());
- return;
- }
-
- bool override = !access.HasPriv("ASSIGN");
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "for " << bi->nick;
-
- bi->Assign(source.GetUser(), ci);
- source.Reply(_("Bot \002%s\002 has been assigned to %s."), bi->nick.c_str(), ci->name.c_str());
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Assigns the specified bot to a channel. You\n"
- "can then configure the bot for the channel so it fits\n"
- "your needs."));
- return true;
- }
-};
-
-class CommandBSUnassign : public Command
-{
- public:
- CommandBSUnassign(Module *creator) : Command(creator, "botserv/unassign", 1, 1)
- {
- this->SetDesc(_("Unassigns a bot from a channel"));
- this->SetSyntax(_("\037channel\037"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (Anope::ReadOnly)
- {
- source.Reply(_("Sorry, bot assignment is temporarily disabled."));
- return;
- }
-
- ChannelInfo *ci = ChannelInfo::Find(params[0]);
- if (ci == NULL)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
- return;
- }
-
- AccessGroup access = source.AccessFor(ci);
- if (!source.HasPriv("botserv/administration") && !access.HasPriv("ASSIGN"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- if (!ci->bi)
- {
- source.Reply(BOT_NOT_ASSIGNED);
- return;
- }
-
- if (ci->HasExt("PERSIST") && !ModeManager::FindChannelModeByName("PERM"))
- {
- source.Reply(_("You cannot unassign bots while persist is set on the channel."));
- return;
- }
-
- bool override = !access.HasPriv("ASSIGN");
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "for " << ci->bi->nick;
-
- ci->bi->UnAssign(source.GetUser(), ci);
- source.Reply(_("There is no bot assigned to %s anymore."), ci->name.c_str());
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Unassigns a bot from a channel. When you use this command,\n"
- "the bot won't join the channel anymore. However, bot\n"
- "configuration for the channel is kept, so you will always\n"
- "be able to reassign a bot later without having to reconfigure\n"
- "it entirely."));
- return true;
- }
-};
-
-class CommandBSSetNoBot : public Command
-{
- public:
- CommandBSSetNoBot(Module *creator, const Anope::string &sname = "botserv/set/nobot") : Command(creator, sname, 2, 2)
- {
- this->SetDesc(_("Prevent a bot from being assigned to a channel"));
- this->SetSyntax(_("\037channel\037 {\037ON|OFF\037}"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- ChannelInfo *ci = ChannelInfo::Find(params[0]);
- const Anope::string &value = params[1];
-
- if (Anope::ReadOnly)
- {
- source.Reply(_("Sorry, bot modification is temporarily disabled."));
- return;
- }
-
- if (ci == NULL)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
- return;
- }
-
- if (value.equals_ci("ON"))
- {
- Log(LOG_ADMIN, source, this, ci) << "to enable nobot";
-
- ci->Extend<bool>("BS_NOBOT");
- if (ci->bi)
- ci->bi->UnAssign(source.GetUser(), ci);
- source.Reply(_("No-bot mode is now \002on\002 on channel %s."), ci->name.c_str());
- }
- else if (value.equals_ci("OFF"))
- {
- Log(LOG_ADMIN, source, this, ci) << "to disable nobot";
-
- ci->Shrink<bool>("BS_NOBOT");
- source.Reply(_("No-bot mode is now \002off\002 on channel %s."), ci->name.c_str());
- }
- else
- this->OnSyntaxError(source, source.command);
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
- {
- this->SendSyntax(source);
- source.Reply(_(" \n"
- "This option makes a channel unassignable. If a bot\n"
- "is already assigned to the channel, it is unassigned\n"
- "automatically when you enable it."));
- return true;
- }
-};
-
-class BSAssign : public Module
-{
- ExtensibleItem<bool> nobot;
-
- CommandBSAssign commandbsassign;
- CommandBSUnassign commandbsunassign;
- CommandBSSetNoBot commandbssetnobot;
-
- public:
- BSAssign(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- nobot(this, "BS_NOBOT"),
- commandbsassign(this), commandbsunassign(this), commandbssetnobot(this)
- {
- }
-
- void OnInvite(User *source, Channel *c, User *targ) anope_override
- {
- BotInfo *bi;
- if (Anope::ReadOnly || !c->ci || targ->server != Me || !(bi = dynamic_cast<BotInfo *>(targ)))
- return;
-
- AccessGroup access = c->ci->AccessFor(source);
- if (nobot.HasExt(c->ci) || (!access.HasPriv("ASSIGN") && !source->HasPriv("botserv/administration")))
- {
- targ->SendMessage(bi, ACCESS_DENIED);
- return;
- }
-
- if (bi->oper_only && !source->HasPriv("botserv/administration"))
- {
- targ->SendMessage(bi, ACCESS_DENIED);
- return;
- }
-
- if (c->ci->bi == bi)
- {
- targ->SendMessage(bi, _("Bot \002%s\002 is already assigned to channel \002%s\002."), c->ci->bi->nick.c_str(), c->name.c_str());
- return;
- }
-
- bi->Assign(source, c->ci);
- targ->SendMessage(bi, _("Bot \002%s\002 has been assigned to %s."), bi->nick.c_str(), c->name.c_str());
- }
-
- void OnBotInfo(CommandSource &source, BotInfo *bi, ChannelInfo *ci, InfoFormatter &info) anope_override
- {
- if (nobot.HasExt(ci))
- info.AddOption(_("No bot"));
- }
-};
-
-MODULE_INIT(BSAssign)
diff --git a/modules/commands/bs_badwords.cpp b/modules/commands/bs_badwords.cpp
deleted file mode 100644
index 57731b168..000000000
--- a/modules/commands/bs_badwords.cpp
+++ /dev/null
@@ -1,469 +0,0 @@
-/* BotServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-#include "modules/bs_badwords.h"
-
-struct BadWordImpl : BadWord, Serializable
-{
- BadWordImpl() : Serializable("BadWord") { }
- ~BadWordImpl();
-
- void Serialize(Serialize::Data &data) const anope_override
- {
- data["ci"] << this->chan;
- data["word"] << this->word;
- data.SetType("type", Serialize::Data::DT_INT); data["type"] << this->type;
- }
-
- static Serializable* Unserialize(Serializable *obj, Serialize::Data &);
-};
-
-struct BadWordsImpl : BadWords
-{
- Serialize::Reference<ChannelInfo> ci;
- typedef std::vector<BadWordImpl *> list;
- Serialize::Checker<list> badwords;
-
- BadWordsImpl(Extensible *obj) : ci(anope_dynamic_static_cast<ChannelInfo *>(obj)), badwords("BadWord") { }
-
- ~BadWordsImpl();
-
- BadWord* AddBadWord(const Anope::string &word, BadWordType type) anope_override
- {
- BadWordImpl *bw = new BadWordImpl();
- bw->chan = ci->name;
- bw->word = word;
- bw->type = type;
-
- this->badwords->push_back(bw);
-
- FOREACH_MOD(OnBadWordAdd, (ci, bw));
-
- return bw;
- }
-
- BadWord* GetBadWord(unsigned index) const anope_override
- {
- if (this->badwords->empty() || index >= this->badwords->size())
- return NULL;
-
- BadWordImpl *bw = (*this->badwords)[index];
- bw->QueueUpdate();
- return bw;
- }
-
- unsigned GetBadWordCount() const anope_override
- {
- return this->badwords->size();
- }
-
- void EraseBadWord(unsigned index) anope_override
- {
- if (this->badwords->empty() || index >= this->badwords->size())
- return;
-
- FOREACH_MOD(OnBadWordDel, (ci, (*this->badwords)[index]));
-
- delete this->badwords->at(index);
- }
-
- void ClearBadWords() anope_override
- {
- while (!this->badwords->empty())
- delete this->badwords->back();
- }
-
- void Check() anope_override
- {
- if (this->badwords->empty())
- ci->Shrink<BadWords>("badwords");
- }
-};
-
-BadWordsImpl::~BadWordsImpl()
-{
- for (list::iterator it = badwords->begin(); it != badwords->end();)
- {
- BadWord *bw = *it;
- ++it;
- delete bw;
- }
-}
-
-BadWordImpl::~BadWordImpl()
-{
- ChannelInfo *ci = ChannelInfo::Find(chan);
- if (ci)
- {
- BadWordsImpl *badwords = ci->GetExt<BadWordsImpl>("badwords");
- if (badwords)
- {
- BadWordsImpl::list::iterator it = std::find(badwords->badwords->begin(), badwords->badwords->end(), this);
- if (it != badwords->badwords->end())
- badwords->badwords->erase(it);
- }
- }
-}
-
-Serializable* BadWordImpl::Unserialize(Serializable *obj, Serialize::Data &data)
-{
- Anope::string sci, sword;
-
- data["ci"] >> sci;
- data["word"] >> sword;
-
- ChannelInfo *ci = ChannelInfo::Find(sci);
- if (!ci)
- return NULL;
-
- unsigned int n;
- data["type"] >> n;
-
- BadWordImpl *bw;
- if (obj)
- bw = anope_dynamic_static_cast<BadWordImpl *>(obj);
- else
- bw = new BadWordImpl();
- bw->chan = sci;
- bw->word = sword;
- bw->type = static_cast<BadWordType>(n);
-
- BadWordsImpl *bws = ci->Require<BadWordsImpl>("badwords");
- if (!obj)
- bws->badwords->push_back(bw);
-
- return bw;
-}
-
-class BadwordsDelCallback : public NumberList
-{
- CommandSource &source;
- ChannelInfo *ci;
- BadWords *bw;
- Command *c;
- unsigned deleted;
- bool override;
- public:
- BadwordsDelCallback(CommandSource &_source, ChannelInfo *_ci, Command *_c, const Anope::string &list) : NumberList(list, true), source(_source), ci(_ci), c(_c), deleted(0), override(false)
- {
- if (!source.AccessFor(ci).HasPriv("BADWORDS") && source.HasPriv("botserv/administration"))
- this->override = true;
- bw = ci->Require<BadWords>("badwords");
- }
-
- ~BadwordsDelCallback()
- {
- if (!deleted)
- source.Reply(_("No matching entries on %s bad words list."), ci->name.c_str());
- else if (deleted == 1)
- source.Reply(_("Deleted 1 entry from %s bad words list."), ci->name.c_str());
- else
- source.Reply(_("Deleted %d entries from %s bad words list."), deleted, ci->name.c_str());
- }
-
- void HandleNumber(unsigned Number) anope_override
- {
- if (!bw || !Number || Number > bw->GetBadWordCount())
- return;
-
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, c, ci) << "DEL " << bw->GetBadWord(Number - 1)->word;
- ++deleted;
- bw->EraseBadWord(Number - 1);
- }
-};
-
-class CommandBSBadwords : public Command
-{
- private:
- void DoList(CommandSource &source, ChannelInfo *ci, const Anope::string &word)
- {
- bool override = !source.AccessFor(ci).HasPriv("BADWORDS");
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "LIST";
- ListFormatter list(source.GetAccount());
- BadWords *bw = ci->GetExt<BadWords>("badwords");
-
- list.AddColumn(_("Number")).AddColumn(_("Word")).AddColumn(_("Type"));
-
- if (!bw || !bw->GetBadWordCount())
- {
- source.Reply(_("%s bad words list is empty."), ci->name.c_str());
- return;
- }
- else if (!word.empty() && word.find_first_not_of("1234567890,-") == Anope::string::npos)
- {
- class BadwordsListCallback : public NumberList
- {
- ListFormatter &list;
- BadWords *bw;
- public:
- BadwordsListCallback(ListFormatter &_list, BadWords *_bw, const Anope::string &numlist) : NumberList(numlist, false), list(_list), bw(_bw)
- {
- }
-
- void HandleNumber(unsigned Number) anope_override
- {
- if (!Number || Number > bw->GetBadWordCount())
- return;
-
- const BadWord *b = bw->GetBadWord(Number - 1);
- ListFormatter::ListEntry entry;
- entry["Number"] = stringify(Number);
- entry["Word"] = b->word;
- entry["Type"] = b->type == BW_SINGLE ? "(SINGLE)" : (b->type == BW_START ? "(START)" : (b->type == BW_END ? "(END)" : ""));
- this->list.AddEntry(entry);
- }
- }
- nl_list(list, bw, word);
- nl_list.Process();
- }
- else
- {
- for (unsigned i = 0, end = bw->GetBadWordCount(); i < end; ++i)
- {
- const BadWord *b = bw->GetBadWord(i);
-
- if (!word.empty() && !Anope::Match(b->word, word))
- continue;
-
- ListFormatter::ListEntry entry;
- entry["Number"] = stringify(i + 1);
- entry["Word"] = b->word;
- entry["Type"] = b->type == BW_SINGLE ? "(SINGLE)" : (b->type == BW_START ? "(START)" : (b->type == BW_END ? "(END)" : ""));
- list.AddEntry(entry);
- }
- }
-
- if (list.IsEmpty())
- source.Reply(_("No matching entries on %s bad words list."), ci->name.c_str());
- else
- {
- std::vector<Anope::string> replies;
- list.Process(replies);
-
- source.Reply(_("Bad words list for %s:"), ci->name.c_str());
-
- for (unsigned i = 0; i < replies.size(); ++i)
- source.Reply(replies[i]);
-
- source.Reply(_("End of bad words list."));
- }
- }
-
- void DoAdd(CommandSource &source, ChannelInfo *ci, const Anope::string &word)
- {
- size_t pos = word.rfind(' ');
- BadWordType bwtype = BW_ANY;
- Anope::string realword = word;
- BadWords *badwords = ci->Require<BadWords>("badwords");
-
- if (pos != Anope::string::npos)
- {
- Anope::string opt = word.substr(pos + 1);
- if (!opt.empty())
- {
- if (opt.equals_ci("SINGLE"))
- bwtype = BW_SINGLE;
- else if (opt.equals_ci("START"))
- bwtype = BW_START;
- else if (opt.equals_ci("END"))
- bwtype = BW_END;
- }
- realword = word.substr(0, pos);
- }
-
- unsigned badwordsmax = Config->GetModule(this->module)->Get<unsigned>("badwordsmax");
- if (badwords->GetBadWordCount() >= badwordsmax)
- {
- source.Reply(_("Sorry, you can only have %d bad words entries on a channel."), badwordsmax);
- return;
- }
-
- bool casesensitive = Config->GetModule(this->module)->Get<bool>("casesensitive");
-
- for (unsigned i = 0, end = badwords->GetBadWordCount(); i < end; ++i)
- {
- const BadWord *bw = badwords->GetBadWord(i);
-
- if ((casesensitive && realword.equals_cs(bw->word)) || (!casesensitive && realword.equals_ci(bw->word)))
- {
- source.Reply(_("\002%s\002 already exists in %s bad words list."), bw->word.c_str(), ci->name.c_str());
- return;
- }
- }
-
- bool override = !source.AccessFor(ci).HasPriv("BADWORDS");
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "ADD " << realword;
- badwords->AddBadWord(realword, bwtype);
-
- source.Reply(_("\002%s\002 added to %s bad words list."), realword.c_str(), ci->name.c_str());
- }
-
- void DoDelete(CommandSource &source, ChannelInfo *ci, const Anope::string &word)
- {
- BadWords *badwords = ci->GetExt<BadWords>("badwords");
-
- if (!badwords || !badwords->GetBadWordCount())
- {
- source.Reply(_("%s bad words list is empty."), ci->name.c_str());
- return;
- }
-
- /* Special case: is it a number/list? Only do search if it isn't. */
- if (!word.empty() && isdigit(word[0]) && word.find_first_not_of("1234567890,-") == Anope::string::npos)
- {
- BadwordsDelCallback list(source, ci, this, word);
- list.Process();
- }
- else
- {
- unsigned i, end;
- const BadWord *badword;
-
- for (i = 0, end = badwords->GetBadWordCount(); i < end; ++i)
- {
- badword = badwords->GetBadWord(i);
-
- if (word.equals_ci(badword->word))
- break;
- }
-
- if (i == end)
- {
- source.Reply(_("\002%s\002 not found on %s bad words list."), word.c_str(), ci->name.c_str());
- return;
- }
-
- bool override = !source.AccessFor(ci).HasPriv("BADWORDS");
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "DEL " << badword->word;
-
- source.Reply(_("\002%s\002 deleted from %s bad words list."), badword->word.c_str(), ci->name.c_str());
-
- badwords->EraseBadWord(i);
- }
-
- badwords->Check();
- }
-
- void DoClear(CommandSource &source, ChannelInfo *ci)
- {
- bool override = !source.AccessFor(ci).HasPriv("BADWORDS");
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "CLEAR";
-
- BadWords *badwords = ci->GetExt<BadWords>("badwords");
- if (badwords)
- badwords->ClearBadWords();
- source.Reply(_("Bad words list is now empty."));
- }
-
- public:
- CommandBSBadwords(Module *creator) : Command(creator, "botserv/badwords", 2, 3)
- {
- this->SetDesc(_("Maintains the bad words list"));
- this->SetSyntax(_("\037channel\037 ADD \037word\037 [\037SINGLE\037 | \037START\037 | \037END\037]"));
- this->SetSyntax(_("\037channel\037 DEL {\037word\037 | \037entry-num\037 | \037list\037}"));
- this->SetSyntax(_("\037channel\037 LIST [\037mask\037 | \037list\037]"));
- this->SetSyntax(_("\037channel\037 CLEAR"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &cmd = params[1];
- const Anope::string &word = params.size() > 2 ? params[2] : "";
- bool need_args = cmd.equals_ci("LIST") || cmd.equals_ci("CLEAR");
-
- if (!need_args && word.empty())
- {
- this->OnSyntaxError(source, cmd);
- return;
- }
-
- ChannelInfo *ci = ChannelInfo::Find(params[0]);
- if (ci == NULL)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
- return;
- }
-
- if (!source.AccessFor(ci).HasPriv("BADWORDS") && (!need_args || !source.HasPriv("botserv/administration")))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- if (Anope::ReadOnly)
- {
- source.Reply(_("Sorry, bad words list modification is temporarily disabled."));
- return;
- }
-
- if (cmd.equals_ci("ADD"))
- return this->DoAdd(source, ci, word);
- else if (cmd.equals_ci("DEL"))
- return this->DoDelete(source, ci, word);
- else if (cmd.equals_ci("LIST"))
- return this->DoList(source, ci, word);
- else if (cmd.equals_ci("CLEAR"))
- return this->DoClear(source, ci);
- else
- this->OnSyntaxError(source, "");
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Maintains the \002bad words list\002 for a channel. The bad\n"
- "words list determines which words are to be kicked\n"
- "when the bad words kicker is enabled. For more information,\n"
- "type \002%s%s HELP KICK %s\002.\n"
- " \n"
- "The \002ADD\002 command adds the given word to the\n"
- "bad words list. If SINGLE is specified, a kick will be\n"
- "done only if a user says the entire word. If START is\n"
- "specified, a kick will be done if a user says a word\n"
- "that starts with \037word\037. If END is specified, a kick\n"
- "will be done if a user says a word that ends with\n"
- "\037word\037. If you don't specify anything, a kick will\n"
- "be issued every time \037word\037 is said by a user.\n"
- " \n"), Config->StrictPrivmsg.c_str(), source.service->nick.c_str(), source.command.c_str());
- source.Reply(_("The \002DEL\002 command removes the given word from the\n"
- "bad words list. If a list of entry numbers is given, those\n"
- "entries are deleted. (See the example for LIST below.)\n"
- " \n"
- "The \002LIST\002 command displays the bad words list. If\n"
- "a wildcard mask is given, only those entries matching the\n"
- "mask are displayed. If a list of entry numbers is given,\n"
- "only those entries are shown; for example:\n"
- " \002#channel LIST 2-5,7-9\002\n"
- " Lists bad words entries numbered 2 through 5 and\n"
- " 7 through 9.\n"
- " \n"
- "The \002CLEAR\002 command clears all entries from the\n"
- "bad words list."));
- return true;
- }
-};
-
-class BSBadwords : public Module
-{
- CommandBSBadwords commandbsbadwords;
- ExtensibleItem<BadWordsImpl> badwords;
- Serialize::Type badword_type;
-
- public:
- BSBadwords(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandbsbadwords(this), badwords(this, "badwords"), badword_type("BadWord", BadWordImpl::Unserialize)
- {
- }
-};
-
-MODULE_INIT(BSBadwords)
diff --git a/modules/commands/bs_bot.cpp b/modules/commands/bs_bot.cpp
deleted file mode 100644
index 6a152937a..000000000
--- a/modules/commands/bs_bot.cpp
+++ /dev/null
@@ -1,385 +0,0 @@
-/* BotServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class CommandBSBot : public Command
-{
- private:
- void DoAdd(CommandSource &source, const std::vector<Anope::string> &params)
- {
- const Anope::string &nick = params[1];
- const Anope::string &user = params[2];
- const Anope::string &host = params[3];
- const Anope::string &real = params[4];
-
- if (BotInfo::Find(nick, true))
- {
- source.Reply(_("Bot \002%s\002 already exists."), nick.c_str());
- return;
- }
-
- Configuration::Block *networkinfo = Config->GetBlock("networkinfo");
-
- if (nick.length() > networkinfo->Get<unsigned>("nicklen"))
- {
- source.Reply(_("Bot nicks may only be %d characters long."), networkinfo->Get<unsigned>("nicklen"));
- return;
- }
-
- if (user.length() > networkinfo->Get<unsigned>("userlen"))
- {
- source.Reply(_("Bot idents may only be %d characters long."), networkinfo->Get<unsigned>("userlen"));
- return;
- }
-
- if (host.length() > networkinfo->Get<unsigned>("hostlen"))
- {
- source.Reply(_("Bot hosts may only be %d characters long."), networkinfo->Get<unsigned>("hostlen"));
- return;
- }
-
- if (!IRCD->IsNickValid(nick))
- {
- source.Reply(_("Bot nicks may only contain valid nick characters."));
- return;
- }
-
- if (!IRCD->IsIdentValid(user))
- {
- source.Reply(_("Bot idents may only contain valid ident characters."));
- return;
- }
-
- if (!IRCD->IsHostValid(host))
- {
- source.Reply(_("Bot hosts may only contain valid host characters."));
- return;
- }
-
- /* We check whether the nick is registered, and inform the user
- * if so. You need to drop the nick manually before you can use
- * it as a bot nick from now on -GD
- */
- if (NickAlias::Find(nick))
- {
- source.Reply(NICK_ALREADY_REGISTERED, nick.c_str());
- return;
- }
-
- User *u = User::Find(nick, true);
- if (u)
- {
- source.Reply(_("Nick \002%s\002 is currently in use."), u->nick.c_str());
- return;
- }
-
- BotInfo *bi = new BotInfo(nick, user, host, real);
-
- Log(LOG_ADMIN, source, this) << "ADD " << bi->GetMask() << " " << bi->realname;
-
- source.Reply(_("%s!%s@%s (%s) added to the bot list."), bi->nick.c_str(), bi->GetIdent().c_str(), bi->host.c_str(), bi->realname.c_str());
-
- FOREACH_MOD(OnBotCreate, (bi));
- return;
- }
-
- void DoChange(CommandSource &source, const std::vector<Anope::string> &params)
- {
- const Anope::string &oldnick = params[1];
- const Anope::string &nick = params.size() > 2 ? params[2] : "";
- const Anope::string &user = params.size() > 3 ? params[3] : "";
- const Anope::string &host = params.size() > 4 ? params[4] : "";
- const Anope::string &real = params.size() > 5 ? params[5] : "";
-
- if (oldnick.empty() || nick.empty())
- {
- this->OnSyntaxError(source, "CHANGE");
- return;
- }
-
- BotInfo *bi = BotInfo::Find(oldnick, true);
- if (!bi)
- {
- source.Reply(BOT_DOES_NOT_EXIST, oldnick.c_str());
- return;
- }
-
- if (bi->conf)
- {
- source.Reply(_("Bot %s is not changeable."), bi->nick.c_str());
- return;
- }
-
- Configuration::Block *networkinfo = Config->GetBlock("networkinfo");
-
- if (nick.length() > networkinfo->Get<unsigned>("nicklen"))
- {
- source.Reply(_("Bot nicks may only be %d characters long."), networkinfo->Get<unsigned>("nicklen"));
- return;
- }
-
- if (user.length() > networkinfo->Get<unsigned>("userlen"))
- {
- source.Reply(_("Bot idents may only be %d characters long."), networkinfo->Get<unsigned>("userlen"));
- return;
- }
-
- if (host.length() > networkinfo->Get<unsigned>("hostlen"))
- {
- source.Reply(_("Bot hosts may only be %d characters long."), networkinfo->Get<unsigned>("hostlen"));
- return;
- }
-
- /* Checks whether there *are* changes.
- * Case sensitive because we may want to change just the case.
- * And we must finally check that the nick is not already
- * taken by another bot.
- */
- if (nick.equals_cs(bi->nick) && (!user.empty() ? user.equals_cs(bi->GetIdent()) : 1) && (!host.empty() ? host.equals_cs(bi->host) : 1) && (!real.empty() ? real.equals_cs(bi->realname) : 1))
- {
- source.Reply(_("The old information is the same as the new information specified."));
- return;
- }
-
- if (!IRCD->IsNickValid(nick))
- {
- source.Reply(_("Bot nicks may only contain valid nick characters."));
- return;
- }
-
- if (!user.empty() && !IRCD->IsIdentValid(user))
- {
- source.Reply(_("Bot idents may only contain valid ident characters."));
- return;
- }
-
- if (!host.empty() && !IRCD->IsHostValid(host))
- {
- source.Reply(_("Bot hosts may only contain valid host characters."));
- return;
- }
-
- if (!nick.equals_ci(bi->nick))
- {
- if (BotInfo::Find(nick, true))
- {
- source.Reply(_("Bot \002%s\002 already exists."), nick.c_str());
- return;
- }
-
- if (User::Find(nick, true))
- {
- source.Reply(_("Nick \002%s\002 is currently in use."), nick.c_str());
- return;
- }
- }
-
- if (!nick.equals_ci(bi->nick))
- {
- /* We check whether the nick is registered, and inform the user
- * if so. You need to drop the nick manually before you can use
- * it as a bot nick from now on -GD
- */
- if (NickAlias::Find(nick))
- {
- source.Reply(NICK_ALREADY_REGISTERED, nick.c_str());
- return;
- }
-
- /* The new nick is really different, so we remove the Q line for the old nick. */
- XLine x_del(bi->nick);
- IRCD->SendSQLineDel(&x_del);
-
- /* Add a Q line for the new nick */
- XLine x(nick, "Reserved for services");
- IRCD->SendSQLine(NULL, &x);
- }
-
- if (!user.empty())
- {
- IRCD->SendQuit(bi, "Quit: Be right back");
- bi->introduced = false;
- }
- else
- IRCD->SendNickChange(bi, nick);
-
- if (!nick.equals_cs(bi->nick))
- bi->SetNewNick(nick);
-
- if (!user.empty() && !user.equals_cs(bi->GetIdent()))
- bi->SetIdent(user);
- if (!host.empty() && !host.equals_cs(bi->host))
- bi->host = host;
- if (!real.empty() && !real.equals_cs(bi->realname))
- bi->realname = real;
-
- if (!user.empty())
- bi->OnKill();
-
- source.Reply(_("Bot \002%s\002 has been changed to %s!%s@%s (%s)."), oldnick.c_str(), bi->nick.c_str(), bi->GetIdent().c_str(), bi->host.c_str(), bi->realname.c_str());
- Log(LOG_ADMIN, source, this) << "CHANGE " << oldnick << " to " << bi->GetMask() << " " << bi->realname;
-
- FOREACH_MOD(OnBotChange, (bi));
- return;
- }
-
- void DoDel(CommandSource &source, const std::vector<Anope::string> &params)
- {
- const Anope::string &nick = params[1];
-
- if (nick.empty())
- {
- this->OnSyntaxError(source, "DEL");
- return;
- }
-
- BotInfo *bi = BotInfo::Find(nick, true);
- if (!bi)
- {
- source.Reply(BOT_DOES_NOT_EXIST, nick.c_str());
- return;
- }
-
- if (bi->conf)
- {
- source.Reply(_("Bot %s is not deletable."), bi->nick.c_str());
- return;
- }
-
- FOREACH_MOD(OnBotDelete, (bi));
-
- Log(LOG_ADMIN, source, this) << "DEL " << bi->nick;
-
- source.Reply(_("Bot \002%s\002 has been deleted."), nick.c_str());
- delete bi;
- return;
- }
- public:
- CommandBSBot(Module *creator) : Command(creator, "botserv/bot", 1, 6)
- {
- this->SetDesc(_("Maintains network bot list"));
- this->SetSyntax(_("\002ADD \037nick\037 \037user\037 \037host\037 \037real\037\002"));
- this->SetSyntax(_("\002CHANGE \037oldnick\037 \037newnick\037 [\037user\037 [\037host\037 [\037real\037]]]\002"));
- this->SetSyntax(_("\002DEL \037nick\037\002"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &cmd = params[0];
-
- if (Anope::ReadOnly)
- {
- source.Reply(_("Sorry, bot modification is temporarily disabled."));
- return;
- }
-
- if (cmd.equals_ci("ADD"))
- {
- // ADD nick user host real - 5
- if (!source.HasCommand("botserv/bot/add"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- if (params.size() < 5)
- {
- this->OnSyntaxError(source, "ADD");
- return;
- }
-
- std::vector<Anope::string> tempparams = params;
- // ADD takes less params than CHANGE, so we need to take 6 if given and append it with a space to 5.
- if (tempparams.size() >= 6)
- tempparams[4] = tempparams[4] + " " + tempparams[5];
-
- return this->DoAdd(source, tempparams);
- }
- else if (cmd.equals_ci("CHANGE"))
- {
- // CHANGE oldn newn user host real - 6
- // but only oldn and newn are required
- if (!source.HasCommand("botserv/bot/change"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- if (params.size() < 3)
- {
- this->OnSyntaxError(source, "CHANGE");
- return;
- }
-
- return this->DoChange(source, params);
- }
- else if (cmd.equals_ci("DEL"))
- {
- // DEL nick
- if (!source.HasCommand("botserv/bot/del"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- if (params.size() < 1)
- {
- this->OnSyntaxError(source, "DEL");
- return;
- }
-
- return this->DoDel(source, params);
- }
- else
- this->OnSyntaxError(source, "");
-
- return;
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Allows Services Operators to create, modify, and delete\n"
- "bots that users will be able to use on their own\n"
- "channels.\n"
- " \n"
- "\002BOT ADD\002 adds a bot with the given nickname, username,\n"
- "hostname and realname. Since no integrity checks are done\n"
- "for these settings, be really careful.\n"
- " \n"
- "\002BOT CHANGE\002 allows you to change the nickname, username, hostname\n"
- "or realname of a bot without deleting it (and\n"
- "all the data associated with it).\n"
- " \n"
- "\002BOT DEL\002 removes the given bot from the bot list.\n"
- " \n"
- "\002Note\002: You cannot create a bot with a nick that is\n"
- "currently registered. If an unregistered user is currently\n"
- "using the nick, they will be killed."));
- return true;
- }
-};
-
-class BSBot : public Module
-{
- CommandBSBot commandbsbot;
-
- public:
- BSBot(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandbsbot(this)
- {
-
- }
-};
-
-MODULE_INIT(BSBot)
diff --git a/modules/commands/bs_botlist.cpp b/modules/commands/bs_botlist.cpp
deleted file mode 100644
index 089c2894d..000000000
--- a/modules/commands/bs_botlist.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-/* BotServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class CommandBSBotList : public Command
-{
- public:
- CommandBSBotList(Module *creator) : Command(creator, "botserv/botlist", 0, 0)
- {
- this->SetDesc(_("Lists available bots"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- unsigned count = 0;
- ListFormatter list(source.GetAccount());
-
- list.AddColumn(_("Nick")).AddColumn(_("Mask"));
-
- for (botinfo_map::const_iterator it = BotListByNick->begin(), it_end = BotListByNick->end(); it != it_end; ++it)
- {
- BotInfo *bi = it->second;
-
- if (source.HasPriv("botserv/administration") || !bi->oper_only)
- {
- ++count;
- ListFormatter::ListEntry entry;
- entry["Nick"] = (bi->oper_only ? "* " : "") + bi->nick;
- entry["Mask"] = bi->GetIdent() + "@" + bi->host;
- list.AddEntry(entry);
- }
- }
-
- std::vector<Anope::string> replies;
- list.Process(replies);
-
- if (!count)
- source.Reply(_("There are no bots available at this time.\n"
- "Ask a Services Operator to create one!"));
- else
- {
- source.Reply(_("Bot list:"));
-
- for (unsigned i = 0; i < replies.size(); ++i)
- source.Reply(replies[i]);
-
- source.Reply(_("%d bots available."), count);
- }
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Lists all available bots on this network.\n"
- "Bots prefixed by a * are reserved for IRC Operators."));
- return true;
- }
-};
-
-class BSBotList : public Module
-{
- CommandBSBotList commandbsbotlist;
-
- public:
- BSBotList(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandbsbotlist(this)
- {
-
- }
-};
-
-MODULE_INIT(BSBotList)
diff --git a/modules/commands/bs_control.cpp b/modules/commands/bs_control.cpp
deleted file mode 100644
index bdacf8dd1..000000000
--- a/modules/commands/bs_control.cpp
+++ /dev/null
@@ -1,146 +0,0 @@
-/* BotServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class CommandBSSay : public Command
-{
- public:
- CommandBSSay(Module *creator) : Command(creator, "botserv/say", 2, 2)
- {
- this->SetDesc(_("Makes the bot say the specified text on the specified channel"));
- this->SetSyntax(_("\037channel\037 \037text\037"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &text = params[1];
-
- ChannelInfo *ci = ChannelInfo::Find(params[0]);
- if (ci == NULL)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
- return;
- }
-
- if (!source.AccessFor(ci).HasPriv("SAY") && !source.HasPriv("botserv/administration"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- if (!ci->bi)
- {
- source.Reply(BOT_NOT_ASSIGNED);
- return;
- }
-
- if (!ci->c || !ci->c->FindUser(ci->bi))
- {
- source.Reply(BOT_NOT_ON_CHANNEL, ci->name.c_str());
- return;
- }
-
- if (text[0] == '\001')
- {
- this->OnSyntaxError(source, "");
- return;
- }
-
- IRCD->SendPrivmsg(*ci->bi, ci->name, "%s", text.c_str());
- ci->bi->lastmsg = Anope::CurTime;
-
- bool override = !source.AccessFor(ci).HasPriv("SAY");
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to say: " << text;
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Makes the bot say the specified text on the specified channel."));
- return true;
- }
-};
-
-class CommandBSAct : public Command
-{
- public:
- CommandBSAct(Module *creator) : Command(creator, "botserv/act", 2, 2)
- {
- this->SetDesc(_("Makes the bot do the equivalent of a \"/me\" command"));
- this->SetSyntax(_("\037channel\037 \037text\037"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- Anope::string message = params[1];
-
- ChannelInfo *ci = ChannelInfo::Find(params[0]);
- if (ci == NULL)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
- return;
- }
-
- if (!source.AccessFor(ci).HasPriv("SAY") && !source.HasPriv("botserv/administration"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- if (!ci->bi)
- {
- source.Reply(BOT_NOT_ASSIGNED);
- return;
- }
-
- if (!ci->c || !ci->c->FindUser(ci->bi))
- {
- source.Reply(BOT_NOT_ON_CHANNEL, ci->name.c_str());
- return;
- }
-
- message = message.replace_all_cs("\1", "");
- if (message.empty())
- return;
-
- IRCD->SendAction(*ci->bi, ci->name, "%s", message.c_str());
- ci->bi->lastmsg = Anope::CurTime;
-
- bool override = !source.AccessFor(ci).HasPriv("SAY");
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to say: " << message;
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Makes the bot do the equivalent of a \"/me\" command\n"
- "on the specified channel using the specified text."));
- return true;
- }
-};
-
-class BSControl : public Module
-{
- CommandBSSay commandbssay;
- CommandBSAct commandbsact;
-
- public:
- BSControl(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandbssay(this), commandbsact(this)
- {
-
- }
-};
-
-MODULE_INIT(BSControl)
diff --git a/modules/commands/bs_info.cpp b/modules/commands/bs_info.cpp
deleted file mode 100644
index 76dcfa4f4..000000000
--- a/modules/commands/bs_info.cpp
+++ /dev/null
@@ -1,134 +0,0 @@
-/* BotServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class CommandBSInfo : public Command
-{
- private:
- void send_bot_channels(std::vector<Anope::string> &buffers, const BotInfo *bi)
- {
- Anope::string buf;
- for (registered_channel_map::const_iterator it = RegisteredChannelList->begin(), it_end = RegisteredChannelList->end(); it != it_end; ++it)
- {
- const ChannelInfo *ci = it->second;
-
- if (ci->bi == bi)
- {
- buf += " " + ci->name + " ";
- if (buf.length() > 300)
- {
- buffers.push_back(buf);
- buf.clear();
- }
- }
- }
- if (!buf.empty())
- buffers.push_back(buf);
- }
-
- public:
- CommandBSInfo(Module *creator) : Command(creator, "botserv/info", 1, 1)
- {
- this->SetSyntax(_("{\037channel\037 | \037nickname\037}"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &query = params[0];
-
- BotInfo *bi = BotInfo::Find(query, true);
- ChannelInfo *ci = ChannelInfo::Find(query);
- InfoFormatter info(source.nc);
-
- if (bi)
- {
- source.Reply(_("Information for bot \002%s\002:"), bi->nick.c_str());
- info[_("Mask")] = bi->GetIdent() + "@" + bi->host;
- info[_("Real name")] = bi->realname;
- info[_("Created")] = Anope::strftime(bi->created, source.GetAccount());
- info[_("Options")] = bi->oper_only ? _("Private") : _("None");
- info[_("Used on")] = stringify(bi->GetChannelCount()) + " channel(s)";
-
- FOREACH_MOD(OnBotInfo, (source, bi, ci, info));
-
- std::vector<Anope::string> replies;
- info.Process(replies);
-
- for (unsigned i = 0; i < replies.size(); ++i)
- source.Reply(replies[i]);
-
- if (source.HasPriv("botserv/administration"))
- {
- std::vector<Anope::string> buf;
- this->send_bot_channels(buf, bi);
- for (unsigned i = 0; i < buf.size(); ++i)
- source.Reply(buf[i]);
- }
-
- }
- else if (ci)
- {
- if (!source.AccessFor(ci).HasPriv("INFO") && !source.HasPriv("botserv/administration"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- source.Reply(CHAN_INFO_HEADER, ci->name.c_str());
- info[_("Bot nick")] = ci->bi ? ci->bi->nick : _("not assigned yet");
-
- Anope::string enabled = Language::Translate(source.nc, _("Enabled"));
- Anope::string disabled = Language::Translate(source.nc, _("Disabled"));
-
- FOREACH_MOD(OnBotInfo, (source, bi, ci, info));
-
- std::vector<Anope::string> replies;
- info.Process(replies);
-
- for (unsigned i = 0; i < replies.size(); ++i)
- source.Reply(replies[i]);
- }
- else
- source.Reply(_("\002%s\002 is not a valid bot or registered channel."), query.c_str());
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Allows you to see %s information about a channel or a bot.\n"
- "If the parameter is a channel, then you'll get information\n"
- "such as enabled kickers. If the parameter is a nick,\n"
- "you'll get information about a bot, such as creation\n"
- "time or number of channels it is on."), source.service->nick.c_str());
- return true;
- }
-
- const Anope::string GetDesc(CommandSource &source) const anope_override
- {
- return Anope::printf(Language::Translate(source.GetAccount(), _("Allows you to see %s information about a channel or a bot")), source.service->nick.c_str());
- }
-};
-
-class BSInfo : public Module
-{
- CommandBSInfo commandbsinfo;
-
- public:
- BSInfo(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandbsinfo(this)
- {
-
- }
-};
-
-MODULE_INIT(BSInfo)
diff --git a/modules/commands/bs_kick.cpp b/modules/commands/bs_kick.cpp
deleted file mode 100644
index 1897bc5e6..000000000
--- a/modules/commands/bs_kick.cpp
+++ /dev/null
@@ -1,1474 +0,0 @@
-/* BotServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-#include "modules/bs_kick.h"
-#include "modules/bs_badwords.h"
-
-static Module *me;
-
-struct KickerDataImpl : KickerData
-{
- KickerDataImpl(Extensible *obj)
- {
- amsgs = badwords = bolds = caps = colors = flood = italics = repeat = reverses = underlines = false;
- for (int16_t i = 0; i < TTB_SIZE; ++i)
- ttb[i] = 0;
- capsmin = capspercent = 0;
- floodlines = floodsecs = 0;
- repeattimes = 0;
-
- dontkickops = dontkickvoices = false;
- }
-
- void Check(ChannelInfo *ci) anope_override
- {
- if (amsgs || badwords || bolds || caps || colors || flood || italics || repeat || reverses || underlines)
- return;
-
- ci->Shrink<KickerData>("kickerdata");
- }
-
- struct ExtensibleItem : ::ExtensibleItem<KickerDataImpl>
- {
- ExtensibleItem(Module *m, const Anope::string &ename) : ::ExtensibleItem<KickerDataImpl>(m, ename) { }
-
- void ExtensibleSerialize(const Extensible *e, const Serializable *s, Serialize::Data &data) const anope_override
- {
- if (s->GetSerializableType()->GetName() != "ChannelInfo")
- return;
-
- const ChannelInfo *ci = anope_dynamic_static_cast<const ChannelInfo *>(e);
- KickerData *kd = this->Get(ci);
- if (kd == NULL)
- return;
-
- data["kickerdata:amsgs"] << kd->amsgs;
- data["kickerdata:badwords"] << kd->badwords;
- data["kickerdata:bolds"] << kd->bolds;
- data["kickerdata:caps"] << kd->caps;
- data["kickerdata:colors"] << kd->colors;
- data["kickerdata:flood"] << kd->flood;
- data["kickerdata:italics"] << kd->italics;
- data["kickerdata:repeat"] << kd->repeat;
- data["kickerdata:reverses"] << kd->reverses;
- data["kickerdata:underlines"] << kd->underlines;
-
- data.SetType("capsmin", Serialize::Data::DT_INT); data["capsmin"] << kd->capsmin;
- data.SetType("capspercent", Serialize::Data::DT_INT); data["capspercent"] << kd->capspercent;
- data.SetType("floodlines", Serialize::Data::DT_INT); data["floodlines"] << kd->floodlines;
- data.SetType("floodsecs", Serialize::Data::DT_INT); data["floodsecs"] << kd->floodsecs;
- data.SetType("repeattimes", Serialize::Data::DT_INT); data["repeattimes"] << kd->repeattimes;
- for (int16_t i = 0; i < TTB_SIZE; ++i)
- data["ttb"] << kd->ttb[i] << " ";
- }
-
- void ExtensibleUnserialize(Extensible *e, Serializable *s, Serialize::Data &data) anope_override
- {
- if (s->GetSerializableType()->GetName() != "ChannelInfo")
- return;
-
- ChannelInfo *ci = anope_dynamic_static_cast<ChannelInfo *>(e);
- KickerData *kd = ci->Require<KickerData>("kickerdata");
-
- data["kickerdata:amsgs"] >> kd->amsgs;
- data["kickerdata:badwords"] >> kd->badwords;
- data["kickerdata:bolds"] >> kd->bolds;
- data["kickerdata:caps"] >> kd->caps;
- data["kickerdata:colors"] >> kd->colors;
- data["kickerdata:flood"] >> kd->flood;
- data["kickerdata:italics"] >> kd->italics;
- data["kickerdata:repeat"] >> kd->repeat;
- data["kickerdata:reverses"] >> kd->reverses;
- data["kickerdata:underlines"] >> kd->underlines;
-
- data["capsmin"] >> kd->capsmin;
- data["capspercent"] >> kd->capspercent;
- data["floodlines"] >> kd->floodlines;
- data["floodsecs"] >> kd->floodsecs;
- data["repeattimes"] >> kd->repeattimes;
-
- Anope::string ttb, tok;
- data["ttb"] >> ttb;
- spacesepstream sep(ttb);
- for (int i = 0; sep.GetToken(tok) && i < TTB_SIZE; ++i)
- try
- {
- kd->ttb[i] = convertTo<int16_t>(tok);
- }
- catch (const ConvertException &) { }
-
- kd->Check(ci);
- }
- };
-};
-
-class CommandBSKick : public Command
-{
- public:
- CommandBSKick(Module *creator) : Command(creator, "botserv/kick", 0)
- {
- this->SetDesc(_("Configures kickers"));
- this->SetSyntax(_("\037option\037 \037channel\037 {\037ON|OFF\037} [\037settings\037]"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- this->OnSyntaxError(source, "");
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Configures bot kickers. \037option\037 can be one of:"));
-
- Anope::string this_name = source.command;
- for (CommandInfo::map::const_iterator it = source.service->commands.begin(), it_end = source.service->commands.end(); it != it_end; ++it)
- {
- const Anope::string &c_name = it->first;
- const CommandInfo &info = it->second;
-
- if (c_name.find_ci(this_name + " ") == 0)
- {
- ServiceReference<Command> command("Command", info.name);
- if (command)
- {
- source.command = c_name;
- command->OnServHelp(source);
- }
- }
- }
-
- source.Reply(_("Type \002%s%s HELP %s \037option\037\002 for more information\n"
- "on a specific option.\n"
- " \n"
- "Note: access to this command is controlled by the\n"
- "level SET."), Config->StrictPrivmsg.c_str(), source.service->nick.c_str(), this_name.c_str());
-
- return true;
- }
-};
-
-class CommandBSKickBase : public Command
-{
- public:
- CommandBSKickBase(Module *creator, const Anope::string &cname, int minarg, int maxarg) : Command(creator, cname, minarg, maxarg)
- {
- }
-
- virtual void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override = 0;
-
- virtual bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override = 0;
-
- protected:
- bool CheckArguments(CommandSource &source, const std::vector<Anope::string> &params, ChannelInfo* &ci)
- {
- const Anope::string &chan = params[0];
- const Anope::string &option = params[1];
-
- ci = ChannelInfo::Find(chan);
-
- if (Anope::ReadOnly)
- source.Reply(_("Sorry, kicker configuration is temporarily disabled."));
- else if (ci == NULL)
- source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
- else if (option.empty())
- this->OnSyntaxError(source, "");
- else if (!option.equals_ci("ON") && !option.equals_ci("OFF"))
- this->OnSyntaxError(source, "");
- else if (!source.AccessFor(ci).HasPriv("SET") && !source.HasPriv("botserv/administration"))
- source.Reply(ACCESS_DENIED);
- else if (!ci->bi)
- source.Reply(BOT_NOT_ASSIGNED);
- else
- return true;
-
- return false;
- }
-
- void Process(CommandSource &source, ChannelInfo *ci, const Anope::string &param, const Anope::string &ttb, size_t ttb_idx, const Anope::string &optname, KickerData *kd, bool &val)
- {
- if (param.equals_ci("ON"))
- {
- if (!ttb.empty())
- {
- int16_t i;
-
- try
- {
- i = convertTo<int16_t>(ttb);
- if (i < 0)
- throw ConvertException();
- }
- catch (const ConvertException &)
- {
- source.Reply(_("\002%s\002 cannot be taken as times to ban."), ttb.c_str());
- return;
- }
-
- kd->ttb[ttb_idx] = i;
- }
- else
- kd->ttb[ttb_idx] = 0;
-
- val = true;
- if (kd->ttb[ttb_idx])
- source.Reply(_("Bot will now kick for \002%s\002, and will place a ban\n"
- "after %d kicks for the same user."), optname.c_str(), kd->ttb[ttb_idx]);
- else
- source.Reply(_("Bot will now kick for \002%s\002."), optname.c_str());
-
- bool override = !source.AccessFor(ci).HasPriv("SET");
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to enable the " << optname << " kicker";
- }
- else if (param.equals_ci("OFF"))
- {
- bool override = !source.AccessFor(ci).HasPriv("SET");
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to disable the " << optname << " kicker";
-
- val = false;
- source.Reply(_("Bot won't kick for \002%s\002 anymore."), optname.c_str());
- }
- else
- this->OnSyntaxError(source, "");
- }
-};
-
-class CommandBSKickAMSG : public CommandBSKickBase
-{
- public:
- CommandBSKickAMSG(Module *creator) : CommandBSKickBase(creator, "botserv/kick/amsg", 2, 3)
- {
- this->SetDesc(_("Configures AMSG kicker"));
- this->SetSyntax(_("\037channel\037 {\037ON|OFF\037} [\037ttb\037]"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- ChannelInfo *ci;
- if (CheckArguments(source, params, ci))
- {
- KickerData *kd = ci->Require<KickerData>("kickerdata");
- Process(source, ci, params[1], params.size() > 2 ? params[2] : "", TTB_AMSGS, "AMSG", kd, kd->amsgs);
- kd->Check(ci);
- }
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- BotInfo *bi = Config->GetClient("BotServ");
- source.Reply(_("Sets the AMSG kicker on or off. When enabled, the bot will\n"
- "kick users who send the same message to multiple channels\n"
- "where %s bots are.\n"
- " \n"
- "\037ttb\037 is the number of times a user can be kicked\n"
- "before they get banned. Don't give ttb to disable\n"
- "the ban system once activated."), bi ? bi->nick.c_str() : "BotServ");
- return true;
- }
-};
-
-class CommandBSKickBadwords : public CommandBSKickBase
-{
- public:
- CommandBSKickBadwords(Module *creator) : CommandBSKickBase(creator, "botserv/kick/badwords", 2, 3)
- {
- this->SetDesc(_("Configures badwords kicker"));
- this->SetSyntax(_("\037channel\037 {\037ON|OFF\037} [\037ttb\037]"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- ChannelInfo *ci;
- if (CheckArguments(source, params, ci))
- {
- KickerData *kd = ci->Require<KickerData>("kickerdata");
- Process(source, ci, params[1], params.size() > 2 ? params[2] : "", TTB_BADWORDS, "badwords", kd, kd->badwords);
- kd->Check(ci);
- }
-
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Sets the bad words kicker on or off. When enabled, this\n"
- "option tells the bot to kick users who say certain words\n"
- "on the channels.\n"
- "You can define bad words for your channel using the\n"
- "\002BADWORDS\002 command. Type \002%s%s HELP BADWORDS\002 for\n"
- "more information.\n"
- " \n"
- "\037ttb\037 is the number of times a user can be kicked\n"
- "before it gets banned. Don't give ttb to disable\n"
- "the ban system once activated."), Config->StrictPrivmsg.c_str(), source.service->nick.c_str());
- return true;
- }
-};
-
-class CommandBSKickBolds : public CommandBSKickBase
-{
- public:
- CommandBSKickBolds(Module *creator) : CommandBSKickBase(creator, "botserv/kick/bolds", 2, 3)
- {
- this->SetDesc(_("Configures bolds kicker"));
- this->SetSyntax(_("\037channel\037 {\037ON|OFF\037} [\037ttb\037]"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- ChannelInfo *ci;
- if (CheckArguments(source, params, ci))
- {
- KickerData *kd = ci->Require<KickerData>("kickerdata");
- Process(source, ci, params[1], params.size() > 2 ? params[2] : "", TTB_BOLDS, "bolds", kd, kd->bolds);
- kd->Check(ci);
- }
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Sets the bolds kicker on or off. When enabled, this\n"
- "option tells the bot to kick users who use bolds.\n"
- " \n"
- "\037ttb\037 is the number of times a user can be kicked\n"
- "before it gets banned. Don't give ttb to disable\n"
- "the ban system once activated."));
- return true;
- }
-};
-
-class CommandBSKickCaps : public CommandBSKickBase
-{
- public:
- CommandBSKickCaps(Module *creator) : CommandBSKickBase(creator, "botserv/kick/caps", 2, 5)
- {
- this->SetDesc(_("Configures caps kicker"));
- this->SetSyntax(_("\037channel\037 {\037ON|OFF\037} [\037ttb\037 [\037min\037 [\037percent\037]]]\002"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- ChannelInfo *ci;
- if (!CheckArguments(source, params, ci))
- return;
-
- KickerData *kd = ci->Require<KickerData>("kickerdata");
-
- if (params[1].equals_ci("ON"))
- {
- const Anope::string &ttb = params.size() > 2 ? params[2] : "",
- &min = params.size() > 3 ? params[3] : "",
- &percent = params.size() > 4 ? params[4] : "";
-
- if (!ttb.empty())
- {
- try
- {
- kd->ttb[TTB_CAPS] = convertTo<int16_t>(ttb);
- if (kd->ttb[TTB_CAPS] < 0)
- throw ConvertException();
- }
- catch (const ConvertException &)
- {
- kd->ttb[TTB_CAPS] = 0;
- source.Reply(_("\002%s\002 cannot be taken as times to ban."), ttb.c_str());
- return;
- }
- }
- else
- kd->ttb[TTB_CAPS] = 0;
-
- kd->capsmin = 10;
- try
- {
- kd->capsmin = convertTo<int16_t>(min);
- }
- catch (const ConvertException &) { }
- if (kd->capsmin < 1)
- kd->capsmin = 10;
-
- kd->capspercent = 25;
- try
- {
- kd->capspercent = convertTo<int16_t>(percent);
- }
- catch (const ConvertException &) { }
- if (kd->capspercent < 1 || kd->capspercent > 100)
- kd->capspercent = 25;
-
- kd->caps = true;
- if (kd->ttb[TTB_CAPS])
- source.Reply(_("Bot will now kick for \002caps\002 (they must constitute at least\n"
- "%d characters and %d%% of the entire message), and will\n"
- "place a ban after %d kicks for the same user."), kd->capsmin, kd->capspercent, kd->ttb[TTB_CAPS]);
- else
- source.Reply(_("Bot will now kick for \002caps\002 (they must constitute at least\n"
- "%d characters and %d%% of the entire message)."), kd->capsmin, kd->capspercent);
- }
- else
- {
- kd->caps = false;
- source.Reply(_("Bot won't kick for \002caps\002 anymore."));
- }
-
- kd->Check(ci);
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Sets the caps kicker on or off. When enabled, this\n"
- "option tells the bot to kick users who are talking in\n"
- "CAPS.\n"
- "The bot kicks only if there are at least \002min\002 caps\n"
- "and they constitute at least \002percent\002%% of the total\n"
- "text line (if not given, it defaults to 10 characters\n"
- "and 25%%).\n"
- " \n"
- "\037ttb\037 is the number of times a user can be kicked\n"
- "before it gets banned. Don't give ttb to disable\n"
- "the ban system once activated."));
- return true;
- }
-};
-
-class CommandBSKickColors : public CommandBSKickBase
-{
- public:
- CommandBSKickColors(Module *creator) : CommandBSKickBase(creator, "botserv/kick/colors", 2, 3)
- {
- this->SetDesc(_("Configures color kicker"));
- this->SetSyntax(_("\037channel\037 {\037ON|OFF\037} [\037ttb\037]"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- ChannelInfo *ci;
- if (CheckArguments(source, params, ci))
- {
- KickerData *kd = ci->Require<KickerData>("kickerdata");
- Process(source, ci, params[1], params.size() > 2 ? params[2] : "", TTB_COLORS, "colors", kd, kd->colors);
- kd->Check(ci);
- }
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Sets the colors kicker on or off. When enabled, this\n"
- "option tells the bot to kick users who use colors.\n"
- " \n"
- "\037ttb\037 is the number of times a user can be kicked\n"
- "before it gets banned. Don't give ttb to disable\n"
- "the ban system once activated."));
- return true;
- }
-};
-
-class CommandBSKickFlood : public CommandBSKickBase
-{
- public:
- CommandBSKickFlood(Module *creator) : CommandBSKickBase(creator, "botserv/kick/flood", 2, 5)
- {
- this->SetDesc(_("Configures flood kicker"));
- this->SetSyntax(_("\037channel\037 {\037ON|OFF\037} [\037ttb\037 [\037ln\037 [\037secs\037]]]\002"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- ChannelInfo *ci;
- if (!CheckArguments(source, params, ci))
- return;
-
- KickerData *kd = ci->Require<KickerData>("kickerdata");
-
- if (params[1].equals_ci("ON"))
- {
- const Anope::string &ttb = params.size() > 2 ? params[2] : "",
- &lines = params.size() > 3 ? params[3] : "",
- &secs = params.size() > 4 ? params[4] : "";
-
- if (!ttb.empty())
- {
- int16_t i;
-
- try
- {
- i = convertTo<int16_t>(ttb);
- if (i < 0)
- throw ConvertException();
- }
- catch (const ConvertException &)
- {
- source.Reply(_("\002%s\002 cannot be taken as times to ban."), ttb.c_str());
- return;
- }
-
- kd->ttb[TTB_FLOOD] = i;
- }
- else
- kd->ttb[TTB_FLOOD] = 0;
-
- kd->floodlines = 6;
- try
- {
- kd->floodlines = convertTo<int16_t>(lines);
- }
- catch (const ConvertException &) { }
- if (kd->floodlines < 2)
- kd->floodlines = 6;
-
- kd->floodsecs = 10;
- try
- {
- kd->floodsecs = convertTo<int16_t>(secs);
- }
- catch (const ConvertException &) { }
- if (kd->floodsecs < 1)
- kd->floodsecs = 10;
- if (kd->floodsecs > Config->GetModule(me)->Get<time_t>("keepdata"))
- kd->floodsecs = Config->GetModule(me)->Get<time_t>("keepdata");
-
- kd->flood = true;
- if (kd->ttb[TTB_FLOOD])
- source.Reply(_("Bot will now kick for \002flood\002 (%d lines in %d seconds\n"
- "and will place a ban after %d kicks for the same user."), kd->floodlines, kd->floodsecs, kd->ttb[TTB_FLOOD]);
- else
- source.Reply(_("Bot will now kick for \002flood\002 (%d lines in %d seconds)."), kd->floodlines, kd->floodsecs);
- }
- else if (params[1].equals_ci("OFF"))
- {
- kd->flood = false;
- source.Reply(_("Bot won't kick for \002flood\002 anymore."));
- }
- else
- this->OnSyntaxError(source, params[1]);
-
- kd->Check(ci);
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Sets the flood kicker on or off. When enabled, this\n"
- "option tells the bot to kick users who are flooding\n"
- "the channel using at least \002ln\002 lines in \002secs\002 seconds\n"
- "(if not given, it defaults to 6 lines in 10 seconds).\n"
- " \n"
- "\037ttb\037 is the number of times a user can be kicked\n"
- "before it gets banned. Don't give ttb to disable\n"
- "the ban system once activated."));
- return true;
- }
-};
-
-class CommandBSKickItalics : public CommandBSKickBase
-{
- public:
- CommandBSKickItalics(Module *creator) : CommandBSKickBase(creator, "botserv/kick/italics", 2, 3)
- {
- this->SetDesc(_("Configures italics kicker"));
- this->SetSyntax(_("\037channel\037 {\037ON|OFF\037} [\037ttb\037]"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- ChannelInfo *ci;
- if (CheckArguments(source, params, ci))
- {
- KickerData *kd = ci->Require<KickerData>("kickerdata");
- Process(source, ci, params[1], params.size() > 2 ? params[2] : "", TTB_ITALICS, "italics", kd, kd->italics);
- kd->Check(ci);
- }
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Sets the italics kicker on or off. When enabled, this\n"
- "option tells the bot to kick users who use italics.\n"
- " \n"
- "\037ttb\037 is the number of times a user can be kicked\n"
- "before it gets banned. Don't give ttb to disable\n"
- "the ban system once activated."));
- return true;
- }
-};
-
-class CommandBSKickRepeat : public CommandBSKickBase
-{
- public:
- CommandBSKickRepeat(Module *creator) : CommandBSKickBase(creator, "botserv/kick/repeat", 2, 4)
- {
- this->SetDesc(_("Configures repeat kicker"));
- this->SetSyntax(_("\037channel\037 {\037ON|OFF\037} [\037ttb\037 [\037num\037]]\002"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- ChannelInfo *ci;
- if (!CheckArguments(source, params, ci))
- return;
-
- KickerData *kd = ci->Require<KickerData>("kickerdata");
-
- if (params[1].equals_ci("ON"))
- {
- const Anope::string &ttb = params.size() > 2 ? params[2] : "",
- &times = params.size() > 3 ? params[3] : "";
-
- if (!ttb.empty())
- {
- int16_t i;
-
- try
- {
- i = convertTo<int16_t>(ttb);
- if (i < 0)
- throw ConvertException();
- }
- catch (const ConvertException &)
- {
- source.Reply(_("\002%s\002 cannot be taken as times to ban."), ttb.c_str());
- return;
- }
-
- kd->ttb[TTB_REPEAT] = i;
- }
- else
- kd->ttb[TTB_REPEAT] = 0;
-
- kd->repeattimes = 3;
- try
- {
- kd->repeattimes = convertTo<int16_t>(times);
- }
- catch (const ConvertException &) { }
- if (kd->repeattimes < 1)
- kd->repeattimes = 3;
-
- kd->repeat = true;
- if (kd->ttb[TTB_REPEAT])
- {
- if (kd->repeattimes != 1)
- source.Reply(_("Bot will now kick for \002repeats\002 (users that repeat the\n"
- "same message %d times), and will place a ban after %d\n"
- "kicks for the same user."), kd->repeattimes, kd->ttb[TTB_REPEAT]);
- else
- source.Reply(_("Bot will now kick for \002repeats\002 (users that repeat the\n"
- "same message %d time), and will place a ban after %d\n"
- "kicks for the same user."), kd->repeattimes, kd->ttb[TTB_REPEAT]);
- }
- else
- {
- if (kd->repeattimes != 1)
- source.Reply(_("Bot will now kick for \002repeats\002 (users that repeat the\n"
- "same message %d times)."), kd->repeattimes);
- else
- source.Reply(_("Bot will now kick for \002repeats\002 (users that repeat the\n"
- "same message %d time)."), kd->repeattimes);
- }
- }
- else if (params[1].equals_ci("OFF"))
- {
- kd->repeat = false;
- source.Reply(_("Bot won't kick for \002repeats\002 anymore."));
- }
- else
- this->OnSyntaxError(source, params[1]);
-
- kd->Check(ci);
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Sets the repeat kicker on or off. When enabled, this\n"
- "option tells the bot to kick users who are repeating\n"
- "themselves \002num\002 times (if num is not given, it\n"
- "defaults to 3).\n"
- " \n"
- "\037ttb\037 is the number of times a user can be kicked\n"
- "before it gets banned. Don't give ttb to disable\n"
- "the ban system once activated."));
- return true;
- }
-};
-
-class CommandBSKickReverses : public CommandBSKickBase
-{
- public:
- CommandBSKickReverses(Module *creator) : CommandBSKickBase(creator, "botserv/kick/reverses", 2, 3)
- {
- this->SetDesc(_("Configures reverses kicker"));
- this->SetSyntax(_("\037channel\037 {\037ON|OFF\037} [\037ttb\037]"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- ChannelInfo *ci;
- if (CheckArguments(source, params, ci))
- {
- KickerData *kd = ci->Require<KickerData>("kickerdata");
- Process(source, ci, params[1], params.size() > 2 ? params[2] : "", TTB_REVERSES, "reverses", kd, kd->reverses);
- kd->Check(ci);
- }
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Sets the reverses kicker on or off. When enabled, this\n"
- "option tells the bot to kick users who use reverses.\n"
- " \n"
- "\037ttb\037 is the number of times a user can be kicked\n"
- "before it gets banned. Don't give ttb to disable\n"
- "the ban system once activated."));
- return true;
- }
-};
-
-class CommandBSKickUnderlines : public CommandBSKickBase
-{
- public:
- CommandBSKickUnderlines(Module *creator) : CommandBSKickBase(creator, "botserv/kick/underlines", 2, 3)
- {
- this->SetDesc(_("Configures underlines kicker"));
- this->SetSyntax(_("\037channel\037 {\037ON|OFF\037} [\037ttb\037]"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- ChannelInfo *ci;
- if (CheckArguments(source, params, ci))
- {
- KickerData *kd = ci->Require<KickerData>("kickerdata");
- Process(source, ci, params[1], params.size() > 2 ? params[2] : "", TTB_UNDERLINES, "underlines", kd, kd->underlines);
- kd->Check(ci);
- }
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Sets the underlines kicker on or off. When enabled, this\n"
- "option tells the bot to kick users who use underlines.\n"
- " \n"
- "\037ttb\037 is the number of times a user can be kicked\n"
- "before it gets banned. Don't give ttb to disable\n"
- "the ban system once activated."));
- return true;
- }
-};
-
-class CommandBSSetDontKickOps : public Command
-{
- public:
- CommandBSSetDontKickOps(Module *creator, const Anope::string &sname = "botserv/set/dontkickops") : Command(creator, sname, 2, 2)
- {
- this->SetDesc(_("To protect ops against bot kicks"));
- this->SetSyntax(_("\037channel\037 {ON | OFF}"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- ChannelInfo *ci = ChannelInfo::Find(params[0]);
- if (ci == NULL)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
- return;
- }
-
- AccessGroup access = source.AccessFor(ci);
- if (!source.HasPriv("botserv/administration") && !access.HasPriv("SET"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- if (Anope::ReadOnly)
- {
- source.Reply(_("Sorry, bot option setting is temporarily disabled."));
- return;
- }
-
- KickerData *kd = ci->Require<KickerData>("kickerdata");
- if (params[1].equals_ci("ON"))
- {
- bool override = !access.HasPriv("SET");
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to enable dontkickops";
-
- kd->dontkickops = true;
- source.Reply(_("Bot \002won't kick ops\002 on channel %s."), ci->name.c_str());
- }
- else if (params[1].equals_ci("OFF"))
- {
- bool override = !access.HasPriv("SET");
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to disable dontkickops";
-
- kd->dontkickops = false;
- source.Reply(_("Bot \002will kick ops\002 on channel %s."), ci->name.c_str());
- }
- else
- this->OnSyntaxError(source, source.command);
-
- kd->Check(ci);
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
- {
- this->SendSyntax(source);
- source.Reply(_(" \n"
- "Enables or disables \002ops protection\002 mode on a channel.\n"
- "When it is enabled, ops won't be kicked by the bot\n"
- "even if they don't match the NOKICK level."));
- return true;
- }
-};
-
-class CommandBSSetDontKickVoices : public Command
-{
- public:
- CommandBSSetDontKickVoices(Module *creator, const Anope::string &sname = "botserv/set/dontkickvoices") : Command(creator, sname, 2, 2)
- {
- this->SetDesc(_("To protect voices against bot kicks"));
- this->SetSyntax(_("\037channel\037 {ON | OFF}"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- ChannelInfo *ci = ChannelInfo::Find(params[0]);
- if (ci == NULL)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
- return;
- }
-
- AccessGroup access = source.AccessFor(ci);
- if (!source.HasPriv("botserv/administration") && !access.HasPriv("SET"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- if (Anope::ReadOnly)
- {
- source.Reply(_("Sorry, bot option setting is temporarily disabled."));
- return;
- }
-
- KickerData *kd = ci->Require<KickerData>("kickerdata");
- if (params[1].equals_ci("ON"))
- {
- bool override = !access.HasPriv("SET");
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to enable dontkickvoices";
-
- kd->dontkickvoices = true;
- source.Reply(_("Bot \002won't kick voices\002 on channel %s."), ci->name.c_str());
- }
- else if (params[1].equals_ci("OFF"))
- {
- bool override = !access.HasPriv("SET");
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to disable dontkickvoices";
-
- kd->dontkickvoices = false;
- source.Reply(_("Bot \002will kick voices\002 on channel %s."), ci->name.c_str());
- }
- else
- this->OnSyntaxError(source, source.command);
-
- kd->Check(ci);
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
- {
- this->SendSyntax(source);
- source.Reply(_(" \n"
- "Enables or disables \002voices protection\002 mode on a channel.\n"
- "When it is enabled, voices won't be kicked by the bot\n"
- "even if they don't match the NOKICK level."));
- return true;
- }
-};
-
-struct BanData
-{
- struct Data
- {
- Anope::string mask;
- time_t last_use;
- int16_t ttb[TTB_SIZE];
-
- Data()
- {
- last_use = 0;
- for (int i = 0; i < TTB_SIZE; ++i)
- this->ttb[i] = 0;
- }
- };
-
- private:
- typedef Anope::map<Data> data_type;
- data_type data_map;
-
- public:
- BanData(Extensible *) { }
-
- Data &get(const Anope::string &key)
- {
- return this->data_map[key];
- }
-
- bool empty() const
- {
- return this->data_map.empty();
- }
-
- void purge()
- {
- time_t keepdata = Config->GetModule(me)->Get<time_t>("keepdata");
- for (data_type::iterator it = data_map.begin(), it_end = data_map.end(); it != it_end;)
- {
- const Anope::string &user = it->first;
- Data &bd = it->second;
- ++it;
-
- if (Anope::CurTime - bd.last_use > keepdata)
- data_map.erase(user);
- }
- }
-};
-
-struct UserData
-{
- UserData(Extensible *)
- {
- last_use = last_start = Anope::CurTime;
- lines = times = 0;
- lastline.clear();
- }
-
- /* Data validity */
- time_t last_use;
-
- /* for flood kicker */
- int16_t lines;
- time_t last_start;
-
- /* for repeat kicker */
- Anope::string lasttarget;
- int16_t times;
-
- Anope::string lastline;
-};
-
-class BanDataPurger : public Timer
-{
- public:
- BanDataPurger(Module *o) : Timer(o, 300, Anope::CurTime, true) { }
-
- void Tick(time_t) anope_override
- {
- Log(LOG_DEBUG) << "bs_main: Running bandata purger";
-
- for (channel_map::iterator it = ChannelList.begin(), it_end = ChannelList.end(); it != it_end; ++it)
- {
- Channel *c = it->second;
-
- BanData *bd = c->GetExt<BanData>("bandata");
- if (bd != NULL)
- {
- bd->purge();
- if (bd->empty())
- c->Shrink<BanData>("bandata");
- }
- }
- }
-};
-
-class BSKick : public Module
-{
- ExtensibleItem<BanData> bandata;
- ExtensibleItem<UserData> userdata;
- KickerDataImpl::ExtensibleItem kickerdata;
-
- CommandBSKick commandbskick;
- CommandBSKickAMSG commandbskickamsg;
- CommandBSKickBadwords commandbskickbadwords;
- CommandBSKickBolds commandbskickbolds;
- CommandBSKickCaps commandbskickcaps;
- CommandBSKickColors commandbskickcolors;
- CommandBSKickFlood commandbskickflood;
- CommandBSKickItalics commandbskickitalics;
- CommandBSKickRepeat commandbskickrepeat;
- CommandBSKickReverses commandbskickreverse;
- CommandBSKickUnderlines commandbskickunderlines;
-
- CommandBSSetDontKickOps commandbssetdontkickops;
- CommandBSSetDontKickVoices commandbssetdontkickvoices;
-
- BanDataPurger purger;
-
- BanData::Data &GetBanData(User *u, Channel *c)
- {
- BanData *bd = bandata.Require(c);
- return bd->get(u->GetMask());
- }
-
- UserData *GetUserData(User *u, Channel *c)
- {
- ChanUserContainer *uc = c->FindUser(u);
- if (uc == NULL)
- return NULL;
-
- UserData *ud = userdata.Require(uc);
- return ud;
- }
-
- void check_ban(ChannelInfo *ci, User *u, KickerData *kd, int ttbtype)
- {
- /* Don't ban ulines or protected users */
- if (u->IsProtected())
- return;
-
- BanData::Data &bd = this->GetBanData(u, ci->c);
-
- ++bd.ttb[ttbtype];
- if (kd->ttb[ttbtype] && bd.ttb[ttbtype] >= kd->ttb[ttbtype])
- {
- /* Should not use == here because bd.ttb[ttbtype] could possibly be > kd->ttb[ttbtype]
- * if the TTB was changed after it was not set (0) before and the user had already been
- * kicked a few times. Bug #1056 - Adam */
-
- bd.ttb[ttbtype] = 0;
-
- Anope::string mask = ci->GetIdealBan(u);
-
- ci->c->SetMode(NULL, "BAN", mask);
- FOREACH_MOD(OnBotBan, (u, ci, mask));
- }
- }
-
- void bot_kick(ChannelInfo *ci, User *u, const char *message, ...)
- {
- va_list args;
- char buf[1024];
-
- if (!ci || !ci->bi || !ci->c || !u || u->IsProtected() || !ci->c->FindUser(u))
- return;
-
- Anope::string fmt = Language::Translate(u, message);
- va_start(args, message);
- vsnprintf(buf, sizeof(buf), fmt.c_str(), args);
- va_end(args);
-
- ci->c->Kick(ci->bi, u, "%s", buf);
- }
-
- public:
- BSKick(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- bandata(this, "bandata"),
- userdata(this, "userdata"),
- kickerdata(this, "kickerdata"),
-
- commandbskick(this),
- commandbskickamsg(this), commandbskickbadwords(this), commandbskickbolds(this), commandbskickcaps(this),
- commandbskickcolors(this), commandbskickflood(this), commandbskickitalics(this), commandbskickrepeat(this),
- commandbskickreverse(this), commandbskickunderlines(this),
-
- commandbssetdontkickops(this), commandbssetdontkickvoices(this),
-
- purger(this)
- {
- me = this;
-
- }
-
- void OnBotInfo(CommandSource &source, BotInfo *bi, ChannelInfo *ci, InfoFormatter &info) anope_override
- {
- if (!ci)
- return;
-
- Anope::string enabled = Language::Translate(source.nc, _("Enabled"));
- Anope::string disabled = Language::Translate(source.nc, _("Disabled"));
- KickerData *kd = kickerdata.Get(ci);
-
- if (kd && kd->badwords)
- {
- if (kd->ttb[TTB_BADWORDS])
- info[_("Bad words kicker")] = Anope::printf("%s (%d kick(s) to ban)", enabled.c_str(), kd->ttb[TTB_BADWORDS]);
- else
- info[_("Bad words kicker")] = enabled;
- }
- else
- info[_("Bad words kicker")] = disabled;
-
- if (kd && kd->bolds)
- {
- if (kd->ttb[TTB_BOLDS])
- info[_("Bolds kicker")] = Anope::printf("%s (%d kick(s) to ban)", enabled.c_str(), kd->ttb[TTB_BOLDS]);
- else
- info[_("Bolds kicker")] = enabled;
- }
- else
- info[_("Bolds kicker")] = disabled;
-
- if (kd && kd->caps)
- {
- if (kd->ttb[TTB_CAPS])
- info[_("Caps kicker")] = Anope::printf(_("%s (%d kick(s) to ban; minimum %d/%d%%)"), enabled.c_str(), kd->ttb[TTB_CAPS], kd->capsmin, kd->capspercent);
- else
- info[_("Caps kicker")] = Anope::printf(_("%s (minimum %d/%d%%)"), enabled.c_str(), kd->capsmin, kd->capspercent);
- }
- else
- info[_("Caps kicker")] = disabled;
-
- if (kd && kd->colors)
- {
- if (kd->ttb[TTB_COLORS])
- info[_("Colors kicker")] = Anope::printf(_("%s (%d kick(s) to ban)"), enabled.c_str(), kd->ttb[TTB_COLORS]);
- else
- info[_("Colors kicker")] = enabled;
- }
- else
- info[_("Colors kicker")] = disabled;
-
- if (kd && kd->flood)
- {
- if (kd->ttb[TTB_FLOOD])
- info[_("Flood kicker")] = Anope::printf(_("%s (%d kick(s) to ban; %d lines in %ds)"), enabled.c_str(), kd->ttb[TTB_FLOOD], kd->floodlines, kd->floodsecs);
- else
- info[_("Flood kicker")] = Anope::printf(_("%s (%d lines in %ds)"), enabled.c_str(), kd->floodlines, kd->floodsecs);
- }
- else
- info[_("Flood kicker")] = disabled;
-
- if (kd && kd->repeat)
- {
- if (kd->ttb[TTB_REPEAT])
- info[_("Repeat kicker")] = Anope::printf(_("%s (%d kick(s) to ban; %d times)"), enabled.c_str(), kd->ttb[TTB_REPEAT], kd->repeattimes);
- else
- info[_("Repeat kicker")] = Anope::printf(_("%s (%d times)"), enabled.c_str(), kd->repeattimes);
- }
- else
- info[_("Repeat kicker")] = disabled;
-
- if (kd && kd->reverses)
- {
- if (kd->ttb[TTB_REVERSES])
- info[_("Reverses kicker")] = Anope::printf(_("%s (%d kick(s) to ban)"), enabled.c_str(), kd->ttb[TTB_REVERSES]);
- else
- info[_("Reverses kicker")] = enabled;
- }
- else
- info[_("Reverses kicker")] = disabled;
-
- if (kd && kd->underlines)
- {
- if (kd->ttb[TTB_UNDERLINES])
- info[_("Underlines kicker")] = Anope::printf(_("%s (%d kick(s) to ban)"), enabled.c_str(), kd->ttb[TTB_UNDERLINES]);
- else
- info[_("Underlines kicker")] = enabled;
- }
- else
- info[_("Underlines kicker")] = disabled;
-
- if (kd && kd->italics)
- {
- if (kd->ttb[TTB_ITALICS])
- info[_("Italics kicker")] = Anope::printf(_("%s (%d kick(s) to ban)"), enabled.c_str(), kd->ttb[TTB_ITALICS]);
- else
- info[_("Italics kicker")] = enabled;
- }
- else
- info[_("Italics kicker")] = disabled;
-
- if (kd && kd->amsgs)
- {
- if (kd->ttb[TTB_AMSGS])
- info[_("AMSG kicker")] = Anope::printf(_("%s (%d kick(s) to ban)"), enabled.c_str(), kd->ttb[TTB_AMSGS]);
- else
- info[_("AMSG kicker")] = enabled;
- }
- else
- info[_("AMSG kicker")] = disabled;
-
- if (kd && kd->dontkickops)
- info.AddOption(_("Ops protection"));
- if (kd && kd->dontkickvoices)
- info.AddOption(_("Voices protection"));
- }
-
- void OnPrivmsg(User *u, Channel *c, Anope::string &msg) anope_override
- {
- /* Now we can make kicker stuff. We try to order the checks
- * from the fastest one to the slowest one, since there's
- * no need to process other kickers if a user is kicked before
- * the last kicker check.
- *
- * But FIRST we check whether the user is protected in any
- * way.
- */
- ChannelInfo *ci = c->ci;
- if (ci == NULL)
- return;
- KickerData *kd = kickerdata.Get(ci);
- if (kd == NULL)
- return;
-
- if (ci->AccessFor(u).HasPriv("NOKICK"))
- return;
- else if (kd->dontkickops && (c->HasUserStatus(u, "HALFOP") || c->HasUserStatus(u, "OP") || c->HasUserStatus(u, "PROTECT") || c->HasUserStatus(u, "OWNER")))
- return;
- else if (kd->dontkickvoices && c->HasUserStatus(u, "VOICE"))
- return;
-
- Anope::string realbuf = msg;
-
- /* If it's a /me, cut the CTCP part because the ACTION will cause
- * problems with the caps or badwords kicker
- */
- if (realbuf.substr(0, 8).equals_ci("\1ACTION ") && realbuf[realbuf.length() - 1] == '\1')
- {
- realbuf.erase(0, 8);
- realbuf.erase(realbuf.length() - 1);
- }
-
- if (realbuf.empty())
- return;
-
- /* Bolds kicker */
- if (kd->bolds && realbuf.find(2) != Anope::string::npos)
- {
- check_ban(ci, u, kd, TTB_BOLDS);
- bot_kick(ci, u, _("Don't use bolds on this channel!"));
- return;
- }
-
- /* Color kicker */
- if (kd->colors && realbuf.find(3) != Anope::string::npos)
- {
- check_ban(ci, u, kd, TTB_COLORS);
- bot_kick(ci, u, _("Don't use colors on this channel!"));
- return;
- }
-
- /* Reverses kicker */
- if (kd->reverses && realbuf.find(22) != Anope::string::npos)
- {
- check_ban(ci, u, kd, TTB_REVERSES);
- bot_kick(ci, u, _("Don't use reverses on this channel!"));
- return;
- }
-
- /* Italics kicker */
- if (kd->italics && realbuf.find(29) != Anope::string::npos)
- {
- check_ban(ci, u, kd, TTB_ITALICS);
- bot_kick(ci, u, _("Don't use italics on this channel!"));
- return;
- }
-
- /* Underlines kicker */
- if (kd->underlines && realbuf.find(31) != Anope::string::npos)
- {
- check_ban(ci, u, kd, TTB_UNDERLINES);
- bot_kick(ci, u, _("Don't use underlines on this channel!"));
- return;
- }
-
- /* Caps kicker */
- if (kd->caps && realbuf.length() >= static_cast<unsigned>(kd->capsmin))
- {
- int i = 0, l = 0;
-
- for (unsigned j = 0, end = realbuf.length(); j < end; ++j)
- {
- if (isupper(realbuf[j]))
- ++i;
- else if (islower(realbuf[j]))
- ++l;
- }
-
- /* i counts uppercase chars, l counts lowercase chars. Only
- * alphabetic chars (so islower || isupper) qualify for the
- * percentage of caps to kick for; the rest is ignored. -GD
- */
-
- if ((i || l) && i >= kd->capsmin && i * 100 / (i + l) >= kd->capspercent)
- {
- check_ban(ci, u, kd, TTB_CAPS);
- bot_kick(ci, u, _("Turn caps lock OFF!"));
- return;
- }
- }
-
- /* Bad words kicker */
- if (kd->badwords)
- {
- bool mustkick = false;
- BadWords *badwords = ci->GetExt<BadWords>("badwords");
-
- /* Normalize the buffer */
- Anope::string nbuf = Anope::NormalizeBuffer(realbuf);
- bool casesensitive = Config->GetModule("botserv")->Get<bool>("casesensitive");
-
- /* Normalize can return an empty string if this only conains control codes etc */
- if (badwords && !nbuf.empty())
- for (unsigned i = 0; i < badwords->GetBadWordCount(); ++i)
- {
- const BadWord *bw = badwords->GetBadWord(i);
-
- if (bw->word.empty())
- continue; // Shouldn't happen
-
- if (bw->word.length() > nbuf.length())
- continue; // This can't ever match
-
- if (bw->type == BW_ANY && ((casesensitive && nbuf.find(bw->word) != Anope::string::npos) || (!casesensitive && nbuf.find_ci(bw->word) != Anope::string::npos)))
- mustkick = true;
- else if (bw->type == BW_SINGLE)
- {
- size_t len = bw->word.length();
-
- if ((casesensitive && bw->word.equals_cs(nbuf)) || (!casesensitive && bw->word.equals_ci(nbuf)))
- mustkick = true;
- else if (nbuf.find(' ') == len && ((casesensitive && bw->word.equals_cs(nbuf.substr(0, len))) || (!casesensitive && bw->word.equals_ci(nbuf.substr(0, len)))))
- mustkick = true;
- else
- {
- if (len < nbuf.length() && nbuf.rfind(' ') == nbuf.length() - len - 1 && ((casesensitive && nbuf.find(bw->word) == nbuf.length() - len) || (!casesensitive && nbuf.find_ci(bw->word) == nbuf.length() - len)))
- mustkick = true;
- else
- {
- Anope::string wordbuf = " " + bw->word + " ";
-
- if ((casesensitive && nbuf.find(wordbuf) != Anope::string::npos) || (!casesensitive && nbuf.find_ci(wordbuf) != Anope::string::npos))
- mustkick = true;
- }
- }
- }
- else if (bw->type == BW_START)
- {
- size_t len = bw->word.length();
-
- if ((casesensitive && nbuf.substr(0, len).equals_cs(bw->word)) || (!casesensitive && nbuf.substr(0, len).equals_ci(bw->word)))
- mustkick = true;
- else
- {
- Anope::string wordbuf = " " + bw->word;
-
- if ((casesensitive && nbuf.find(wordbuf) != Anope::string::npos) || (!casesensitive && nbuf.find_ci(wordbuf) != Anope::string::npos))
- mustkick = true;
- }
- }
- else if (bw->type == BW_END)
- {
- size_t len = bw->word.length();
-
- if ((casesensitive && nbuf.substr(nbuf.length() - len).equals_cs(bw->word)) || (!casesensitive && nbuf.substr(nbuf.length() - len).equals_ci(bw->word)))
- mustkick = true;
- else
- {
- Anope::string wordbuf = bw->word + " ";
-
- if ((casesensitive && nbuf.find(wordbuf) != Anope::string::npos) || (!casesensitive && nbuf.find_ci(wordbuf) != Anope::string::npos))
- mustkick = true;
- }
- }
-
- if (mustkick)
- {
- check_ban(ci, u, kd, TTB_BADWORDS);
- if (Config->GetModule(me)->Get<bool>("gentlebadwordreason"))
- bot_kick(ci, u, _("Watch your language!"));
- else
- bot_kick(ci, u, _("Don't use the word \"%s\" on this channel!"), bw->word.c_str());
-
- return;
- }
- } /* for */
- } /* if badwords */
-
- UserData *ud = GetUserData(u, c);
-
- if (ud)
- {
- /* Flood kicker */
- if (kd->flood)
- {
- if (Anope::CurTime - ud->last_start > kd->floodsecs)
- {
- ud->last_start = Anope::CurTime;
- ud->lines = 0;
- }
-
- ++ud->lines;
- if (ud->lines >= kd->floodlines)
- {
- check_ban(ci, u, kd, TTB_FLOOD);
- bot_kick(ci, u, _("Stop flooding!"));
- return;
- }
- }
-
- /* Repeat kicker */
- if (kd->repeat)
- {
- if (!ud->lastline.equals_ci(realbuf))
- ud->times = 0;
- else
- ++ud->times;
-
- if (ud->times >= kd->repeattimes)
- {
- check_ban(ci, u, kd, TTB_REPEAT);
- bot_kick(ci, u, _("Stop repeating yourself!"));
- return;
- }
- }
-
- if (ud->lastline.equals_ci(realbuf) && !ud->lasttarget.empty() && !ud->lasttarget.equals_ci(ci->name))
- {
- for (User::ChanUserList::iterator it = u->chans.begin(); it != u->chans.end();)
- {
- Channel *chan = it->second->chan;
- ++it;
-
- if (chan->ci && kd->amsgs && !chan->ci->AccessFor(u).HasPriv("NOKICK"))
- {
- check_ban(chan->ci, u, kd, TTB_AMSGS);
- bot_kick(chan->ci, u, _("Don't use AMSGs!"));
- }
- }
- }
-
- ud->lasttarget = ci->name;
- ud->lastline = realbuf;
- }
- }
-};
-
-MODULE_INIT(BSKick)
diff --git a/modules/commands/cs_access.cpp b/modules/commands/cs_access.cpp
deleted file mode 100644
index 6e9b79c6a..000000000
--- a/modules/commands/cs_access.cpp
+++ /dev/null
@@ -1,904 +0,0 @@
-/* ChanServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-static std::map<Anope::string, int16_t, ci::less> defaultLevels;
-
-static inline void reset_levels(ChannelInfo *ci)
-{
- ci->ClearLevels();
- for (std::map<Anope::string, int16_t, ci::less>::iterator it = defaultLevels.begin(), it_end = defaultLevels.end(); it != it_end; ++it)
- ci->SetLevel(it->first, it->second);
-}
-
-class AccessChanAccess : public ChanAccess
-{
- public:
- int level;
-
- AccessChanAccess(AccessProvider *p) : ChanAccess(p), level(0)
- {
- }
-
- bool HasPriv(const Anope::string &name) const anope_override
- {
- return this->ci->GetLevel(name) != ACCESS_INVALID && this->level >= this->ci->GetLevel(name);
- }
-
- Anope::string AccessSerialize() const
- {
- return stringify(this->level);
- }
-
- void AccessUnserialize(const Anope::string &data) anope_override
- {
- try
- {
- this->level = convertTo<int>(data);
- }
- catch (const ConvertException &)
- {
- }
- }
-
- bool operator>(const ChanAccess &other) const anope_override
- {
- if (this->provider != other.provider)
- return ChanAccess::operator>(other);
- else
- return this->level > anope_dynamic_static_cast<const AccessChanAccess *>(&other)->level;
- }
-
- bool operator<(const ChanAccess &other) const anope_override
- {
- if (this->provider != other.provider)
- return ChanAccess::operator<(other);
- else
- return this->level < anope_dynamic_static_cast<const AccessChanAccess *>(&other)->level;
- }
-};
-
-class AccessAccessProvider : public AccessProvider
-{
- public:
- static AccessAccessProvider *me;
-
- AccessAccessProvider(Module *o) : AccessProvider(o, "access/access")
- {
- me = this;
- }
-
- ChanAccess *Create() anope_override
- {
- return new AccessChanAccess(this);
- }
-};
-AccessAccessProvider* AccessAccessProvider::me;
-
-class CommandCSAccess : public Command
-{
- void DoAdd(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
- {
- Anope::string mask = params[2];
- Privilege *p = NULL;
- int level = ACCESS_INVALID;
-
- try
- {
- level = convertTo<int>(params[3]);
- }
- catch (const ConvertException &)
- {
- p = PrivilegeManager::FindPrivilege(params[3]);
- if (p != NULL && defaultLevels[p->name])
- level = defaultLevels[p->name];
- }
-
- if (!level)
- {
- source.Reply(_("Access level must be non-zero."));
- return;
- }
- else if (level <= ACCESS_INVALID || level >= ACCESS_FOUNDER)
- {
- source.Reply(CHAN_ACCESS_LEVEL_RANGE, ACCESS_INVALID + 1, ACCESS_FOUNDER - 1);
- return;
- }
-
- AccessGroup u_access = source.AccessFor(ci);
- const ChanAccess *highest = u_access.Highest();
-
- AccessChanAccess tmp_access(AccessAccessProvider::me);
- tmp_access.ci = ci;
- tmp_access.level = level;
-
- bool override = false;
- const NickAlias *na = NULL;
-
- if ((!highest || *highest <= tmp_access) && !u_access.founder)
- {
- if (source.HasPriv("chanserv/access/modify"))
- override = true;
- else
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
- }
-
- if (IRCD->IsChannelValid(mask))
- {
- if (Config->GetModule("chanserv")->Get<bool>("disallow_channel_access"))
- {
- source.Reply(_("Channels may not be on access lists."));
- return;
- }
-
- ChannelInfo *targ_ci = ChannelInfo::Find(mask);
- if (targ_ci == NULL)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, mask.c_str());
- return;
- }
- else if (ci == targ_ci)
- {
- source.Reply(_("You can't add a channel to its own access list."));
- return;
- }
-
- mask = targ_ci->name;
- }
- else
- {
- na = NickAlias::Find(mask);
-
- if (!na && Config->GetModule("chanserv")->Get<bool>("disallow_hostmask_access"))
- {
- source.Reply(_("Masks and unregistered users may not be on access lists."));
- return;
- }
- else if (mask.find_first_of("!*@") == Anope::string::npos && !na)
- {
- User *targ = User::Find(mask, true);
- if (targ != NULL)
- mask = "*!*@" + targ->GetDisplayedHost();
- else
- {
- source.Reply(NICK_X_NOT_REGISTERED, mask.c_str());
- return;
- }
- }
-
- if (na)
- mask = na->nick;
- }
-
- for (unsigned i = ci->GetAccessCount(); i > 0; --i)
- {
- const ChanAccess *access = ci->GetAccess(i - 1);
- if ((na && na->nc == access->GetAccount()) || mask.equals_ci(access->Mask()))
- {
- /* Don't allow lowering from a level >= u_level */
- if ((!highest || *access >= *highest) && !u_access.founder && !source.HasPriv("chanserv/access/modify"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
- delete ci->EraseAccess(i - 1);
- break;
- }
- }
-
- unsigned access_max = Config->GetModule("chanserv")->Get<unsigned>("accessmax", "1024");
- if (access_max && ci->GetDeepAccessCount() >= access_max)
- {
- source.Reply(_("Sorry, you can only have %d access entries on a channel, including access entries from other channels."), access_max);
- return;
- }
-
- ServiceReference<AccessProvider> provider("AccessProvider", "access/access");
- if (!provider)
- return;
- AccessChanAccess *access = anope_dynamic_static_cast<AccessChanAccess *>(provider->Create());
- access->SetMask(mask, ci);
- access->creator = source.GetNick();
- access->level = level;
- access->last_seen = 0;
- access->created = Anope::CurTime;
- ci->AddAccess(access);
-
- FOREACH_MOD(OnAccessAdd, (ci, source, access));
-
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to add " << mask << " with level " << level;
- if (p != NULL)
- source.Reply(_("\002%s\002 added to %s access list at privilege %s (level %d)"), access->Mask().c_str(), ci->name.c_str(), p->name.c_str(), level);
- else
- source.Reply(_("\002%s\002 added to %s access list at level \002%d\002."), access->Mask().c_str(), ci->name.c_str(), level);
- }
-
- void DoDel(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
- {
- Anope::string mask = params[2];
-
- if (!isdigit(mask[0]) && mask.find_first_of("#!*@") == Anope::string::npos && !NickAlias::Find(mask))
- {
- User *targ = User::Find(mask, true);
- if (targ != NULL)
- mask = "*!*@" + targ->GetDisplayedHost();
- else
- {
- source.Reply(NICK_X_NOT_REGISTERED, mask.c_str());
- return;
- }
- }
-
- if (!ci->GetAccessCount())
- source.Reply(_("%s access list is empty."), ci->name.c_str());
- else if (isdigit(mask[0]) && mask.find_first_not_of("1234567890,-") == Anope::string::npos)
- {
- class AccessDelCallback : public NumberList
- {
- CommandSource &source;
- ChannelInfo *ci;
- Command *c;
- unsigned deleted;
- Anope::string Nicks;
- bool denied;
- bool override;
- public:
- AccessDelCallback(CommandSource &_source, ChannelInfo *_ci, Command *_c, const Anope::string &numlist) : NumberList(numlist, true), source(_source), ci(_ci), c(_c), deleted(0), denied(false), override(false)
- {
- if (!source.AccessFor(ci).HasPriv("ACCESS_CHANGE") && source.HasPriv("chanserv/access/modify"))
- this->override = true;
- }
-
- ~AccessDelCallback()
- {
- if (denied && !deleted)
- source.Reply(ACCESS_DENIED);
- else if (!deleted)
- source.Reply(_("No matching entries on %s access list."), ci->name.c_str());
- else
- {
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, c, ci) << "to delete " << Nicks;
-
- if (deleted == 1)
- source.Reply(_("Deleted 1 entry from %s access list."), ci->name.c_str());
- else
- source.Reply(_("Deleted %d entries from %s access list."), deleted, ci->name.c_str());
- }
- }
-
- void HandleNumber(unsigned Number) anope_override
- {
- if (!Number || Number > ci->GetAccessCount())
- return;
-
- ChanAccess *access = ci->GetAccess(Number - 1);
-
- AccessGroup ag = source.AccessFor(ci);
- const ChanAccess *u_highest = ag.Highest();
-
- if ((!u_highest || *u_highest <= *access) && !ag.founder && !this->override && access->GetAccount() != source.nc)
- {
- denied = true;
- return;
- }
-
- ++deleted;
- if (!Nicks.empty())
- Nicks += ", " + access->Mask();
- else
- Nicks = access->Mask();
-
- ci->EraseAccess(Number - 1);
-
- FOREACH_MOD(OnAccessDel, (ci, source, access));
- delete access;
- }
- }
- delcallback(source, ci, this, mask);
- delcallback.Process();
- }
- else
- {
- AccessGroup u_access = source.AccessFor(ci);
- const ChanAccess *highest = u_access.Highest();
-
- for (unsigned i = ci->GetAccessCount(); i > 0; --i)
- {
- ChanAccess *access = ci->GetAccess(i - 1);
- if (mask.equals_ci(access->Mask()))
- {
- if (access->GetAccount() != source.nc && !u_access.founder && (!highest || *highest <= *access) && !source.HasPriv("chanserv/access/modify"))
- source.Reply(ACCESS_DENIED);
- else
- {
- source.Reply(_("\002%s\002 deleted from %s access list."), access->Mask().c_str(), ci->name.c_str());
- bool override = !u_access.founder && !u_access.HasPriv("ACCESS_CHANGE") && access->GetAccount() != source.nc;
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to delete " << access->Mask();
-
- ci->EraseAccess(i - 1);
- FOREACH_MOD(OnAccessDel, (ci, source, access));
- delete access;
- }
- return;
- }
- }
-
- source.Reply(_("\002%s\002 not found on %s access list."), mask.c_str(), ci->name.c_str());
- }
-
- return;
- }
-
- void ProcessList(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params, ListFormatter &list)
- {
- const Anope::string &nick = params.size() > 2 ? params[2] : "";
-
- if (!ci->GetAccessCount())
- source.Reply(_("%s access list is empty."), ci->name.c_str());
- else if (!nick.empty() && nick.find_first_not_of("1234567890,-") == Anope::string::npos)
- {
- class AccessListCallback : public NumberList
- {
- ListFormatter &list;
- ChannelInfo *ci;
-
- public:
- AccessListCallback(ListFormatter &_list, ChannelInfo *_ci, const Anope::string &numlist) : NumberList(numlist, false), list(_list), ci(_ci)
- {
- }
-
- void HandleNumber(unsigned number) anope_override
- {
- if (!number || number > ci->GetAccessCount())
- return;
-
- const ChanAccess *access = ci->GetAccess(number - 1);
-
- Anope::string timebuf;
- if (ci->c)
- for (Channel::ChanUserList::const_iterator cit = ci->c->users.begin(), cit_end = ci->c->users.end(); cit != cit_end; ++cit)
- {
- ChannelInfo *p;
- if (access->Matches(cit->second->user, cit->second->user->Account(), p))
- timebuf = "Now";
- }
- if (timebuf.empty())
- {
- if (access->last_seen == 0)
- timebuf = "Never";
- else
- timebuf = Anope::strftime(access->last_seen, NULL, true);
- }
-
- ListFormatter::ListEntry entry;
- entry["Number"] = stringify(number);
- entry["Level"] = access->AccessSerialize();
- entry["Mask"] = access->Mask();
- entry["By"] = access->creator;
- entry["Last seen"] = timebuf;
- this->list.AddEntry(entry);
- }
- }
- nl_list(list, ci, nick);
- nl_list.Process();
- }
- else
- {
- for (unsigned i = 0, end = ci->GetAccessCount(); i < end; ++i)
- {
- const ChanAccess *access = ci->GetAccess(i);
-
- if (!nick.empty() && !Anope::Match(access->Mask(), nick))
- continue;
-
- Anope::string timebuf;
- if (ci->c)
- for (Channel::ChanUserList::const_iterator cit = ci->c->users.begin(), cit_end = ci->c->users.end(); cit != cit_end; ++cit)
- {
- ChannelInfo *p;
- if (access->Matches(cit->second->user, cit->second->user->Account(), p))
- timebuf = "Now";
- }
- if (timebuf.empty())
- {
- if (access->last_seen == 0)
- timebuf = "Never";
- else
- timebuf = Anope::strftime(access->last_seen, NULL, true);
- }
-
- ListFormatter::ListEntry entry;
- entry["Number"] = stringify(i + 1);
- entry["Level"] = access->AccessSerialize();
- entry["Mask"] = access->Mask();
- entry["By"] = access->creator;
- entry["Last seen"] = timebuf;
- list.AddEntry(entry);
- }
- }
-
- if (list.IsEmpty())
- source.Reply(_("No matching entries on %s access list."), ci->name.c_str());
- else
- {
- std::vector<Anope::string> replies;
- list.Process(replies);
-
- source.Reply(_("Access list for %s:"), ci->name.c_str());
-
- for (unsigned i = 0; i < replies.size(); ++i)
- source.Reply(replies[i]);
-
- source.Reply(_("End of access list"));
- }
-
- return;
- }
-
- void DoList(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
- {
- if (!ci->GetAccessCount())
- {
- source.Reply(_("%s access list is empty."), ci->name.c_str());
- return;
- }
-
- ListFormatter list(source.GetAccount());
- list.AddColumn(_("Number")).AddColumn(_("Level")).AddColumn(_("Mask"));
- this->ProcessList(source, ci, params, list);
- }
-
- void DoView(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
- {
- if (!ci->GetAccessCount())
- {
- source.Reply(_("%s access list is empty."), ci->name.c_str());
- return;
- }
-
- ListFormatter list(source.GetAccount());
- list.AddColumn(_("Number")).AddColumn(_("Level")).AddColumn(_("Mask")).AddColumn(_("By")).AddColumn(_("Last seen"));
- this->ProcessList(source, ci, params, list);
- }
-
- void DoClear(CommandSource &source, ChannelInfo *ci)
- {
- if (!source.IsFounder(ci) && !source.HasPriv("chanserv/access/modify"))
- source.Reply(ACCESS_DENIED);
- else
- {
- FOREACH_MOD(OnAccessClear, (ci, source));
-
- ci->ClearAccess();
-
- source.Reply(_("Channel %s access list has been cleared."), ci->name.c_str());
-
- bool override = !source.IsFounder(ci);
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to clear the access list";
- }
-
- return;
- }
-
- public:
- CommandCSAccess(Module *creator) : Command(creator, "chanserv/access", 2, 4)
- {
- this->SetDesc(_("Modify the list of privileged users"));
- this->SetSyntax(_("\037channel\037 ADD \037mask\037 \037level\037"));
- this->SetSyntax(_("\037channel\037 DEL {\037mask\037 | \037entry-num\037 | \037list\037}"));
- this->SetSyntax(_("\037channel\037 LIST [\037mask\037 | \037list\037]"));
- this->SetSyntax(_("\037channel\037 VIEW [\037mask\037 | \037list\037]"));
- this->SetSyntax(_("\037channel\037 CLEAR"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &cmd = params[1];
- const Anope::string &nick = params.size() > 2 ? params[2] : "";
- const Anope::string &s = params.size() > 3 ? params[3] : "";
-
- ChannelInfo *ci = ChannelInfo::Find(params[0]);
- if (ci == NULL)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
- return;
- }
-
- bool is_list = cmd.equals_ci("LIST") || cmd.equals_ci("VIEW");
- bool is_clear = cmd.equals_ci("CLEAR");
- bool is_del = cmd.equals_ci("DEL");
-
- bool has_access = false;
- if (source.HasPriv("chanserv/access/modify"))
- has_access = true;
- else if (is_list && source.HasPriv("chanserv/access/list"))
- has_access = true;
- else if (is_list && source.AccessFor(ci).HasPriv("ACCESS_LIST"))
- has_access = true;
- else if (source.AccessFor(ci).HasPriv("ACCESS_CHANGE"))
- has_access = true;
- else if (is_del)
- {
- const NickAlias *na = NickAlias::Find(nick);
- if (na && na->nc == source.GetAccount())
- has_access = true;
- }
-
- /* If LIST, we don't *require* any parameters, but we can take any.
- * If DEL, we require a nick and no level.
- * Else (ADD), we require a level (which implies a nick). */
- if (is_list || is_clear ? 0 : (cmd.equals_ci("DEL") ? (nick.empty() || !s.empty()) : s.empty()))
- this->OnSyntaxError(source, cmd);
- else if (!has_access)
- source.Reply(ACCESS_DENIED);
- else if (Anope::ReadOnly && !is_list)
- source.Reply(_("Sorry, channel access list modification is temporarily disabled."));
- else if (cmd.equals_ci("ADD"))
- this->DoAdd(source, ci, params);
- else if (cmd.equals_ci("DEL"))
- this->DoDel(source, ci, params);
- else if (cmd.equals_ci("LIST"))
- this->DoList(source, ci, params);
- else if (cmd.equals_ci("VIEW"))
- this->DoView(source, ci, params);
- else if (cmd.equals_ci("CLEAR"))
- this->DoClear(source, ci);
- else
- this->OnSyntaxError(source, "");
-
- return;
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Maintains the \002access list\002 for a channel. The access\n"
- "list specifies which users are allowed chanop status or\n"
- "access to %s commands on the channel. Different\n"
- "user levels allow for access to different subsets of\n"
- "privileges. Any registered user not on the access list has\n"
- "a user level of 0, and any unregistered user has a user level\n"
- "of -1."), source.service->nick.c_str());
- source.Reply(" ");
- source.Reply(_("The \002ACCESS ADD\002 command adds the given mask to the\n"
- "access list with the given user level; if the mask is\n"
- "already present on the list, its access level is changed to\n"
- "the level specified in the command. The \037level\037 specified\n"
- "may be a numerical level or the name of a privilege (eg AUTOOP).\n"
- "When a user joins the channel the access they receive is from the\n"
- "highest level entry in the access list."));
- if (!Config->GetModule("chanserv")->Get<bool>("disallow_channel_access"))
- source.Reply(_("The given mask may also be a channel, which will use the\n"
- "access list from the other channel up to the given \037level\037."));
- source.Reply(" ");
- source.Reply(_("The \002ACCESS DEL\002 command removes the given nick from the\n"
- "access list. If a list of entry numbers is given, those\n"
- "entries are deleted. (See the example for LIST below.)\n"
- "You may remove yourself from an access list, even if you\n"
- "do not have access to modify that list otherwise."));
- source.Reply(" ");
- source.Reply(_("The \002ACCESS LIST\002 command displays the access list. If\n"
- "a wildcard mask is given, only those entries matching the\n"
- "mask are displayed. If a list of entry numbers is given,\n"
- "only those entries are shown; for example:\n"
- " \002ACCESS #channel LIST 2-5,7-9\002\n"
- " Lists access entries numbered 2 through 5 and\n"
- " 7 through 9.\n"
- " \n"
- "The \002ACCESS VIEW\002 command displays the access list similar\n"
- "to \002ACCESS LIST\002 but shows the creator and last used time.\n"
- " \n"
- "The \002ACCESS CLEAR\002 command clears all entries of the\n"
- "access list."));
- source.Reply(" ");
-
- BotInfo *bi;
- Anope::string cmd;
- if (Command::FindCommandFromService("chanserv/levels", bi, cmd))
- source.Reply(_("\002User access levels\002 can be seen by using the\n"
- "\002%s\002 command; type \002%s%s HELP LEVELS\002 for\n"
- "information."), cmd.c_str(), Config->StrictPrivmsg.c_str(), bi->nick.c_str());
- return true;
- }
-};
-
-class CommandCSLevels : public Command
-{
- void DoSet(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
- {
- const Anope::string &what = params[2];
- const Anope::string &lev = params[3];
-
- int level;
-
- if (lev.equals_ci("FOUNDER"))
- level = ACCESS_FOUNDER;
- else
- {
- try
- {
- level = convertTo<int>(lev);
- }
- catch (const ConvertException &)
- {
- this->OnSyntaxError(source, "SET");
- return;
- }
- }
-
- if (level <= ACCESS_INVALID || level > ACCESS_FOUNDER)
- source.Reply(_("Level must be between %d and %d inclusive."), ACCESS_INVALID + 1, ACCESS_FOUNDER - 1);
- else
- {
- Privilege *p = PrivilegeManager::FindPrivilege(what);
- if (p == NULL)
- source.Reply(_("Setting \002%s\002 not known. Type \002%s%s HELP LEVELS\002 for a list of valid settings."), what.c_str(), Config->StrictPrivmsg.c_str(), source.service->nick.c_str());
- else
- {
- bool override = !source.AccessFor(ci).HasPriv("FOUNDER");
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to set " << p->name << " to level " << level;
-
- ci->SetLevel(p->name, level);
- FOREACH_MOD(OnLevelChange, (source, ci, p->name, level));
-
- if (level == ACCESS_FOUNDER)
- source.Reply(_("Level for %s on channel %s changed to founder only."), p->name.c_str(), ci->name.c_str());
- else
- source.Reply(_("Level for \002%s\002 on channel %s changed to \002%d\002."), p->name.c_str(), ci->name.c_str(), level);
- }
- }
- }
-
- void DoDisable(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
- {
- const Anope::string &what = params[2];
-
- /* Don't allow disabling of the founder level. It would be hard to change it back if you don't have access to use this command */
- if (what.equals_ci("FOUNDER"))
- {
- source.Reply(_("You can not disable the founder privilege because it would be impossible to reenable it at a later time."));
- return;
- }
-
- Privilege *p = PrivilegeManager::FindPrivilege(what);
- if (p != NULL)
- {
- bool override = !source.AccessFor(ci).HasPriv("FOUNDER");
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to disable " << p->name;
-
- ci->SetLevel(p->name, ACCESS_INVALID);
- FOREACH_MOD(OnLevelChange, (source, ci, p->name, ACCESS_INVALID));
-
- source.Reply(_("\002%s\002 disabled on channel %s."), p->name.c_str(), ci->name.c_str());
- return;
- }
-
- source.Reply(_("Setting \002%s\002 not known. Type \002%s%s HELP LEVELS\002 for a list of valid settings."), what.c_str(), Config->StrictPrivmsg.c_str(), source.service->nick.c_str());
- }
-
- void DoList(CommandSource &source, ChannelInfo *ci)
- {
- source.Reply(_("Access level settings for channel %s:"), ci->name.c_str());
-
- ListFormatter list(source.GetAccount());
- list.AddColumn(_("Name")).AddColumn(_("Level"));
-
- const std::vector<Privilege> &privs = PrivilegeManager::GetPrivileges();
-
- for (unsigned i = 0; i < privs.size(); ++i)
- {
- const Privilege &p = privs[i];
- int16_t j = ci->GetLevel(p.name);
-
- ListFormatter::ListEntry entry;
- entry["Name"] = p.name;
-
- if (j == ACCESS_INVALID)
- entry["Level"] = Language::Translate(source.GetAccount(), _("(disabled)"));
- else if (j == ACCESS_FOUNDER)
- entry["Level"] = Language::Translate(source.GetAccount(), _("(founder only)"));
- else
- entry["Level"] = stringify(j);
-
- list.AddEntry(entry);
- }
-
- std::vector<Anope::string> replies;
- list.Process(replies);
-
- for (unsigned i = 0; i < replies.size(); ++i)
- source.Reply(replies[i]);
- }
-
- void DoReset(CommandSource &source, ChannelInfo *ci)
- {
- bool override = !source.AccessFor(ci).HasPriv("FOUNDER");
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to reset all levels";
-
- reset_levels(ci);
- FOREACH_MOD(OnLevelChange, (source, ci, "ALL", 0));
-
- source.Reply(_("Access levels for \002%s\002 reset to defaults."), ci->name.c_str());
- return;
- }
-
- public:
- CommandCSLevels(Module *creator) : Command(creator, "chanserv/levels", 2, 4)
- {
- this->SetDesc(_("Redefine the meanings of access levels"));
- this->SetSyntax(_("\037channel\037 SET \037type\037 \037level\037"));
- this->SetSyntax(_("\037channel\037 {DIS | DISABLE} \037type\037"));
- this->SetSyntax(_("\037channel\037 LIST"));
- this->SetSyntax(_("\037channel\037 RESET"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &cmd = params[1];
- const Anope::string &what = params.size() > 2 ? params[2] : "";
- const Anope::string &s = params.size() > 3 ? params[3] : "";
-
- ChannelInfo *ci = ChannelInfo::Find(params[0]);
- if (ci == NULL)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
- return;
- }
-
- bool has_access = false;
- if (source.HasPriv("chanserv/access/modify"))
- has_access = true;
- else if (cmd.equals_ci("LIST") && source.HasPriv("chanserv/access/list"))
- has_access = true;
- else if (source.AccessFor(ci).HasPriv("FOUNDER"))
- has_access = true;
-
- /* If SET, we want two extra parameters; if DIS[ABLE] or FOUNDER, we want only
- * one; else, we want none.
- */
- if (cmd.equals_ci("SET") ? s.empty() : (cmd.substr(0, 3).equals_ci("DIS") ? (what.empty() || !s.empty()) : !what.empty()))
- this->OnSyntaxError(source, cmd);
- else if (!has_access)
- source.Reply(ACCESS_DENIED);
- else if (Anope::ReadOnly && !cmd.equals_ci("LIST"))
- source.Reply(READ_ONLY_MODE);
- else if (cmd.equals_ci("SET"))
- this->DoSet(source, ci, params);
- else if (cmd.equals_ci("DIS") || cmd.equals_ci("DISABLE"))
- this->DoDisable(source, ci, params);
- else if (cmd.equals_ci("LIST"))
- this->DoList(source, ci);
- else if (cmd.equals_ci("RESET"))
- this->DoReset(source, ci);
- else
- this->OnSyntaxError(source, "");
-
- return;
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- if (subcommand.equals_ci("DESC"))
- {
- source.Reply(_("The following feature/function names are available:"));
-
- ListFormatter list(source.GetAccount());
- list.AddColumn(_("Name")).AddColumn(_("Description"));
-
- const std::vector<Privilege> &privs = PrivilegeManager::GetPrivileges();
- for (unsigned i = 0; i < privs.size(); ++i)
- {
- const Privilege &p = privs[i];
- ListFormatter::ListEntry entry;
- entry["Name"] = p.name;
- entry["Description"] = Language::Translate(source.nc, p.desc.c_str());
- list.AddEntry(entry);
- }
-
- std::vector<Anope::string> replies;
- list.Process(replies);
-
- for (unsigned i = 0; i < replies.size(); ++i)
- source.Reply(replies[i]);
- }
- else
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("The \002LEVELS\002 command allows fine control over the meaning of\n"
- "the numeric access levels used for channels. With this\n"
- "command, you can define the access level required for most\n"
- "of %s's functions. (The \002SET FOUNDER\002 and this command\n"
- "are always restricted to the channel founder.)\n"
- " \n"
- "\002LEVELS SET\002 allows the access level for a function or group of\n"
- "functions to be changed. \002LEVELS DISABLE\002 (or \002DIS\002 for short)\n"
- "disables an automatic feature or disallows access to a\n"
- "function by anyone, INCLUDING the founder (although, the founder\n"
- "can always reenable it). Use \002LEVELS SET founder\002 to make a level\n"
- "founder only.\n"
- " \n"
- "\002LEVELS LIST\002 shows the current levels for each function or\n"
- "group of functions. \002LEVELS RESET\002 resets the levels to the\n"
- "default levels of a newly-created channel.\n"
- " \n"
- "For a list of the features and functions whose levels can be\n"
- "set, see \002HELP LEVELS DESC\002."), source.service->nick.c_str());
- }
- return true;
- }
-};
-
-class CSAccess : public Module
-{
- AccessAccessProvider accessprovider;
- CommandCSAccess commandcsaccess;
- CommandCSLevels commandcslevels;
-
- public:
- CSAccess(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- accessprovider(this), commandcsaccess(this), commandcslevels(this)
- {
- this->SetPermanent(true);
-
- }
-
- void OnReload(Configuration::Conf *conf) anope_override
- {
- defaultLevels.clear();
-
- for (int i = 0; i < conf->CountBlock("privilege"); ++i)
- {
- Configuration::Block *priv = conf->GetBlock("privilege", i);
-
- const Anope::string &pname = priv->Get<const Anope::string>("name");
-
- Privilege *p = PrivilegeManager::FindPrivilege(pname);
- if (p == NULL)
- continue;
-
- const Anope::string &value = priv->Get<const Anope::string>("level");
- if (value.empty())
- continue;
- else if (value.equals_ci("founder"))
- defaultLevels[p->name] = ACCESS_FOUNDER;
- else if (value.equals_ci("disabled"))
- defaultLevels[p->name] = ACCESS_INVALID;
- else
- defaultLevels[p->name] = priv->Get<int16_t>("level");
- }
- }
-
- void OnCreateChan(ChannelInfo *ci) anope_override
- {
- reset_levels(ci);
- }
-
- EventReturn OnGroupCheckPriv(const AccessGroup *group, const Anope::string &priv) anope_override
- {
- if (group->ci == NULL)
- return EVENT_CONTINUE;
- /* Special case. Allows a level of -1 to match anyone, and a level of 0 to match anyone identified. */
- int16_t level = group->ci->GetLevel(priv);
- if (level == -1)
- return EVENT_ALLOW;
- else if (level == 0 && group->nc)
- return EVENT_ALLOW;
- return EVENT_CONTINUE;
- }
-};
-
-MODULE_INIT(CSAccess)
diff --git a/modules/commands/cs_akick.cpp b/modules/commands/cs_akick.cpp
deleted file mode 100644
index cb0a49ccb..000000000
--- a/modules/commands/cs_akick.cpp
+++ /dev/null
@@ -1,572 +0,0 @@
-/* ChanServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class CommandCSAKick : public Command
-{
- void DoAdd(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
- {
- Anope::string mask = params[2];
- Anope::string reason = params.size() > 3 ? params[3] : "";
- const NickAlias *na = NickAlias::Find(mask);
- NickCore *nc = NULL;
- const AutoKick *akick;
- unsigned reasonmax = Config->GetModule("chanserv")->Get<unsigned>("reasonmax", "200");
-
- if (reason.length() > reasonmax)
- reason = reason.substr(0, reasonmax);
-
- if (IRCD->IsExtbanValid(mask))
- ; /* If this is an extban don't try to complete the mask */
- else if (IRCD->IsChannelValid(mask))
- {
- /* Also don't try to complete the mask if this is a channel */
-
- if (mask.equals_ci(ci->name) && ci->HasExt("PEACE"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
- }
- else if (!na)
- {
- /* If the mask contains a realname the reason must be prepended with a : */
- if (mask.find('#') != Anope::string::npos)
- {
- size_t r = reason.find(':');
- if (r != Anope::string::npos)
- {
- mask += " " + reason.substr(0, r);
- mask.trim();
- reason = reason.substr(r + 1);
- reason.trim();
- }
- else
- {
- mask = mask + " " + reason;
- reason.clear();
- }
- }
-
- Entry e("", mask);
-
- mask = (e.nick.empty() ? "*" : e.nick) + "!"
- + (e.user.empty() ? "*" : e.user) + "@"
- + (e.host.empty() ? "*" : e.host);
- if (!e.real.empty())
- mask += "#" + e.real;
- }
- else
- nc = na->nc;
-
- /* Check excepts BEFORE we get this far */
- if (ci->c)
- {
- std::vector<Anope::string> modes = ci->c->GetModeList("EXCEPT");
- for (unsigned int i = 0; i < modes.size(); ++i)
- {
- if (Anope::Match(modes[i], mask))
- {
- source.Reply(CHAN_EXCEPTED, mask.c_str(), ci->name.c_str());
- return;
- }
- }
- }
-
- bool override = !source.AccessFor(ci).HasPriv("AKICK");
- /* Opers overriding get to bypass PEACE */
- if (override)
- ;
- /* These peace checks are only for masks */
- else if (IRCD->IsChannelValid(mask))
- ;
- /* Check whether target nick has equal/higher access
- * or whether the mask matches a user with higher/equal access - Viper */
- else if (ci->HasExt("PEACE") && nc)
- {
- AccessGroup nc_access = ci->AccessFor(nc), u_access = source.AccessFor(ci);
- if (nc == ci->GetFounder() || nc_access >= u_access)
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
- }
- else if (ci->HasExt("PEACE"))
- {
- /* Match against all currently online users with equal or
- * higher access. - Viper */
- for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it)
- {
- User *u2 = it->second;
-
- AccessGroup nc_access = ci->AccessFor(nc), u_access = source.AccessFor(ci);
- Entry entry_mask("", mask);
-
- if ((ci->AccessFor(u2).HasPriv("FOUNDER") || nc_access >= u_access) && entry_mask.Matches(u2))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
- }
-
- /* Match against the lastusermask of all nickalias's with equal
- * or higher access. - Viper */
- for (nickalias_map::const_iterator it = NickAliasList->begin(), it_end = NickAliasList->end(); it != it_end; ++it)
- {
- na = it->second;
-
- AccessGroup nc_access = ci->AccessFor(na->nc), u_access = source.AccessFor(ci);
- if (na->nc && (na->nc == ci->GetFounder() || nc_access >= u_access))
- {
- Anope::string buf = na->nick + "!" + na->last_usermask;
- if (Anope::Match(buf, mask))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
- }
- }
- }
-
- for (unsigned j = 0, end = ci->GetAkickCount(); j < end; ++j)
- {
- akick = ci->GetAkick(j);
- if (akick->nc ? akick->nc == nc : mask.equals_ci(akick->mask))
- {
- source.Reply(_("\002%s\002 already exists on %s autokick list."), akick->nc ? akick->nc->display.c_str() : akick->mask.c_str(), ci->name.c_str());
- return;
- }
- }
-
- if (ci->GetAkickCount() >= Config->GetModule(this->owner)->Get<unsigned>("autokickmax"))
- {
- source.Reply(_("Sorry, you can only have %d autokick masks on a channel."), Config->GetModule(this->owner)->Get<unsigned>("autokickmax"));
- return;
- }
-
- if (nc)
- akick = ci->AddAkick(source.GetNick(), nc, reason);
- else
- akick = ci->AddAkick(source.GetNick(), mask, reason);
-
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to add " << mask << (reason == "" ? "" : ": ") << reason;
-
- FOREACH_MOD(OnAkickAdd, (source, ci, akick));
-
- source.Reply(_("\002%s\002 added to %s autokick list."), mask.c_str(), ci->name.c_str());
-
- this->DoEnforce(source, ci);
- }
-
- void DoDel(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
- {
- const Anope::string &mask = params[2];
- unsigned i, end;
-
- if (!ci->GetAkickCount())
- {
- source.Reply(_("%s autokick list is empty."), ci->name.c_str());
- return;
- }
-
- /* Special case: is it a number/list? Only do search if it isn't. */
- if (isdigit(mask[0]) && mask.find_first_not_of("1234567890,-") == Anope::string::npos)
- {
- class AkickDelCallback : public NumberList
- {
- CommandSource &source;
- ChannelInfo *ci;
- Command *c;
- unsigned deleted;
- AccessGroup ag;
- public:
- AkickDelCallback(CommandSource &_source, ChannelInfo *_ci, Command *_c, const Anope::string &list) : NumberList(list, true), source(_source), ci(_ci), c(_c), deleted(0), ag(source.AccessFor(ci))
- {
- }
-
- ~AkickDelCallback()
- {
- if (!deleted)
- source.Reply(_("No matching entries on %s autokick list."), ci->name.c_str());
- else if (deleted == 1)
- source.Reply(_("Deleted 1 entry from %s autokick list."), ci->name.c_str());
- else
- source.Reply(_("Deleted %d entries from %s autokick list."), deleted, ci->name.c_str());
- }
-
- void HandleNumber(unsigned number) anope_override
- {
- if (!number || number > ci->GetAkickCount())
- return;
-
- const AutoKick *akick = ci->GetAkick(number - 1);
-
- FOREACH_MOD(OnAkickDel, (source, ci, akick));
-
- bool override = !ag.HasPriv("AKICK");
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, c, ci) << "to delete " << (akick->nc ? akick->nc->display : akick->mask);
-
- ++deleted;
- ci->EraseAkick(number - 1);
- }
- }
- delcallback(source, ci, this, mask);
- delcallback.Process();
- }
- else
- {
- const NickAlias *na = NickAlias::Find(mask);
- const NickCore *nc = na ? *na->nc : NULL;
-
- for (i = 0, end = ci->GetAkickCount(); i < end; ++i)
- {
- const AutoKick *akick = ci->GetAkick(i);
-
- if (akick->nc ? akick->nc == nc : mask.equals_ci(akick->mask))
- break;
- }
-
- if (i == ci->GetAkickCount())
- {
- source.Reply(_("\002%s\002 not found on %s autokick list."), mask.c_str(), ci->name.c_str());
- return;
- }
-
- bool override = !source.AccessFor(ci).HasPriv("AKICK");
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to delete " << mask;
-
- FOREACH_MOD(OnAkickDel, (source, ci, ci->GetAkick(i)));
-
- ci->EraseAkick(i);
-
- source.Reply(_("\002%s\002 deleted from %s autokick list."), mask.c_str(), ci->name.c_str());
- }
- }
-
- void ProcessList(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params, ListFormatter &list)
- {
- const Anope::string &mask = params.size() > 2 ? params[2] : "";
-
- if (!mask.empty() && isdigit(mask[0]) && mask.find_first_not_of("1234567890,-") == Anope::string::npos)
- {
- class AkickListCallback : public NumberList
- {
- ListFormatter &list;
- ChannelInfo *ci;
-
- public:
- AkickListCallback(ListFormatter &_list, ChannelInfo *_ci, const Anope::string &numlist) : NumberList(numlist, false), list(_list), ci(_ci)
- {
- }
-
- void HandleNumber(unsigned number) anope_override
- {
- if (!number || number > ci->GetAkickCount())
- return;
-
- const AutoKick *akick = ci->GetAkick(number - 1);
-
- Anope::string timebuf, lastused;
- if (akick->addtime)
- timebuf = Anope::strftime(akick->addtime, NULL, true);
- else
- timebuf = UNKNOWN;
- if (akick->last_used)
- lastused = Anope::strftime(akick->last_used, NULL, true);
- else
- lastused = UNKNOWN;
-
- ListFormatter::ListEntry entry;
- entry["Number"] = stringify(number);
- if (akick->nc)
- entry["Mask"] = akick->nc->display;
- else
- entry["Mask"] = akick->mask;
- entry["Creator"] = akick->creator;
- entry["Created"] = timebuf;
- entry["Last used"] = lastused;
- entry["Reason"] = akick->reason;
- this->list.AddEntry(entry);
- }
- }
- nl_list(list, ci, mask);
- nl_list.Process();
- }
- else
- {
- for (unsigned i = 0, end = ci->GetAkickCount(); i < end; ++i)
- {
- const AutoKick *akick = ci->GetAkick(i);
-
- if (!mask.empty())
- {
- if (!akick->nc && !Anope::Match(akick->mask, mask))
- continue;
- if (akick->nc && !Anope::Match(akick->nc->display, mask))
- continue;
- }
-
- Anope::string timebuf, lastused;
- if (akick->addtime)
- timebuf = Anope::strftime(akick->addtime, NULL, true);
- else
- timebuf = UNKNOWN;
- if (akick->last_used)
- lastused = Anope::strftime(akick->last_used, NULL, true);
- else
- lastused = UNKNOWN;
-
- ListFormatter::ListEntry entry;
- entry["Number"] = stringify(i + 1);
- if (akick->nc)
- entry["Mask"] = akick->nc->display;
- else
- entry["Mask"] = akick->mask;
- entry["Creator"] = akick->creator;
- entry["Created"] = timebuf;
- entry["Last used"] = lastused;
- entry["Reason"] = akick->reason;
- list.AddEntry(entry);
- }
- }
-
- if (list.IsEmpty())
- source.Reply(_("No matching entries on %s autokick list."), ci->name.c_str());
- else
- {
- std::vector<Anope::string> replies;
- list.Process(replies);
-
- source.Reply(_("Autokick list for %s:"), ci->name.c_str());
-
- for (unsigned i = 0; i < replies.size(); ++i)
- source.Reply(replies[i]);
-
- source.Reply(_("End of autokick list"));
- }
- }
-
- void DoList(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
- {
- if (!ci->GetAkickCount())
- {
- source.Reply(_("%s autokick list is empty."), ci->name.c_str());
- return;
- }
-
- ListFormatter list(source.GetAccount());
- list.AddColumn(_("Number")).AddColumn(_("Mask")).AddColumn(_("Reason"));
- this->ProcessList(source, ci, params, list);
- }
-
- void DoView(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
- {
- if (!ci->GetAkickCount())
- {
- source.Reply(_("%s autokick list is empty."), ci->name.c_str());
- return;
- }
-
- ListFormatter list(source.GetAccount());
- list.AddColumn(_("Number")).AddColumn(_("Mask")).AddColumn(_("Creator")).AddColumn(_("Created")).AddColumn(_("Last used")).AddColumn(_("Reason"));
- this->ProcessList(source, ci, params, list);
- }
-
- void DoEnforce(CommandSource &source, ChannelInfo *ci)
- {
- Channel *c = ci->c;
- int count = 0;
-
- if (!c)
- {
- source.Reply(CHAN_X_NOT_IN_USE, ci->name.c_str());
- return;
- }
-
- for (Channel::ChanUserList::iterator it = c->users.begin(), it_end = c->users.end(); it != it_end; )
- {
- ChanUserContainer *uc = it->second;
- ++it;
-
- if (c->CheckKick(uc->user))
- ++count;
- }
-
- bool override = !source.AccessFor(ci).HasPriv("AKICK");
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "ENFORCE, affects " << count << " users";
-
- source.Reply(_("AKICK ENFORCE for \002%s\002 complete; \002%d\002 users were affected."), ci->name.c_str(), count);
- }
-
- void DoClear(CommandSource &source, ChannelInfo *ci)
- {
- bool override = !source.AccessFor(ci).HasPriv("AKICK");
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to clear the akick list";
-
- ci->ClearAkick();
- source.Reply(_("Channel %s akick list has been cleared."), ci->name.c_str());
- }
-
- public:
- CommandCSAKick(Module *creator) : Command(creator, "chanserv/akick", 2, 4)
- {
- this->SetDesc(_("Maintain the AutoKick list"));
- this->SetSyntax(_("\037channel\037 ADD {\037nick\037 | \037mask\037} [\037reason\037]"));
- this->SetSyntax(_("\037channel\037 DEL {\037nick\037 | \037mask\037 | \037entry-num\037 | \037list\037}"));
- this->SetSyntax(_("\037channel\037 LIST [\037mask\037 | \037entry-num\037 | \037list\037]"));
- this->SetSyntax(_("\037channel\037 VIEW [\037mask\037 | \037entry-num\037 | \037list\037]"));
- this->SetSyntax(_("\037channel\037 ENFORCE"));
- this->SetSyntax(_("\037channel\037 CLEAR"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- Anope::string chan = params[0];
- Anope::string cmd = params[1];
- Anope::string mask = params.size() > 2 ? params[2] : "";
-
- ChannelInfo *ci = ChannelInfo::Find(params[0]);
- if (ci == NULL)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
- return;
- }
-
- bool is_list = cmd.equals_ci("LIST") || cmd.equals_ci("VIEW");
-
- bool has_access = false;
- if (source.AccessFor(ci).HasPriv("AKICK") || source.HasPriv("chanserv/access/modify"))
- has_access = true;
- else if (is_list && source.HasPriv("chanserv/access/list"))
- has_access = true;
-
- if (mask.empty() && (cmd.equals_ci("ADD") || cmd.equals_ci("DEL")))
- this->OnSyntaxError(source, cmd);
- else if (!has_access)
- source.Reply(ACCESS_DENIED);
- else if (!cmd.equals_ci("LIST") && !cmd.equals_ci("VIEW") && !cmd.equals_ci("ENFORCE") && Anope::ReadOnly)
- source.Reply(_("Sorry, channel autokick list modification is temporarily disabled."));
- else if (cmd.equals_ci("ADD"))
- this->DoAdd(source, ci, params);
- else if (cmd.equals_ci("DEL"))
- this->DoDel(source, ci, params);
- else if (cmd.equals_ci("LIST"))
- this->DoList(source, ci, params);
- else if (cmd.equals_ci("VIEW"))
- this->DoView(source, ci, params);
- else if (cmd.equals_ci("ENFORCE"))
- this->DoEnforce(source, ci);
- else if (cmd.equals_ci("CLEAR"))
- this->DoClear(source, ci);
- else
- this->OnSyntaxError(source, "");
-
- return;
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- BotInfo *bi = Config->GetClient("NickServ");
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Maintains the \002AutoKick list\002 for a channel. If a user\n"
- "on the AutoKick list attempts to join the channel,\n"
- "%s will ban that user from the channel, then kick\n"
- "the user.\n"
- " \n"
- "The \002AKICK ADD\002 command adds the given nick or usermask\n"
- "to the AutoKick list. If a \037reason\037 is given with\n"
- "the command, that reason will be used when the user is\n"
- "kicked; if not, the default reason is \"User has been\n"
- "banned from the channel\".\n"
- "When akicking a \037registered nick\037 the %s account\n"
- "will be added to the akick list instead of the mask.\n"
- "All users within that nickgroup will then be akicked.\n"),
- source.service->nick.c_str(), bi ? bi->nick.c_str() : "NickServ");
- source.Reply(_(
- " \n"
- "The \002AKICK DEL\002 command removes the given nick or mask\n"
- "from the AutoKick list. It does not, however, remove any\n"
- "bans placed by an AutoKick; those must be removed\n"
- "manually.\n"
- " \n"
- "The \002AKICK LIST\002 command displays the AutoKick list, or\n"
- "optionally only those AutoKick entries which match the\n"
- "given mask.\n"
- " \n"
- "The \002AKICK VIEW\002 command is a more verbose version of the\n"
- "\002AKICK LIST\002 command.\n"
- " \n"
- "The \002AKICK ENFORCE\002 command causes %s to enforce the\n"
- "current AKICK list by removing those users who match an\n"
- "AKICK mask.\n"
- " \n"
- "The \002AKICK CLEAR\002 command clears all entries of the\n"
- "akick list."), source.service->nick.c_str());
- return true;
- }
-};
-
-class CSAKick : public Module
-{
- CommandCSAKick commandcsakick;
-
- public:
- CSAKick(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandcsakick(this)
- {
- }
-
- EventReturn OnCheckKick(User *u, Channel *c, Anope::string &mask, Anope::string &reason) anope_override
- {
- if (!c->ci || c->MatchesList(u, "EXCEPT"))
- return EVENT_CONTINUE;
-
- for (unsigned j = 0, end = c->ci->GetAkickCount(); j < end; ++j)
- {
- AutoKick *autokick = c->ci->GetAkick(j);
- bool kick = false;
-
- if (autokick->nc)
- kick = autokick->nc == u->Account();
- else if (IRCD->IsChannelValid(autokick->mask))
- {
- Channel *chan = Channel::Find(autokick->mask);
- kick = chan != NULL && chan->FindUser(u);
- }
- else
- kick = Entry("BAN", autokick->mask).Matches(u);
-
- if (kick)
- {
- Log(LOG_DEBUG_2) << u->nick << " matched akick " << (autokick->nc ? autokick->nc->display : autokick->mask);
- autokick->last_used = Anope::CurTime;
- if (!autokick->nc && autokick->mask.find('#') == Anope::string::npos)
- mask = autokick->mask;
- reason = autokick->reason;
- if (reason.empty())
- {
- reason = Language::Translate(u, Config->GetModule(this)->Get<const Anope::string>("autokickreason").c_str());
- reason = reason.replace_all_cs("%n", u->nick)
- .replace_all_cs("%c", c->name);
- }
- if (reason.empty())
- reason = Language::Translate(u, _("User has been banned from the channel"));
- return EVENT_STOP;
- }
- }
-
- return EVENT_CONTINUE;
- }
-};
-
-MODULE_INIT(CSAKick)
diff --git a/modules/commands/cs_ban.cpp b/modules/commands/cs_ban.cpp
deleted file mode 100644
index 1cf316b88..000000000
--- a/modules/commands/cs_ban.cpp
+++ /dev/null
@@ -1,248 +0,0 @@
-/* ChanServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-static Module *me;
-
-class TempBan : public Timer
-{
- private:
- Anope::string channel;
- Anope::string mask;
- Anope::string mode;
-
- public:
- TempBan(time_t seconds, Channel *c, const Anope::string &banmask, const Anope::string &mod) : Timer(me, seconds), channel(c->name), mask(banmask), mode(mod) { }
-
- void Tick(time_t ctime) anope_override
- {
- Channel *c = Channel::Find(this->channel);
- if (c)
- c->RemoveMode(NULL, mode, this->mask);
- }
-};
-
-class CommandCSBan : public Command
-{
- public:
- CommandCSBan(Module *creator) : Command(creator, "chanserv/ban", 2, 4)
- {
- this->SetDesc(_("Bans a given nick or mask on a channel"));
- this->SetSyntax(_("\037channel\037 [+\037expiry\037] {\037nick\037 | \037mask\037} [\037reason\037]"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &chan = params[0];
- Configuration::Block *block = Config->GetCommand(source);
- const Anope::string &mode = block->Get<Anope::string>("mode", "BAN");
-
- ChannelInfo *ci = ChannelInfo::Find(chan);
- if (ci == NULL)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, chan.c_str());
- return;
- }
-
- Channel *c = ci->c;
- if (c == NULL)
- {
- source.Reply(CHAN_X_NOT_IN_USE, chan.c_str());
- return;
- }
- else if (IRCD->GetMaxListFor(c) && c->HasMode(mode) >= IRCD->GetMaxListFor(c))
- {
- source.Reply(_("The %s list for %s is full."), mode.lower().c_str(), c->name.c_str());
- return;
- }
-
- Anope::string expiry, target, reason;
- time_t ban_time;
- if (params[1][0] == '+')
- {
- ban_time = Anope::DoTime(params[1]);
- if (ban_time == -1)
- {
- source.Reply(BAD_EXPIRY_TIME);
- return;
- }
- if (params.size() < 3)
- {
- this->SendSyntax(source);
- return;
- }
- target = params[2];
- reason = "Requested";
- if (params.size() > 3)
- reason = params[3];
- }
- else
- {
- ban_time = 0;
- target = params[1];
- reason = "Requested";
- if (params.size() > 2)
- reason = params[2];
- if (params.size() > 3)
- reason += " " + params[3];
- }
-
- unsigned reasonmax = Config->GetModule("chanserv")->Get<unsigned>("reasonmax", "200");
- if (reason.length() > reasonmax)
- reason = reason.substr(0, reasonmax);
-
- Anope::string signkickformat = Config->GetModule("chanserv")->Get<Anope::string>("signkickformat", "%m (%n)");
- signkickformat = signkickformat.replace_all_cs("%n", source.GetNick());
-
- User *u = source.GetUser();
- User *u2 = User::Find(target, true);
-
- AccessGroup u_access = source.AccessFor(ci);
-
- if (!u_access.HasPriv("BAN") && !source.HasPriv("chanserv/kick"))
- source.Reply(ACCESS_DENIED);
- else if (u2)
- {
- AccessGroup u2_access = ci->AccessFor(u2);
-
- if (u != u2 && ci->HasExt("PEACE") && u2_access >= u_access && !source.HasPriv("chanserv/kick"))
- source.Reply(ACCESS_DENIED);
- /*
- * Don't ban/kick the user on channels where he is excepted
- * to prevent services <-> server wars.
- */
- else if (c->MatchesList(u2, "EXCEPT"))
- source.Reply(CHAN_EXCEPTED, u2->nick.c_str(), ci->name.c_str());
- else if (u2->IsProtected())
- source.Reply(ACCESS_DENIED);
- else
- {
- Anope::string mask = ci->GetIdealBan(u2);
-
- bool override = !u_access.HasPriv("BAN") || (u != u2 && ci->HasExt("PEACE") && u2_access >= u_access);
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "for " << mask;
-
- if (!c->HasMode(mode, mask))
- {
- c->SetMode(NULL, mode, mask);
- if (ban_time)
- {
- new TempBan(ban_time, c, mask, mode);
- source.Reply(_("Ban on \002%s\002 expires in %s."), mask.c_str(), Anope::Duration(ban_time, source.GetAccount()).c_str());
- }
- }
-
- /* We still allow host banning while not allowing to kick */
- if (!c->FindUser(u2))
- return;
-
- if (block->Get<bool>("kick", "yes"))
- {
- if (ci->HasExt("SIGNKICK") || (ci->HasExt("SIGNKICK_LEVEL") && !source.AccessFor(ci).HasPriv("SIGNKICK")))
- {
- signkickformat = signkickformat.replace_all_cs("%m", reason);
- c->Kick(ci->WhoSends(), u2, "%s", signkickformat.c_str());
- }
- else
- c->Kick(ci->WhoSends(), u2, "%s", reason.c_str());
- }
- }
- }
- else
- {
- bool founder = u_access.HasPriv("FOUNDER");
- bool override = !founder && !u_access.HasPriv("BAN");
-
- Anope::string mask = IRCD->NormalizeMask(target);
-
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "for " << mask;
-
- if (!c->HasMode(mode, mask))
- {
- c->SetMode(NULL, mode, mask);
- if (ban_time)
- {
- new TempBan(ban_time, c, mask, mode);
- source.Reply(_("Ban on \002%s\002 expires in %s."), mask.c_str(), Anope::Duration(ban_time, source.GetAccount()).c_str());
- }
- }
-
- int matched = 0, kicked = 0;
- for (Channel::ChanUserList::iterator it = c->users.begin(), it_end = c->users.end(); it != it_end;)
- {
- ChanUserContainer *uc = it->second;
- ++it;
-
- Entry e(mode, mask);
- if (e.Matches(uc->user))
- {
- ++matched;
-
- AccessGroup u2_access = ci->AccessFor(uc->user);
-
- if (matched > 1 && !founder)
- continue;
- if (u != uc->user && ci->HasExt("PEACE") && u2_access >= u_access)
- continue;
- else if (ci->c->MatchesList(uc->user, "EXCEPT"))
- continue;
- else if (uc->user->IsProtected())
- continue;
-
- if (block->Get<bool>("kick", "yes"))
- {
- ++kicked;
- if (ci->HasExt("SIGNKICK") || (ci->HasExt("SIGNKICK_LEVEL") && !u_access.HasPriv("SIGNKICK")))
- {
- reason += " (Matches " + mask + ")";
- signkickformat = signkickformat.replace_all_cs("%m", reason);
- c->Kick(ci->WhoSends(), uc->user, "%s", signkickformat.c_str());
- }
- else
- c->Kick(ci->WhoSends(), uc->user, "%s (Matches %s)", reason.c_str(), mask.c_str());
- }
- }
- }
-
- if (matched)
- source.Reply(_("Kicked %d/%d users matching %s from %s."), kicked, matched, mask.c_str(), c->name.c_str());
- else
- source.Reply(_("No users on %s match %s."), c->name.c_str(), mask.c_str());
- }
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Bans a given nick or mask on a channel. An optional expiry may\n"
- "be given to cause services to remove the ban after a set amount\n"
- "of time.\n"
- " \n"
- "By default, limited to AOPs or those with level 5 access\n"
- "and above on the channel. Channel founders may ban masks."));
- return true;
- }
-};
-
-class CSBan : public Module
-{
- CommandCSBan commandcsban;
-
- public:
- CSBan(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), commandcsban(this)
- {
- me = this;
- }
-};
-
-MODULE_INIT(CSBan)
diff --git a/modules/commands/cs_clone.cpp b/modules/commands/cs_clone.cpp
deleted file mode 100644
index a4ec31c1e..000000000
--- a/modules/commands/cs_clone.cpp
+++ /dev/null
@@ -1,261 +0,0 @@
-/* ChanServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-#include "modules/bs_badwords.h"
-
-class CommandCSClone : public Command
-{
- void CopySetting(ChannelInfo *ci, ChannelInfo *target_ci, const Anope::string &setting)
- {
- if (ci->HasExt(setting))
- target_ci->Extend<bool>(setting);
- }
-
- void CopyAccess(CommandSource &source, ChannelInfo *ci, ChannelInfo *target_ci)
- {
- std::set<Anope::string> masks;
- unsigned access_max = Config->GetModule("chanserv")->Get<unsigned>("accessmax", "1024");
- unsigned count = 0;
-
- for (unsigned i = 0; i < target_ci->GetAccessCount(); ++i)
- masks.insert(target_ci->GetAccess(i)->Mask());
-
- for (unsigned i = 0; i < ci->GetAccessCount(); ++i)
- {
- const ChanAccess *taccess = ci->GetAccess(i);
- AccessProvider *provider = taccess->provider;
-
- if (access_max && target_ci->GetDeepAccessCount() >= access_max)
- break;
-
- if (masks.count(taccess->Mask()))
- continue;
- masks.insert(taccess->Mask());
-
- ChanAccess *newaccess = provider->Create();
- newaccess->SetMask(taccess->Mask(), target_ci);
- newaccess->creator = taccess->creator;
- newaccess->last_seen = taccess->last_seen;
- newaccess->created = taccess->created;
- newaccess->AccessUnserialize(taccess->AccessSerialize());
-
- target_ci->AddAccess(newaccess);
-
- ++count;
- }
-
- source.Reply(_("%d access entries from \002%s\002 have been cloned to \002%s\002."), count, ci->name.c_str(), target_ci->name.c_str());
- }
-
- void CopyAkick(CommandSource &source, ChannelInfo *ci, ChannelInfo *target_ci)
- {
- target_ci->ClearAkick();
- for (unsigned i = 0; i < ci->GetAkickCount(); ++i)
- {
- const AutoKick *akick = ci->GetAkick(i);
- if (akick->nc)
- target_ci->AddAkick(akick->creator, akick->nc, akick->reason, akick->addtime, akick->last_used);
- else
- target_ci->AddAkick(akick->creator, akick->mask, akick->reason, akick->addtime, akick->last_used);
- }
-
- source.Reply(_("All akick entries from \002%s\002 have been cloned to \002%s\002."), ci->name.c_str(), target_ci->name.c_str());
- }
-
- void CopyBadwords(CommandSource &source, ChannelInfo *ci, ChannelInfo *target_ci)
- {
- BadWords *target_badwords = target_ci->Require<BadWords>("badwords"),
- *badwords = ci->Require<BadWords>("badwords");
-
- if (!target_badwords || !badwords)
- {
- source.Reply(ACCESS_DENIED); // BotServ doesn't exist/badwords isn't loaded
- return;
- }
-
- target_badwords->ClearBadWords();
-
- for (unsigned i = 0; i < badwords->GetBadWordCount(); ++i)
- {
- const BadWord *bw = badwords->GetBadWord(i);
- target_badwords->AddBadWord(bw->word, bw->type);
- }
-
- badwords->Check();
- target_badwords->Check();
-
- source.Reply(_("All badword entries from \002%s\002 have been cloned to \002%s\002."), ci->name.c_str(), target_ci->name.c_str());
- }
-
- void CopyLevels(CommandSource &source, ChannelInfo *ci, ChannelInfo *target_ci)
- {
- const Anope::map<int16_t> &cilevels = ci->GetLevelEntries();
-
- for (Anope::map<int16_t>::const_iterator it = cilevels.begin(); it != cilevels.end(); ++it)
- {
- target_ci->SetLevel(it->first, it->second);
- }
-
- source.Reply(_("All level entries from \002%s\002 have been cloned into \002%s\002."), ci->name.c_str(), target_ci->name.c_str());
- }
-
-public:
- CommandCSClone(Module *creator) : Command(creator, "chanserv/clone", 2, 3)
- {
- this->SetDesc(_("Copy all settings from one channel to another"));
- this->SetSyntax(_("\037channel\037 \037target\037 [\037what\037]"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &channel = params[0];
- const Anope::string &target = params[1];
- Anope::string what = params.size() > 2 ? params[2] : "";
-
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- User *u = source.GetUser();
- ChannelInfo *ci = ChannelInfo::Find(channel);
- bool override = false;
-
- if (ci == NULL)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, channel.c_str());
- return;
- }
-
- ChannelInfo *target_ci = ChannelInfo::Find(target);
- if (!target_ci)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, target.c_str());
- return;
- }
-
- if (ci == target_ci)
- {
- source.Reply(_("Cannot clone channel \002%s\002 to itself!"), target.c_str());
- return;
- }
-
- if (!source.IsFounder(ci) || !source.IsFounder(target_ci))
- {
- if (!source.HasPriv("chanserv/administration"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
- else
- override = true;
- }
-
- if (what.equals_ci("ALL"))
- what.clear();
-
- if (what.empty())
- {
- delete target_ci;
- target_ci = new ChannelInfo(*ci);
- target_ci->name = target;
- target_ci->time_registered = Anope::CurTime;
- (*RegisteredChannelList)[target_ci->name] = target_ci;
- target_ci->c = Channel::Find(target_ci->name);
-
- target_ci->bi = NULL;
- if (ci->bi)
- ci->bi->Assign(u, target_ci);
-
- if (target_ci->c)
- {
- target_ci->c->ci = target_ci;
-
- target_ci->c->CheckModes();
-
- target_ci->c->SetCorrectModes(u, true);
- }
-
- if (target_ci->c && !target_ci->c->topic.empty())
- {
- target_ci->last_topic = target_ci->c->topic;
- target_ci->last_topic_setter = target_ci->c->topic_setter;
- target_ci->last_topic_time = target_ci->c->topic_time;
- }
- else
- target_ci->last_topic_setter = source.service->nick;
-
- const Anope::string settings[] = { "NOAUTOOP", "CS_KEEP_MODES", "PEACE", "PERSIST", "RESTRICTED",
- "CS_SECURE", "SECUREFOUNDER", "SECUREOPS", "SIGNKICK", "SIGNKICK_LEVEL", "CS_NO_EXPIRE" };
-
- for (unsigned int i = 0; i < sizeof(settings) / sizeof(Anope::string); ++i)
- CopySetting(ci, target_ci, settings[i]);
-
- CopyAccess(source, ci, target_ci);
- CopyAkick(source, ci, target_ci);
- CopyBadwords(source, ci, target_ci);
- CopyLevels(source, ci, target_ci);
-
- FOREACH_MOD(OnChanRegistered, (target_ci));
-
- source.Reply(_("All settings from \002%s\002 have been cloned to \002%s\002."), ci->name.c_str(), target_ci->name.c_str());
- }
- else if (what.equals_ci("ACCESS"))
- {
- CopyAccess(source, ci, target_ci);
- }
- else if (what.equals_ci("AKICK"))
- {
- CopyAkick(source, ci, target_ci);
- }
- else if (what.equals_ci("BADWORDS"))
- {
- CopyBadwords(source, ci, target_ci);
- }
- else if (what.equals_ci("LEVELS"))
- {
- CopyLevels(source, ci, target_ci);
- }
- else
- {
- this->OnSyntaxError(source, "");
- return;
- }
-
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to clone " << (what.empty() ? "everything from it" : what) << " to " << target_ci->name;
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Copies all settings, access, akicks, etc from \002channel\002 to the\n"
- "\002target\002 channel. If \037what\037 is \002ACCESS\002, \002AKICK\002, \002BADWORDS\002,\n"
- "or \002LEVELS\002 then only the respective settings are cloned.\n"
- "You must be the founder of \037channel\037 and \037target\037."));
- return true;
- }
-};
-
-class CSClone : public Module
-{
- CommandCSClone commandcsclone;
-
- public:
- CSClone(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), commandcsclone(this)
- {
-
- }
-};
-
-MODULE_INIT(CSClone)
diff --git a/modules/commands/cs_drop.cpp b/modules/commands/cs_drop.cpp
deleted file mode 100644
index 83299aee5..000000000
--- a/modules/commands/cs_drop.cpp
+++ /dev/null
@@ -1,95 +0,0 @@
-/* ChanServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class CommandCSDrop : public Command
-{
- public:
- CommandCSDrop(Module *creator) : Command(creator, "chanserv/drop", 1, 2)
- {
- this->SetDesc(_("Cancel the registration of a channel"));
- this->SetSyntax(_("\037channel\037 \037channel\037"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &chan = params[0];
-
- if (Anope::ReadOnly && !source.HasPriv("chanserv/administration"))
- {
- source.Reply(_("Sorry, channel de-registration is temporarily disabled.")); // XXX: READ_ONLY_MODE?
- return;
- }
-
- ChannelInfo *ci = ChannelInfo::Find(chan);
- if (ci == NULL)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
- return;
- }
-
- if (params.size() < 2 || !chan.equals_ci(params[1]))
- {
- source.Reply(_("You must enter the channel name twice as a confirmation that you wish to drop \002%s\002."), chan.c_str());
- return;
- }
-
- if ((ci->HasExt("SECUREFOUNDER") ? !source.IsFounder(ci) : !source.AccessFor(ci).HasPriv("FOUNDER")) && !source.HasCommand("chanserv/drop"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- EventReturn MOD_RESULT;
- FOREACH_RESULT(OnChanDrop, MOD_RESULT, (source, ci));
- if (MOD_RESULT == EVENT_STOP)
- return;
-
- bool override = (ci->HasExt("SECUREFOUNDER") ? !source.IsFounder(ci) : !source.AccessFor(ci).HasPriv("FOUNDER"));
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "(founder was: " << (ci->GetFounder() ? ci->GetFounder()->display : "none") << ")";
-
- Reference<Channel> c = ci->c;
- delete ci;
-
- source.Reply(_("Channel \002%s\002 has been dropped."), chan.c_str());
-
- if (c)
- c->CheckModes();
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- if (source.IsServicesOper())
- source.Reply(_("Unregisters the specified channel. Only \002Services Operators\002\n"
- "can drop a channel of which they are not the founder of."));
- else
- source.Reply(_("Unregisters the named channel. Can only be used by\n"
- "the \002channel founder\002."));
-
- return true;
- }
-};
-
-class CSDrop : public Module
-{
- CommandCSDrop commandcsdrop;
-
- public:
- CSDrop(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), commandcsdrop(this)
- {
-
- }
-};
-
-MODULE_INIT(CSDrop)
diff --git a/modules/commands/cs_entrymsg.cpp b/modules/commands/cs_entrymsg.cpp
deleted file mode 100644
index 796aa5148..000000000
--- a/modules/commands/cs_entrymsg.cpp
+++ /dev/null
@@ -1,289 +0,0 @@
-/* ChanServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-#include "modules/cs_entrymsg.h"
-
-struct EntryMsgImpl : EntryMsg, Serializable
-{
- EntryMsgImpl() : Serializable("EntryMsg")
- {
- }
-
- EntryMsgImpl(ChannelInfo *c, const Anope::string &cname, const Anope::string &cmessage, time_t ct = Anope::CurTime) : Serializable("EntryMsg")
- {
- this->chan = c->name;
- this->creator = cname;
- this->message = cmessage;
- this->when = ct;
- }
-
- ~EntryMsgImpl();
-
- void Serialize(Serialize::Data &data) const anope_override
- {
- data["ci"] << this->chan;
- data["creator"] << this->creator;
- data["message"] << this->message;
- data.SetType("when", Serialize::Data::DT_INT); data["when"] << this->when;
- }
-
- static Serializable* Unserialize(Serializable *obj, Serialize::Data &data);
-};
-
-struct EntryMessageListImpl : EntryMessageList
-{
- EntryMessageListImpl(Extensible *) { }
-
- EntryMsg* Create() anope_override
- {
- return new EntryMsgImpl();
- }
-};
-
-EntryMsgImpl::~EntryMsgImpl()
-{
- ChannelInfo *ci = ChannelInfo::Find(this->chan);
- if (!ci)
- return;
-
- EntryMessageList *messages = ci->GetExt<EntryMessageList>("entrymsg");
- if (!messages)
- return;
-
- std::vector<EntryMsg *>::iterator it = std::find((*messages)->begin(), (*messages)->end(), this);
- if (it != (*messages)->end())
- (*messages)->erase(it);
-}
-
-
-Serializable* EntryMsgImpl::Unserialize(Serializable *obj, Serialize::Data &data)
-{
- Anope::string sci, screator, smessage;
- time_t swhen;
-
- data["ci"] >> sci;
- data["creator"] >> screator;
- data["message"] >> smessage;
-
- ChannelInfo *ci = ChannelInfo::Find(sci);
- if (!ci)
- return NULL;
-
- if (obj)
- {
- EntryMsgImpl *msg = anope_dynamic_static_cast<EntryMsgImpl *>(obj);
- msg->chan = ci->name;
- data["creator"] >> msg->creator;
- data["message"] >> msg->message;
- data["when"] >> msg->when;
- return msg;
- }
-
- EntryMessageList *messages = ci->Require<EntryMessageList>("entrymsg");
-
- data["when"] >> swhen;
-
- EntryMsgImpl *m = new EntryMsgImpl(ci, screator, smessage, swhen);
- (*messages)->push_back(m);
- return m;
-}
-
-class CommandEntryMessage : public Command
-{
- private:
- void DoList(CommandSource &source, ChannelInfo *ci)
- {
- EntryMessageList *messages = ci->Require<EntryMessageList>("entrymsg");
-
- if ((*messages)->empty())
- {
- source.Reply(_("Entry message list for \002%s\002 is empty."), ci->name.c_str());
- return;
- }
-
- source.Reply(_("Entry message list for \002%s\002:"), ci->name.c_str());
-
- ListFormatter list(source.GetAccount());
- list.AddColumn(_("Number")).AddColumn(_("Creator")).AddColumn(_("Created")).AddColumn(_("Message"));
- for (unsigned i = 0; i < (*messages)->size(); ++i)
- {
- EntryMsg *msg = (*messages)->at(i);
-
- ListFormatter::ListEntry entry;
- entry["Number"] = stringify(i + 1);
- entry["Creator"] = msg->creator;
- entry["Created"] = Anope::strftime(msg->when, NULL, true);
- entry["Message"] = msg->message;
- list.AddEntry(entry);
- }
-
- std::vector<Anope::string> replies;
- list.Process(replies);
- for (unsigned i = 0; i < replies.size(); ++i)
- source.Reply(replies[i]);
-
- source.Reply(_("End of entry message list."));
- }
-
- void DoAdd(CommandSource &source, ChannelInfo *ci, const Anope::string &message)
- {
- EntryMessageList *messages = ci->Require<EntryMessageList>("entrymsg");
-
- if ((*messages)->size() >= Config->GetModule(this->owner)->Get<unsigned>("maxentries"))
- source.Reply(_("The entry message list for \002%s\002 is full."), ci->name.c_str());
- else
- {
- (*messages)->push_back(new EntryMsgImpl(ci, source.GetNick(), message));
- Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to add a message";
- source.Reply(_("Entry message added to \002%s\002"), ci->name.c_str());
- }
- }
-
- void DoDel(CommandSource &source, ChannelInfo *ci, const Anope::string &message)
- {
- EntryMessageList *messages = ci->Require<EntryMessageList>("entrymsg");
-
- if (!message.is_pos_number_only())
- source.Reply(("Entry message \002%s\002 not found on channel \002%s\002."), message.c_str(), ci->name.c_str());
- else if ((*messages)->empty())
- source.Reply(_("Entry message list for \002%s\002 is empty."), ci->name.c_str());
- else
- {
- try
- {
- unsigned i = convertTo<unsigned>(message);
- if (i > 0 && i <= (*messages)->size())
- {
- delete (*messages)->at(i - 1);
- if ((*messages)->empty())
- ci->Shrink<EntryMessageList>("entrymsg");
- Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to remove a message";
- source.Reply(_("Entry message \002%i\002 for \002%s\002 deleted."), i, ci->name.c_str());
- }
- else
- throw ConvertException();
- }
- catch (const ConvertException &)
- {
- source.Reply(_("Entry message \002%s\002 not found on channel \002%s\002."), message.c_str(), ci->name.c_str());
- }
- }
- }
-
- void DoClear(CommandSource &source, ChannelInfo *ci)
- {
- ci->Shrink<EntryMessageList>("entrymsg");
-
- Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to remove all messages";
- source.Reply(_("Entry messages for \002%s\002 have been cleared."), ci->name.c_str());
- }
-
- public:
- CommandEntryMessage(Module *creator) : Command(creator, "chanserv/entrymsg", 2, 3)
- {
- this->SetDesc(_("Manage the channel's entry messages"));
- this->SetSyntax(_("\037channel\037 ADD \037message\037"));
- this->SetSyntax(_("\037channel\037 DEL \037num\037"));
- this->SetSyntax(_("\037channel\037 LIST"));
- this->SetSyntax(_("\037channel\037 CLEAR"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- ChannelInfo *ci = ChannelInfo::Find(params[0]);
- if (ci == NULL)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
- return;
- }
-
- if (Anope::ReadOnly && !params[1].equals_ci("LIST"))
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- if (!source.AccessFor(ci).HasPriv("SET") && !source.HasPriv("chanserv/administration"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- if (params[1].equals_ci("LIST"))
- this->DoList(source, ci);
- else if (params[1].equals_ci("CLEAR"))
- this->DoClear(source, ci);
- else if (params.size() < 3)
- this->OnSyntaxError(source, "");
- else if (params[1].equals_ci("ADD"))
- this->DoAdd(source, ci, params[2]);
- else if (params[1].equals_ci("DEL"))
- this->DoDel(source, ci, params[2]);
- else
- this->OnSyntaxError(source, "");
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Controls what messages will be sent to users when they join the channel."));
- source.Reply(" ");
- source.Reply(_("The \002ENTRYMSG ADD\002 command adds the given message to\n"
- "the list of messages shown to users when they join\n"
- "the channel."));
- source.Reply(" ");
- source.Reply(_("The \002ENTRYMSG DEL\002 command removes the specified message from\n"
- "the list of messages shown to users when they join\n"
- "the channel. You can remove a message by specifying its number\n"
- "which you can get by listing the messages as explained below."));
- source.Reply(" ");
- source.Reply(_("The \002ENTRYMSG LIST\002 command displays a listing of messages\n"
- "shown to users when they join the channel."));
- source.Reply(" ");
- source.Reply(_("The \002ENTRYMSG CLEAR\002 command clears all entries from\n"
- "the list of messages shown to users when they join\n"
- "the channel, effectively disabling entry messages."));
- source.Reply(" ");
- source.Reply(_("Adding, deleting, or clearing entry messages requires the\n"
- "SET permission."));
- return true;
- }
-};
-
-class CSEntryMessage : public Module
-{
- CommandEntryMessage commandentrymsg;
- ExtensibleItem<EntryMessageListImpl> eml;
- Serialize::Type entrymsg_type;
-
- public:
- CSEntryMessage(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandentrymsg(this),
- eml(this, "entrymsg"), entrymsg_type("EntryMsg", EntryMsgImpl::Unserialize)
- {
- }
-
- void OnJoinChannel(User *u, Channel *c) anope_override
- {
- if (u && c && c->ci && u->server->IsSynced())
- {
- EntryMessageList *messages = c->ci->GetExt<EntryMessageList>("entrymsg");
-
- if (messages != NULL)
- for (unsigned i = 0; i < (*messages)->size(); ++i)
- u->SendMessage(c->ci->WhoSends(), "[%s] %s", c->ci->name.c_str(), (*messages)->at(i)->message.c_str());
- }
- }
-};
-
-MODULE_INIT(CSEntryMessage)
diff --git a/modules/commands/cs_flags.cpp b/modules/commands/cs_flags.cpp
deleted file mode 100644
index 477cc2468..000000000
--- a/modules/commands/cs_flags.cpp
+++ /dev/null
@@ -1,504 +0,0 @@
-/* ChanServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-static std::map<Anope::string, char> defaultFlags;
-
-class FlagsChanAccess : public ChanAccess
-{
- public:
- std::set<char> flags;
-
- FlagsChanAccess(AccessProvider *p) : ChanAccess(p)
- {
- }
-
- bool HasPriv(const Anope::string &priv) const anope_override
- {
- std::map<Anope::string, char>::iterator it = defaultFlags.find(priv);
- if (it != defaultFlags.end() && this->flags.count(it->second) > 0)
- return true;
- return false;
- }
-
- Anope::string AccessSerialize() const
- {
- return Anope::string(this->flags.begin(), this->flags.end());
- }
-
- void AccessUnserialize(const Anope::string &data) anope_override
- {
- for (unsigned i = data.length(); i > 0; --i)
- this->flags.insert(data[i - 1]);
- }
-
- static Anope::string DetermineFlags(const ChanAccess *access)
- {
- if (access->provider->name == "access/flags")
- return access->AccessSerialize();
-
- std::set<char> buffer;
-
- for (std::map<Anope::string, char>::iterator it = defaultFlags.begin(), it_end = defaultFlags.end(); it != it_end; ++it)
- if (access->HasPriv(it->first))
- buffer.insert(it->second);
-
- if (buffer.empty())
- return "(none)";
- else
- return Anope::string(buffer.begin(), buffer.end());
- }
-};
-
-class FlagsAccessProvider : public AccessProvider
-{
- public:
- static FlagsAccessProvider *ap;
-
- FlagsAccessProvider(Module *o) : AccessProvider(o, "access/flags")
- {
- ap = this;
- }
-
- ChanAccess *Create() anope_override
- {
- return new FlagsChanAccess(this);
- }
-};
-FlagsAccessProvider* FlagsAccessProvider::ap;
-
-class CommandCSFlags : public Command
-{
- void DoModify(CommandSource &source, ChannelInfo *ci, Anope::string mask, const Anope::string &flags)
- {
- if (flags.empty())
- {
- this->OnSyntaxError(source, "");
- return;
- }
-
- AccessGroup u_access = source.AccessFor(ci);
- const ChanAccess *highest = u_access.Highest();
- const NickAlias *na = NULL;
-
- if (IRCD->IsChannelValid(mask))
- {
- if (Config->GetModule("chanserv")->Get<bool>("disallow_channel_access"))
- {
- source.Reply(_("Channels may not be on access lists."));
- return;
- }
-
- ChannelInfo *targ_ci = ChannelInfo::Find(mask);
- if (targ_ci == NULL)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, mask.c_str());
- return;
- }
- else if (ci == targ_ci)
- {
- source.Reply(_("You can't add a channel to its own access list."));
- return;
- }
-
- mask = targ_ci->name;
- }
- else
- {
- na = NickAlias::Find(mask);
- if (!na && Config->GetModule("chanserv")->Get<bool>("disallow_hostmask_access"))
- {
- source.Reply(_("Masks and unregistered users may not be on access lists."));
- return;
- }
- else if (mask.find_first_of("!*@") == Anope::string::npos && !na)
- {
- User *targ = User::Find(mask, true);
- if (targ != NULL)
- mask = "*!*@" + targ->GetDisplayedHost();
- else
- {
- source.Reply(NICK_X_NOT_REGISTERED, mask.c_str());
- return;
- }
- }
-
- if (na)
- mask = na->nick;
- }
-
- ChanAccess *current = NULL;
- unsigned current_idx;
- std::set<char> current_flags;
- bool override = false;
- for (current_idx = ci->GetAccessCount(); current_idx > 0; --current_idx)
- {
- ChanAccess *access = ci->GetAccess(current_idx - 1);
- if ((na && na->nc == access->GetAccount()) || mask.equals_ci(access->Mask()))
- {
- // Flags allows removing others that have the same access as you,
- // but no other access system does.
- if (highest && highest->provider != FlagsAccessProvider::ap && !u_access.founder)
- // operator<= on the non-me entry!
- if (*highest <= *access)
- {
- if (source.HasPriv("chanserv/access/modify"))
- override = true;
- else
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
- }
-
- current = access;
- Anope::string cur_flags = FlagsChanAccess::DetermineFlags(access);
- for (unsigned j = cur_flags.length(); j > 0; --j)
- current_flags.insert(cur_flags[j - 1]);
- break;
- }
- }
-
- unsigned access_max = Config->GetModule("chanserv")->Get<unsigned>("accessmax", "1024");
- if (access_max && ci->GetDeepAccessCount() >= access_max)
- {
- source.Reply(_("Sorry, you can only have %d access entries on a channel, including access entries from other channels."), access_max);
- return;
- }
-
- Privilege *p = NULL;
- bool add = true;
- for (size_t i = 0; i < flags.length(); ++i)
- {
- char f = flags[i];
- switch (f)
- {
- case '+':
- add = true;
- break;
- case '-':
- add = false;
- break;
- case '*':
- for (std::map<Anope::string, char>::iterator it = defaultFlags.begin(), it_end = defaultFlags.end(); it != it_end; ++it)
- {
- bool has = current_flags.count(it->second);
- // If we are adding a flag they already have or removing one they don't have, don't bother
- if (add == has)
- continue;
-
- if (!u_access.HasPriv(it->first) && !u_access.founder)
- {
- if (source.HasPriv("chanserv/access/modify"))
- override = true;
- else
- continue;
- }
-
- if (add)
- current_flags.insert(it->second);
- else
- current_flags.erase(it->second);
- }
- break;
- default:
- p = PrivilegeManager::FindPrivilege(flags.substr(i));
- if (p != NULL && defaultFlags[p->name])
- {
- f = defaultFlags[p->name];
- i = flags.length();
- }
-
- for (std::map<Anope::string, char>::iterator it = defaultFlags.begin(), it_end = defaultFlags.end(); it != it_end; ++it)
- {
- if (f != it->second)
- continue;
- else if (!u_access.HasPriv(it->first) && !u_access.founder)
- {
- if (source.HasPriv("chanserv/access/modify"))
- override = true;
- else
- {
- source.Reply(_("You cannot set the \002%c\002 flag."), f);
- break;
- }
- }
- if (add)
- current_flags.insert(f);
- else
- current_flags.erase(f);
- break;
- }
- }
- }
- if (current_flags.empty())
- {
- if (current != NULL)
- {
- ci->EraseAccess(current_idx - 1);
- FOREACH_MOD(OnAccessDel, (ci, source, current));
- delete current;
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to delete " << mask;
- source.Reply(_("\002%s\002 removed from the %s access list."), mask.c_str(), ci->name.c_str());
- }
- else
- {
- source.Reply(_("\002%s\002 not found on %s access list."), mask.c_str(), ci->name.c_str());
- }
- return;
- }
-
- ServiceReference<AccessProvider> provider("AccessProvider", "access/flags");
- if (!provider)
- return;
- FlagsChanAccess *access = anope_dynamic_static_cast<FlagsChanAccess *>(provider->Create());
- access->SetMask(mask, ci);
- access->creator = source.GetNick();
- access->last_seen = current ? current->last_seen : 0;
- access->created = Anope::CurTime;
- access->flags = current_flags;
-
- if (current != NULL)
- delete current;
-
- ci->AddAccess(access);
-
- FOREACH_MOD(OnAccessAdd, (ci, source, access));
-
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to modify " << mask << "'s flags to " << access->AccessSerialize();
- if (p != NULL)
- {
- if (add)
- source.Reply(_("Privilege \002%s\002 added to \002%s\002 on \002%s\002, new flags are +\002%s\002"), p->name.c_str(), access->Mask().c_str(), ci->name.c_str(), access->AccessSerialize().c_str());
- else
- source.Reply(_("Privilege \002%s\002 removed from \002%s\002 on \002%s\002, new flags are +\002%s\002"), p->name.c_str(), access->Mask().c_str(), ci->name.c_str(), access->AccessSerialize().c_str());
- }
- else
- source.Reply(_("Flags for \002%s\002 on %s set to +\002%s\002"), access->Mask().c_str(), ci->name.c_str(), access->AccessSerialize().c_str());
- }
-
- void DoList(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
- {
- const Anope::string &arg = params.size() > 2 ? params[2] : "";
-
- if (!ci->GetAccessCount())
- {
- source.Reply(_("%s access list is empty."), ci->name.c_str());
- return;
- }
-
- ListFormatter list(source.GetAccount());
-
- list.AddColumn(_("Number")).AddColumn(_("Mask")).AddColumn(_("Flags")).AddColumn(_("Creator")).AddColumn(_("Created"));
-
- unsigned count = 0;
- for (unsigned i = 0, end = ci->GetAccessCount(); i < end; ++i)
- {
- const ChanAccess *access = ci->GetAccess(i);
- const Anope::string &flags = FlagsChanAccess::DetermineFlags(access);
-
- if (!arg.empty())
- {
- if (arg[0] == '+')
- {
- bool pass = true;
- for (size_t j = 1; j < arg.length(); ++j)
- if (flags.find(arg[j]) == Anope::string::npos)
- pass = false;
- if (pass == false)
- continue;
- }
- else if (!Anope::Match(access->Mask(), arg))
- continue;
- }
-
- ListFormatter::ListEntry entry;
- ++count;
- entry["Number"] = stringify(i + 1);
- entry["Mask"] = access->Mask();
- entry["Flags"] = flags;
- entry["Creator"] = access->creator;
- entry["Created"] = Anope::strftime(access->created, source.nc, true);
- list.AddEntry(entry);
- }
-
- if (list.IsEmpty())
- source.Reply(_("No matching entries on %s access list."), ci->name.c_str());
- else
- {
- std::vector<Anope::string> replies;
- list.Process(replies);
-
- source.Reply(_("Flags list for %s"), ci->name.c_str());
- for (unsigned i = 0; i < replies.size(); ++i)
- source.Reply(replies[i]);
- if (count == ci->GetAccessCount())
- source.Reply(_("End of access list."));
- else
- source.Reply(_("End of access list - %d/%d entries shown."), count, ci->GetAccessCount());
- }
- }
-
- void DoClear(CommandSource &source, ChannelInfo *ci)
- {
- if (!source.IsFounder(ci) && !source.HasPriv("chanserv/access/modify"))
- source.Reply(ACCESS_DENIED);
- else
- {
- ci->ClearAccess();
-
- FOREACH_MOD(OnAccessClear, (ci, source));
-
- source.Reply(_("Channel %s access list has been cleared."), ci->name.c_str());
-
- bool override = !source.IsFounder(ci);
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to clear the access list";
- }
-
- return;
- }
-
- public:
- CommandCSFlags(Module *creator) : Command(creator, "chanserv/flags", 1, 4)
- {
- this->SetDesc(_("Modify the list of privileged users"));
- this->SetSyntax(_("\037channel\037 [MODIFY] \037mask\037 \037changes\037"));
- this->SetSyntax(_("\037channel\037 LIST [\037mask\037 | +\037flags\037]"));
- this->SetSyntax(_("\037channel\037 CLEAR"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &chan = params[0];
- const Anope::string &cmd = params.size() > 1 ? params[1] : "";
-
- ChannelInfo *ci = ChannelInfo::Find(chan);
- if (ci == NULL)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, chan.c_str());
- return;
- }
-
- bool is_list = cmd.empty() || cmd.equals_ci("LIST");
- bool has_access = false;
- if (source.HasPriv("chanserv/access/modify"))
- has_access = true;
- else if (is_list && source.HasPriv("chanserv/access/list"))
- has_access = true;
- else if (is_list && source.AccessFor(ci).HasPriv("ACCESS_LIST"))
- has_access = true;
- else if (source.AccessFor(ci).HasPriv("ACCESS_CHANGE"))
- has_access = true;
-
- if (!has_access)
- source.Reply(ACCESS_DENIED);
- else if (Anope::ReadOnly && !is_list)
- source.Reply(_("Sorry, channel access list modification is temporarily disabled."));
- else if (is_list)
- this->DoList(source, ci, params);
- else if (cmd.equals_ci("CLEAR"))
- this->DoClear(source, ci);
- else
- {
- Anope::string mask, flags;
- if (cmd.equals_ci("MODIFY"))
- {
- mask = params.size() > 2 ? params[2] : "";
- flags = params.size() > 3 ? params[3] : "";
- }
- else
- {
- mask = cmd;
- flags = params.size() > 2 ? params[2] : "";
- }
-
- this->DoModify(source, ci, mask, flags);
- }
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("%s is another way to modify the channel access list, similar to\n"
- "the XOP and ACCESS methods."), source.command.c_str());
- source.Reply(" ");
- source.Reply(_("The \002MODIFY\002 command allows you to modify the access list. If the mask is\n"
- "not already on the access list it is added, then the changes are applied.\n"
- "If the mask has no more flags, then the mask is removed from the access list.\n"
- "Additionally, you may use +* or -* to add or remove all flags, respectively. You are\n"
- "only able to modify the access list if you have the proper permission on the channel,\n"
- "and even then you can only give other people access to the equivalent of what your access is."));
- source.Reply(" ");
- source.Reply(_("The \002LIST\002 command allows you to list existing entries on the channel access list.\n"
- "If a mask is given, the mask is wildcard matched against all existing entries on the\n"
- "access list, and only those entries are returned. If a set of flags is given, only those\n"
- "on the access list with the specified flags are returned."));
- source.Reply(" ");
- source.Reply(_("The \002CLEAR\002 command clears the channel access list. This requires channel founder access."));
- source.Reply(" ");
- source.Reply(_("The available flags are:"));
-
- typedef std::multimap<char, Anope::string, ci::less> reverse_map;
- reverse_map reverse;
- for (std::map<Anope::string, char>::iterator it = defaultFlags.begin(), it_end = defaultFlags.end(); it != it_end; ++it)
- reverse.insert(std::make_pair(it->second, it->first));
-
- for (reverse_map::iterator it = reverse.begin(), it_end = reverse.end(); it != it_end; ++it)
- {
- Privilege *p = PrivilegeManager::FindPrivilege(it->second);
- if (p == NULL)
- continue;
- source.Reply(" %c - %s", it->first, Language::Translate(source.nc, p->desc.c_str()));
- }
-
- return true;
- }
-};
-
-class CSFlags : public Module
-{
- FlagsAccessProvider accessprovider;
- CommandCSFlags commandcsflags;
-
- public:
- CSFlags(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- accessprovider(this), commandcsflags(this)
- {
- this->SetPermanent(true);
-
- }
-
- void OnReload(Configuration::Conf *conf) anope_override
- {
- defaultFlags.clear();
-
- for (int i = 0; i < conf->CountBlock("privilege"); ++i)
- {
- Configuration::Block *priv = conf->GetBlock("privilege", i);
-
- const Anope::string &pname = priv->Get<const Anope::string>("name");
-
- Privilege *p = PrivilegeManager::FindPrivilege(pname);
- if (p == NULL)
- continue;
-
- const Anope::string &value = priv->Get<const Anope::string>("flag");
- if (value.empty())
- continue;
-
- defaultFlags[p->name] = value[0];
- }
- }
-};
-
-MODULE_INIT(CSFlags)
diff --git a/modules/commands/cs_getkey.cpp b/modules/commands/cs_getkey.cpp
deleted file mode 100644
index b9d58393d..000000000
--- a/modules/commands/cs_getkey.cpp
+++ /dev/null
@@ -1,73 +0,0 @@
-/* ChanServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class CommandCSGetKey : public Command
-{
- public:
- CommandCSGetKey(Module *creator) : Command(creator, "chanserv/getkey", 1, 1)
- {
- this->SetDesc(_("Returns the key of the given channel"));
- this->SetSyntax(_("\037channel\037"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &chan = params[0];
-
- ChannelInfo *ci = ChannelInfo::Find(params[0]);
- if (ci == NULL)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
- return;
- }
-
- if (!source.AccessFor(ci).HasPriv("GETKEY") && !source.HasCommand("chanserv/getkey"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- Anope::string key;
- if (!ci->c || !ci->c->GetParam("KEY", key))
- {
- source.Reply(_("Channel \002%s\002 has no key."), chan.c_str());
- return;
- }
-
- bool override = !source.AccessFor(ci).HasPriv("GETKEY");
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci);
-
- source.Reply(_("Key for channel \002%s\002 is \002%s\002."), chan.c_str(), key.c_str());
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Returns the key of the given channel."));
- return true;
- }
-};
-
-class CSGetKey : public Module
-{
- CommandCSGetKey commandcsgetkey;
-
- public:
- CSGetKey(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), commandcsgetkey(this)
- {
-
- }
-};
-
-MODULE_INIT(CSGetKey)
diff --git a/modules/commands/cs_info.cpp b/modules/commands/cs_info.cpp
deleted file mode 100644
index 8ebcc8e40..000000000
--- a/modules/commands/cs_info.cpp
+++ /dev/null
@@ -1,98 +0,0 @@
-/* ChanServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class CommandCSInfo : public Command
-{
- public:
- CommandCSInfo(Module *creator) : Command(creator, "chanserv/info", 1, 2)
- {
- this->SetDesc(_("Lists information about the specified registered channel"));
- this->SetSyntax(_("\037channel\037"));
- this->AllowUnregistered(true);
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &chan = params[0];
-
- NickCore *nc = source.nc;
- ChannelInfo *ci = ChannelInfo::Find(params[0]);
- if (ci == NULL)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
- return;
- }
-
- bool has_auspex = source.HasPriv("chanserv/auspex");
- bool show_all = false;
-
- /* Should we show all fields? Only for sadmins and identified users */
- if (source.AccessFor(ci).HasPriv("INFO") || has_auspex)
- show_all = true;
-
- InfoFormatter info(nc);
-
- source.Reply(CHAN_INFO_HEADER, chan.c_str());
- if (ci->GetFounder())
- info[_("Founder")] = ci->GetFounder()->display;
-
- if (show_all && ci->GetSuccessor())
- info[_("Successor")] = ci->GetSuccessor()->display;
-
- if (!ci->desc.empty())
- info[_("Description")] = ci->desc;
-
- info[_("Registered")] = Anope::strftime(ci->time_registered, source.GetAccount());
- info[_("Last used")] = Anope::strftime(ci->last_used, source.GetAccount());
-
- if (show_all)
- {
- info[_("Ban type")] = stringify(ci->bantype);
- }
-
- FOREACH_MOD(OnChanInfo, (source, ci, info, show_all));
-
- std::vector<Anope::string> replies;
- info.Process(replies);
-
- for (unsigned i = 0; i < replies.size(); ++i)
- source.Reply(replies[i]);
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Lists information about the specified registered channel,\n"
- "including its founder, time of registration, last\n"
- "time used, and description. If the user issuing the\n"
- "command has the appropriate access for it, then the\n"
- "successor, last topic set, settings and expiration\n"
- "time will also be displayed when applicable."));
- return true;
- }
-};
-
-class CSInfo : public Module
-{
- CommandCSInfo commandcsinfo;
-
- public:
- CSInfo(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandcsinfo(this)
- {
-
- }
-};
-
-MODULE_INIT(CSInfo)
diff --git a/modules/commands/cs_invite.cpp b/modules/commands/cs_invite.cpp
deleted file mode 100644
index a95e25b30..000000000
--- a/modules/commands/cs_invite.cpp
+++ /dev/null
@@ -1,111 +0,0 @@
-/* ChanServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class CommandCSInvite : public Command
-{
- public:
- CommandCSInvite(Module *creator) : Command(creator, "chanserv/invite", 1, 3)
- {
- this->SetDesc(_("Invites you or an optionally specified nick into a channel"));
- this->SetSyntax(_("\037channel\037 [\037nick\037]"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &chan = params[0];
-
- User *u = source.GetUser();
- Channel *c = Channel::Find(chan);
-
- if (!c)
- {
- source.Reply(CHAN_X_NOT_IN_USE, chan.c_str());
- return;
- }
-
- ChannelInfo *ci = c->ci;
- if (!ci)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, chan.c_str());
- return;
- }
-
- if (!source.AccessFor(ci).HasPriv("INVITE") && !source.HasCommand("chanserv/invite"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- User *u2;
- if (params.size() == 1)
- u2 = u;
- else
- u2 = User::Find(params[1], true);
-
- if (!u2)
- {
- source.Reply(NICK_X_NOT_IN_USE, params.size() > 1 ? params[1].c_str() : source.GetNick().c_str());
- return;
- }
-
- if (c->FindUser(u2))
- {
- if (u2 == u)
- source.Reply(_("You are already in \002%s\002!"), c->name.c_str());
- else
- source.Reply(_("\002%s\002 is already in \002%s\002!"), u2->nick.c_str(), c->name.c_str());
- }
- else
- {
- bool override = !source.AccessFor(ci).HasPriv("INVITE");
-
- IRCD->SendInvite(ci->WhoSends(), c, u2);
- if (u2 != u)
- {
- source.Reply(_("\002%s\002 has been invited to \002%s\002."), u2->nick.c_str(), c->name.c_str());
- u2->SendMessage(ci->WhoSends(), _("You have been invited to \002%s\002 by \002%s\002."), c->name.c_str(), source.GetNick().c_str());
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "for " << u2->nick;
- }
- else
- {
- u2->SendMessage(ci->WhoSends(), _("You have been invited to \002%s\002."), c->name.c_str());
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci);
- }
- }
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Tells %s to invite you or an optionally specified\n"
- "nick into the given channel.\n"
- " \n"
- "By default, limited to AOPs or those with level 5 access and above\n"
- "on the channel."), source.service->nick.c_str());
- return true;
- }
-};
-
-class CSInvite : public Module
-{
- CommandCSInvite commandcsinvite;
-
- public:
- CSInvite(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), commandcsinvite(this)
- {
-
- }
-};
-
-MODULE_INIT(CSInvite)
diff --git a/modules/commands/cs_kick.cpp b/modules/commands/cs_kick.cpp
deleted file mode 100644
index d552efb23..000000000
--- a/modules/commands/cs_kick.cpp
+++ /dev/null
@@ -1,147 +0,0 @@
-/* ChanServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class CommandCSKick : public Command
-{
- public:
- CommandCSKick(Module *creator) : Command(creator, "chanserv/kick", 2, 3)
- {
- this->SetDesc(_("Kicks a specified nick from a channel"));
- this->SetSyntax(_("\037channel\037 \037nick\037 [\037reason\037]"));
- this->SetSyntax(_("\037channel\037 \037mask\037 [\037reason\037]"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &chan = params[0];
- const Anope::string &target = params[1];
- Anope::string reason = params.size() > 2 ? params[2] : "Requested";
-
- User *u = source.GetUser();
- ChannelInfo *ci = ChannelInfo::Find(params[0]);
- Channel *c = Channel::Find(params[0]);
- User *u2 = User::Find(target, true);
-
- if (!c)
- {
- source.Reply(CHAN_X_NOT_IN_USE, chan.c_str());
- return;
- }
- else if (!ci)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, chan.c_str());
- return;
- }
-
- unsigned reasonmax = Config->GetModule("chanserv")->Get<unsigned>("reasonmax", "200");
- if (reason.length() > reasonmax)
- reason = reason.substr(0, reasonmax);
-
- Anope::string signkickformat = Config->GetModule("chanserv")->Get<Anope::string>("signkickformat", "%m (%n)");
- signkickformat = signkickformat.replace_all_cs("%n", source.GetNick());
-
- AccessGroup u_access = source.AccessFor(ci);
-
- if (!u_access.HasPriv("KICK") && !source.HasPriv("chanserv/kick"))
- source.Reply(ACCESS_DENIED);
- else if (u2)
- {
- AccessGroup u2_access = ci->AccessFor(u2);
- if (u != u2 && ci->HasExt("PEACE") && u2_access >= u_access && !source.HasPriv("chanserv/kick"))
- source.Reply(ACCESS_DENIED);
- else if (u2->IsProtected())
- source.Reply(ACCESS_DENIED);
- else if (!c->FindUser(u2))
- source.Reply(NICK_X_NOT_ON_CHAN, u2->nick.c_str(), c->name.c_str());
- else
- {
- bool override = !u_access.HasPriv("KICK") || (u != u2 && ci->HasExt("PEACE") && u2_access >= u_access);
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "for " << u2->nick;
-
- if (ci->HasExt("SIGNKICK") || (ci->HasExt("SIGNKICK_LEVEL") && !u_access.HasPriv("SIGNKICK")))
- {
- signkickformat = signkickformat.replace_all_cs("%m", reason);
- c->Kick(ci->WhoSends(), u2, "%s", signkickformat.c_str());
- }
- else
- c->Kick(ci->WhoSends(), u2, "%s", reason.c_str());
- }
- }
- else if (u_access.HasPriv("FOUNDER"))
- {
- Anope::string mask = IRCD->NormalizeMask(target);
-
- Log(LOG_COMMAND, source, this, ci) << "for " << mask;
-
- int matched = 0, kicked = 0;
- for (Channel::ChanUserList::iterator it = c->users.begin(), it_end = c->users.end(); it != it_end;)
- {
- ChanUserContainer *uc = it->second;
- ++it;
-
- Entry e("", mask);
- if (e.Matches(uc->user))
- {
- ++matched;
-
- AccessGroup u2_access = ci->AccessFor(uc->user);
- if (u != uc->user && ci->HasExt("PEACE") && u2_access >= u_access)
- continue;
- else if (uc->user->IsProtected())
- continue;
-
- ++kicked;
- if (ci->HasExt("SIGNKICK") || (ci->HasExt("SIGNKICK_LEVEL") && !u_access.HasPriv("SIGNKICK")))
- {
- reason += " (Matches " + mask + ")";
- signkickformat = signkickformat.replace_all_cs("%m", reason);
- c->Kick(ci->WhoSends(), uc->user, "%s", signkickformat.c_str());
- }
- else
- c->Kick(ci->WhoSends(), uc->user, "%s (Matches %s)", reason.c_str(), mask.c_str());
- }
- }
-
- if (matched)
- source.Reply(_("Kicked %d/%d users matching %s from %s."), kicked, matched, mask.c_str(), c->name.c_str());
- else
- source.Reply(_("No users on %s match %s."), c->name.c_str(), mask.c_str());
- }
- else
- source.Reply(NICK_X_NOT_IN_USE, target.c_str());
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Kicks a specified nick from a channel.\n"
- " \n"
- "By default, limited to AOPs or those with level 5 access\n"
- "and above on the channel. Channel founders can also specify masks."));
- return true;
- }
-};
-
-class CSKick : public Module
-{
- CommandCSKick commandcskick;
-
- public:
- CSKick(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), commandcskick(this)
- {
-
- }
-};
-
-MODULE_INIT(CSKick)
diff --git a/modules/commands/cs_list.cpp b/modules/commands/cs_list.cpp
deleted file mode 100644
index bb2fd854d..000000000
--- a/modules/commands/cs_list.cpp
+++ /dev/null
@@ -1,266 +0,0 @@
-/* ChanServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-#include "modules/cs_mode.h"
-
-class CommandCSList : public Command
-{
- public:
- CommandCSList(Module *creator) : Command(creator, "chanserv/list", 1, 2)
- {
- this->SetDesc(_("Lists all registered channels matching the given pattern"));
- this->SetSyntax(_("\037pattern\037 [SUSPENDED] [NOEXPIRE]"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- Anope::string pattern = params[0];
- unsigned nchans;
- bool is_servadmin = source.HasCommand("chanserv/list");
- int count = 0, from = 0, to = 0;
- bool suspended = false, channoexpire = false;
-
- if (pattern[0] == '#')
- {
- Anope::string n1, n2;
- sepstream(pattern.substr(1), '-').GetToken(n1, 0);
- sepstream(pattern, '-').GetToken(n2, 1);
-
- try
- {
- from = convertTo<int>(n1);
- to = convertTo<int>(n2);
- }
- catch (const ConvertException &)
- {
- source.Reply(LIST_INCORRECT_RANGE);
- source.Reply(_("To search for channels starting with #, search for the channel\n"
- "name without the #-sign prepended (\002anope\002 instead of \002#anope\002)."));
- return;
- }
-
- pattern = "*";
- }
-
- nchans = 0;
-
- if (is_servadmin && params.size() > 1)
- {
- Anope::string keyword;
- spacesepstream keywords(params[1]);
- while (keywords.GetToken(keyword))
- {
- if (keyword.equals_ci("SUSPENDED"))
- suspended = true;
- if (keyword.equals_ci("NOEXPIRE"))
- channoexpire = true;
- }
- }
-
- Anope::string spattern = "#" + pattern;
- unsigned listmax = Config->GetModule(this->owner)->Get<unsigned>("listmax", "50");
-
- source.Reply(_("List of entries matching \002%s\002:"), pattern.c_str());
-
- ListFormatter list(source.GetAccount());
- list.AddColumn(_("Name")).AddColumn(_("Description"));
-
- Anope::map<ChannelInfo *> ordered_map;
- for (registered_channel_map::const_iterator it = RegisteredChannelList->begin(), it_end = RegisteredChannelList->end(); it != it_end; ++it)
- ordered_map[it->first] = it->second;
-
- for (Anope::map<ChannelInfo *>::const_iterator it = ordered_map.begin(), it_end = ordered_map.end(); it != it_end; ++it)
- {
- const ChannelInfo *ci = it->second;
-
- if (!is_servadmin)
- {
- if (ci->HasExt("CS_PRIVATE") || ci->HasExt("CS_SUSPENDED"))
- continue;
- if (ci->c && ci->c->HasMode("SECRET"))
- continue;
-
- ModeLocks *ml = ci->GetExt<ModeLocks>("modelocks");
- const ModeLock *secret = ml ? ml->GetMLock("SECRET") : NULL;
- if (secret && secret->set)
- continue;
- }
-
- if (suspended && !ci->HasExt("CS_SUSPENDED"))
- continue;
-
- if (channoexpire && !ci->HasExt("CS_NO_EXPIRE"))
- continue;
-
- if (pattern.equals_ci(ci->name) || ci->name.equals_ci(spattern) || Anope::Match(ci->name, pattern, false, true) || Anope::Match(ci->name, spattern, false, true) || Anope::Match(ci->desc, pattern, false, true) || Anope::Match(ci->last_topic, pattern, false, true))
- {
- if (((count + 1 >= from && count + 1 <= to) || (!from && !to)) && ++nchans <= listmax)
- {
- bool isnoexpire = false;
- if (is_servadmin && (ci->HasExt("CS_NO_EXPIRE")))
- isnoexpire = true;
-
- ListFormatter::ListEntry entry;
- entry["Name"] = (isnoexpire ? "!" : "") + ci->name;
- if (ci->HasExt("CS_SUSPENDED"))
- entry["Description"] = Language::Translate(source.GetAccount(), _("[Suspended]"));
- else
- entry["Description"] = ci->desc;
- list.AddEntry(entry);
- }
- ++count;
- }
- }
-
- std::vector<Anope::string> replies;
- list.Process(replies);
-
- for (unsigned i = 0; i < replies.size(); ++i)
- source.Reply(replies[i]);
-
- source.Reply(_("End of list - %d/%d matches shown."), nchans > listmax ? listmax : nchans, nchans);
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Lists all registered channels matching the given pattern.\n"
- "Channels with the \002PRIVATE\002 option set will only be\n"
- "displayed to Services Operators with the proper access.\n"
- "Channels with the \002NOEXPIRE\002 option set will have\n"
- "a \002!\002 prefixed to the channel for Services Operators to see.\n"
- " \n"
- "Note that a preceding '#' specifies a range, channel names\n"
- "are to be written without '#'.\n"
- " \n"
- "If the SUSPENDED or NOEXPIRE options are given, only channels\n"
- "which, respectively, are SUSPENDED or have the NOEXPIRE\n"
- "flag set will be displayed. If multiple options are given,\n"
- "all channels matching at least one option will be displayed.\n"
- "Note that these options are limited to \037Services Operators\037.\n"
- " \n"
- "Examples:\n"
- " \n"
- " \002LIST *anope*\002\n"
- " Lists all registered channels with \002anope\002 in their\n"
- " names (case insensitive).\n"
- " \n"
- " \002LIST * NOEXPIRE\002\n"
- " Lists all registered channels which have been set to not expire.\n"
- " \n"
- " \002LIST #51-100\002\n"
- " Lists all registered channels within the given range (51-100)."));
-
- if (!Config->GetBlock("options")->Get<const Anope::string>("regexengine").empty())
- {
- source.Reply(" ");
- source.Reply(_("Regex matches are also supported using the %s engine.\n"
- "Enclose your pattern in // if this is desired."), Config->GetBlock("options")->Get<const Anope::string>("regexengine").c_str());
- }
-
- return true;
- }
-};
-
-class CommandCSSetPrivate : public Command
-{
- public:
- CommandCSSetPrivate(Module *creator, const Anope::string &cname = "chanserv/set/private") : Command(creator, cname, 2, 2)
- {
- this->SetDesc(_("Hide channel from the LIST command"));
- this->SetSyntax(_("\037channel\037 {ON | OFF}"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- ChannelInfo *ci = ChannelInfo::Find(params[0]);
- if (ci == NULL)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
- return;
- }
-
- EventReturn MOD_RESULT;
- FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, params[1]));
- if (MOD_RESULT == EVENT_STOP)
- return;
-
- if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- if (params[1].equals_ci("ON"))
- {
- Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable private";
- ci->Extend<bool>("CS_PRIVATE");
- source.Reply(_("Private option for %s is now \002on\002."), ci->name.c_str());
- }
- else if (params[1].equals_ci("OFF"))
- {
- Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable private";
- ci->Shrink<bool>("CS_PRIVATE");
- source.Reply(_("Private option for %s is now \002off\002."), ci->name.c_str());
- }
- else
- this->OnSyntaxError(source, "PRIVATE");
-
- return;
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Enables or disables the \002private\002 option for a channel."));
-
- BotInfo *bi;
- Anope::string cmd;
- if (Command::FindCommandFromService("chanserv/list", bi, cmd))
- source.Reply(_("When \002private\002 is set, the channel will not appear in\n"
- "%s's %s command."), bi->nick.c_str(), cmd.c_str());
- return true;
- }
-};
-
-class CSList : public Module
-{
- CommandCSList commandcslist;
- CommandCSSetPrivate commandcssetprivate;
-
- SerializableExtensibleItem<bool> priv;
-
- public:
- CSList(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandcslist(this), commandcssetprivate(this), priv(this, "CS_PRIVATE")
- {
- }
-
- void OnChanInfo(CommandSource &source, ChannelInfo *ci, InfoFormatter &info, bool show_all) anope_override
- {
- if (!show_all)
- return;
-
- if (priv.HasExt(ci))
- info.AddOption(_("Private"));
- }
-};
-
-MODULE_INIT(CSList)
diff --git a/modules/commands/cs_log.cpp b/modules/commands/cs_log.cpp
deleted file mode 100644
index 8fd996f27..000000000
--- a/modules/commands/cs_log.cpp
+++ /dev/null
@@ -1,399 +0,0 @@
-/* ChanServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-#include "modules/cs_log.h"
-
-struct LogSettingImpl : LogSetting, Serializable
-{
- LogSettingImpl() : Serializable("LogSetting")
- {
- }
-
- ~LogSettingImpl()
- {
- ChannelInfo *ci = ChannelInfo::Find(chan);
- if (ci)
- {
- LogSettings *ls = ci->GetExt<LogSettings>("logsettings");
- if (ls)
- {
- LogSettings::iterator it = std::find((*ls)->begin(), (*ls)->end(), this);
- if (it != (*ls)->end())
- (*ls)->erase(it);
- }
- }
- }
-
- void Serialize(Serialize::Data &data) const anope_override
- {
- data["ci"] << chan;
- data["service_name"] << service_name;
- data["command_service"] << command_service;
- data["command_name"] << command_name;
- data["method"] << method;
- data["extra"] << extra;
- data["creator"] << creator;
- data.SetType("created", Serialize::Data::DT_INT); data["created"] << created;
- }
-
- static Serializable* Unserialize(Serializable *obj, Serialize::Data &data)
- {
- Anope::string sci;
- data["ci"] >> sci;
-
- ChannelInfo *ci = ChannelInfo::Find(sci);
- if (ci == NULL)
- return NULL;
-
- LogSettingImpl *ls;
- if (obj)
- ls = anope_dynamic_static_cast<LogSettingImpl *>(obj);
- else
- {
- LogSettings *lsettings = ci->Require<LogSettings>("logsettings");
- ls = new LogSettingImpl();
- (*lsettings)->push_back(ls);
- }
-
- ls->chan = ci->name;
- data["service_name"] >> ls->service_name;
- data["command_service"] >> ls->command_service;
- data["command_name"] >> ls->command_name;
- data["method"] >> ls->method;
- data["extra"] >> ls->extra;
- data["creator"] >> ls->creator;
- data["created"] >> ls->created;
-
- return ls;
- }
-};
-
-struct LogSettingsImpl : LogSettings
-{
- LogSettingsImpl(Extensible *) { }
-
- ~LogSettingsImpl()
- {
- for (iterator it = (*this)->begin(); it != (*this)->end();)
- {
- LogSetting *ls = *it;
- ++it;
- delete ls;
- }
- }
-
- LogSetting *Create() anope_override
- {
- return new LogSettingImpl();
- }
-};
-
-class CommandCSLog : public Command
-{
-public:
- CommandCSLog(Module *creator) : Command(creator, "chanserv/log", 1, 4)
- {
- this->SetDesc(_("Configures channel logging settings"));
- this->SetSyntax(_("\037channel\037"));
- this->SetSyntax(_("\037channel\037 \037command\037 \037method\037 [\037status\037]"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &channel = params[0];
-
- ChannelInfo *ci = ChannelInfo::Find(channel);
- if (ci == NULL)
- source.Reply(CHAN_X_NOT_REGISTERED, channel.c_str());
- else if (!source.AccessFor(ci).HasPriv("SET") && !source.HasPriv("chanserv/administration"))
- source.Reply(ACCESS_DENIED);
- else if (params.size() == 1)
- {
- LogSettings *ls = ci->Require<LogSettings>("logsettings");
- if (!ls || (*ls)->empty())
- source.Reply(_("There currently are no logging configurations for %s."), ci->name.c_str());
- else
- {
- ListFormatter list(source.GetAccount());
- list.AddColumn(_("Number")).AddColumn(_("Service")).AddColumn(_("Command")).AddColumn(_("Method")).AddColumn("");
-
- for (unsigned i = 0; i < (*ls)->size(); ++i)
- {
- const LogSetting *log = (*ls)->at(i);
-
- ListFormatter::ListEntry entry;
- entry["Number"] = stringify(i + 1);
- entry["Service"] = log->command_service;
- entry["Command"] = !log->command_name.empty() ? log->command_name : log->service_name;
- entry["Method"] = log->method;
- entry[""] = log->extra;
- list.AddEntry(entry);
- }
-
- source.Reply(_("Log list for %s:"), ci->name.c_str());
-
- std::vector<Anope::string> replies;
- list.Process(replies);
-
- for (unsigned i = 0; i < replies.size(); ++i)
- source.Reply(replies[i]);
- }
- }
- else if (params.size() > 2)
- {
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- LogSettings *ls = ci->Require<LogSettings>("logsettings");
- const Anope::string &command = params[1];
- const Anope::string &method = params[2];
- const Anope::string &extra = params.size() > 3 ? params[3] : "";
-
- size_t sl = command.find('/');
- if (sl == Anope::string::npos)
- {
- source.Reply(_("%s is not a valid command."), command.c_str());
- return;
- }
-
- Anope::string service = command.substr(0, sl),
- command_name = command.substr(sl + 1);
- BotInfo *bi = BotInfo::Find(service, true);
-
- Anope::string service_name;
-
- /* Allow either a command name or a service name. */
- if (bi && bi->commands.count(command_name))
- {
- /* Get service name from command */
- service_name = bi->commands[command_name].name;
- }
- else if (ServiceReference<Command>("Command", command.lower()))
- {
- /* This is the service name, don't use any specific command */
- service_name = command;
- bi = NULL;
- command_name.clear();
- }
- else
- {
- source.Reply(_("%s is not a valid command."), command.c_str());
- return;
- }
-
- if (!method.equals_ci("MESSAGE") && !method.equals_ci("NOTICE") && !method.equals_ci("MEMO"))
- {
- source.Reply(_("%s is not a valid logging method."), method.c_str());
- return;
- }
-
- for (unsigned i = 0; i < extra.length(); ++i)
- if (ModeManager::GetStatusChar(extra[i]) == 0)
- {
- source.Reply(_("%c is an unknown status mode."), extra[i]);
- return;
- }
-
- bool override = !source.AccessFor(ci).HasPriv("SET");
-
- for (unsigned i = (*ls)->size(); i > 0; --i)
- {
- LogSetting *log = (*ls)->at(i - 1);
-
- if (log->service_name == service_name && log->method.equals_ci(method) && command_name.equals_ci(log->command_name))
- {
- if (log->extra == extra)
- {
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to remove logging for " << command << " with method " << method << (extra == "" ? "" : " ") << extra;
- source.Reply(_("Logging for command %s on %s with log method %s%s%s has been removed."), !log->command_name.empty() ? log->command_name.c_str() : log->service_name.c_str(), !log->command_service.empty() ? log->command_service.c_str() : "any service", method.c_str(), extra.empty() ? "" : " ", extra.empty() ? "" : extra.c_str());
- delete log;
- }
- else
- {
- log->extra = extra;
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to change logging for " << command << " to method " << method << (extra == "" ? "" : " ") << extra;
- source.Reply(_("Logging changed for command %s on %s, now using log method %s%s%s."), !log->command_name.empty() ? log->command_name.c_str() : log->service_name.c_str(), !log->command_service.empty() ? log->command_service.c_str() : "any service", method.c_str(), extra.empty() ? "" : " ", extra.empty() ? "" : extra.c_str());
- }
- return;
- }
- }
-
- LogSetting *log = new LogSettingImpl();
- log->chan = ci->name;
- log->service_name = service_name;
- if (bi)
- log->command_service = bi->nick;
- log->command_name = command_name;
- log->method = method;
- log->extra = extra;
- log->created = Anope::CurTime;
- log->creator = source.GetNick();
-
- (*ls)->push_back(log);
-
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to log " << command << " with method " << method << (extra == "" ? "" : " ") << extra;
-
- source.Reply(_("Logging is now active for command %s on %s, using log method %s%s%s."), !command_name.empty() ? command_name.c_str() : service_name.c_str(), bi ? bi->nick.c_str() : "any service", method.c_str(), extra.empty() ? "" : " ", extra.empty() ? "" : extra.c_str());
- }
- else
- this->OnSyntaxError(source, "");
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("The %s command allows users to configure logging settings\n"
- "for their channel. If no parameters are given this command\n"
- "lists the current logging methods in place for this channel.\n"
- " \n"
- "Otherwise, \037command\037 must be a command name, and \037method\037\n"
- "is one of the following logging methods:\n"
- " \n"
- " MESSAGE [status], NOTICE [status], MEMO\n"
- " \n"
- "Which are used to message, notice, and memo the channel respectively.\n"
- "With MESSAGE or NOTICE you must have a service bot assigned to and joined\n"
- "to your channel. Status may be a channel status such as @ or +.\n"
- " \n"
- "To remove a logging method use the same syntax as you would to add it.\n"
- " \n"
- "Example:\n"
- " %s #anope chanserv/access MESSAGE @\n"
- " Would message any channel operators whenever someone used the\n"
- " ACCESS command on ChanServ on the channel."),
- source.command.upper().c_str(), source.command.upper().c_str());
- return true;
- }
-};
-
-class CSLog : public Module
-{
- ServiceReference<MemoServService> MSService;
- CommandCSLog commandcslog;
- ExtensibleItem<LogSettingsImpl> logsettings;
- Serialize::Type logsetting_type;
-
- struct LogDefault
- {
- Anope::string service, command, method;
- };
-
- std::vector<LogDefault> defaults;
-
- public:
- CSLog(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- MSService("MemoServService", "MemoServ"), commandcslog(this),
- logsettings(this, "logsettings"), logsetting_type("LogSetting", LogSettingImpl::Unserialize)
- {
-
- }
-
- void OnReload(Configuration::Conf *conf) anope_override
- {
- Configuration::Block *block = conf->GetModule(this);
- defaults.clear();
-
- for (int i = 0; i < block->CountBlock("default"); ++i)
- {
- Configuration::Block *def = block->GetBlock("default", i);
-
- LogDefault ld;
-
- ld.service = def->Get<const Anope::string>("service");
- ld.command = def->Get<const Anope::string>("command");
- ld.method = def->Get<const Anope::string>("method");
-
- defaults.push_back(ld);
- }
- }
-
- void OnChanRegistered(ChannelInfo *ci) anope_override
- {
- if (defaults.empty())
- return;
-
- LogSettings *ls = logsettings.Require(ci);
- for (unsigned i = 0; i < defaults.size(); ++i)
- {
- LogDefault &d = defaults[i];
-
- LogSetting *log = new LogSettingImpl();
- log->chan = ci->name;
-
- if (!d.service.empty())
- {
- log->service_name = d.service.lower() + "/" + d.command.lower();
- log->command_service = d.service;
- log->command_name = d.command;
- }
- else
- log->service_name = d.command;
-
- spacesepstream sep(d.method);
- sep.GetToken(log->method);
- log->extra = sep.GetRemaining();
-
- log->created = Anope::CurTime;
- log->creator = ci->GetFounder() ? ci->GetFounder()->display : "(default)";
-
- (*ls)->push_back(log);
- }
- }
-
- void OnLog(Log *l) anope_override
- {
- if (l->type != LOG_COMMAND || l->u == NULL || l->c == NULL || l->ci == NULL || !Me || !Me->IsSynced())
- return;
-
- LogSettings *ls = logsettings.Get(l->ci);
- if (ls)
- for (unsigned i = 0; i < (*ls)->size(); ++i)
- {
- const LogSetting *log = (*ls)->at(i);
-
- /* wrong command */
- if (log->service_name != l->c->name)
- continue;
-
- /* if a command name is given check the service and the command */
- if (!log->command_name.empty())
- {
- /* wrong service (only check if not a fantasy command, though) */
- if (!l->source->c && log->command_service != l->source->service->nick)
- continue;
-
- if (!log->command_name.equals_ci(l->source->command))
- continue;
- }
-
- Anope::string buffer = l->u->nick + " used " + l->source->command.upper() + " " + l->buf.str();
-
- if (log->method.equals_ci("MEMO") && MSService && l->ci->WhoSends() != NULL)
- MSService->Send(l->ci->WhoSends()->nick, l->ci->name, buffer, true);
- else if (l->source->c)
- /* Sending a channel message or notice in response to a fantasy command */;
- else if (log->method.equals_ci("MESSAGE") && l->ci->c)
- {
- IRCD->SendPrivmsg(l->ci->WhoSends(), log->extra + l->ci->c->name, "%s", buffer.c_str());
- l->ci->WhoSends()->lastmsg = Anope::CurTime;
- }
- else if (log->method.equals_ci("NOTICE") && l->ci->c)
- IRCD->SendNotice(l->ci->WhoSends(), log->extra + l->ci->c->name, "%s", buffer.c_str());
- }
- }
-};
-
-MODULE_INIT(CSLog)
diff --git a/modules/commands/cs_mode.cpp b/modules/commands/cs_mode.cpp
deleted file mode 100644
index 2acef50f7..000000000
--- a/modules/commands/cs_mode.cpp
+++ /dev/null
@@ -1,1012 +0,0 @@
-/* ChanServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-#include "modules/cs_mode.h"
-
-struct ModeLockImpl : ModeLock, Serializable
-{
- ModeLockImpl() : Serializable("ModeLock")
- {
- }
-
- ~ModeLockImpl()
- {
- ChannelInfo *chan = ChannelInfo::Find(ci);
- if (chan)
- {
- ModeLocks *ml = chan->GetExt<ModeLocks>("modelocks");
- if (ml)
- ml->RemoveMLock(this);
- }
- }
-
- void Serialize(Serialize::Data &data) const anope_override;
- static Serializable* Unserialize(Serializable *obj, Serialize::Data &data);
-};
-
-struct ModeLocksImpl : ModeLocks
-{
- Serialize::Reference<ChannelInfo> ci;
- Serialize::Checker<ModeList> mlocks;
-
- ModeLocksImpl(Extensible *obj) : ci(anope_dynamic_static_cast<ChannelInfo *>(obj)), mlocks("ModeLock")
- {
- }
-
- ~ModeLocksImpl()
- {
- ModeList modelist;
- mlocks->swap(modelist);
- for (ModeList::iterator it = modelist.begin(); it != modelist.end(); ++it)
- {
- ModeLock *ml = *it;
- delete ml;
- }
- }
-
- bool HasMLock(ChannelMode *mode, const Anope::string &param, bool status) const anope_override
- {
- if (!mode)
- return false;
-
- for (ModeList::const_iterator it = this->mlocks->begin(); it != this->mlocks->end(); ++it)
- {
- const ModeLock *ml = *it;
-
- if (ml->name == mode->name && ml->set == status && ml->param == param)
- return true;
- }
-
- return false;
- }
-
- bool SetMLock(ChannelMode *mode, bool status, const Anope::string &param, Anope::string setter, time_t created = Anope::CurTime) anope_override
- {
- if (!mode)
- return false;
-
- RemoveMLock(mode, status, param);
-
- if (setter.empty())
- setter = ci->GetFounder() ? ci->GetFounder()->display : "Unknown";
-
- ModeLock *ml = new ModeLockImpl();
- ml->ci = ci->name;
- ml->set = status;
- ml->name = mode->name;
- ml->param = param;
- ml->setter = setter;
- ml->created = created;
-
- EventReturn MOD_RESULT;
- FOREACH_RESULT(OnMLock, MOD_RESULT, (this->ci, ml));
- if (MOD_RESULT == EVENT_STOP)
- {
- delete ml;
- return false;
- }
-
- this->mlocks->push_back(ml);
- return true;
- }
-
- bool RemoveMLock(ChannelMode *mode, bool status, const Anope::string &param = "") anope_override
- {
- if (!mode)
- return false;
-
- for (ModeList::iterator it = this->mlocks->begin(); it != this->mlocks->end(); ++it)
- {
- ModeLock *m = *it;
-
- if (m->name == mode->name)
- {
- // For list or status modes, we must check the parameter
- if (mode->type == MODE_LIST || mode->type == MODE_STATUS)
- if (m->param != param)
- continue;
-
- EventReturn MOD_RESULT;
- FOREACH_RESULT(OnUnMLock, MOD_RESULT, (this->ci, m));
- if (MOD_RESULT == EVENT_STOP)
- break;
-
- delete m;
- return true;
- }
- }
-
- return false;
- }
-
- void RemoveMLock(ModeLock *mlock) anope_override
- {
- ModeList::iterator it = std::find(this->mlocks->begin(), this->mlocks->end(), mlock);
- if (it != this->mlocks->end())
- this->mlocks->erase(it);
- }
-
- void ClearMLock() anope_override
- {
- ModeList ml;
- this->mlocks->swap(ml);
- for (unsigned i = 0; i < ml.size(); ++i)
- delete ml[i];
- }
-
- const ModeList &GetMLock() const anope_override
- {
- return this->mlocks;
- }
-
- std::list<ModeLock *> GetModeLockList(const Anope::string &name) anope_override
- {
- std::list<ModeLock *> mlist;
- for (ModeList::const_iterator it = this->mlocks->begin(); it != this->mlocks->end(); ++it)
- {
- ModeLock *m = *it;
- if (m->name == name)
- mlist.push_back(m);
- }
- return mlist;
- }
-
- const ModeLock *GetMLock(const Anope::string &mname, const Anope::string &param = "") anope_override
- {
- for (ModeList::const_iterator it = this->mlocks->begin(); it != this->mlocks->end(); ++it)
- {
- ModeLock *m = *it;
-
- if (m->name == mname && m->param == param)
- return m;
- }
-
- return NULL;
- }
-
- Anope::string GetMLockAsString(bool complete) const anope_override
- {
- Anope::string pos = "+", neg = "-", params;
-
- for (ModeList::const_iterator it = this->mlocks->begin(); it != this->mlocks->end(); ++it)
- {
- const ModeLock *ml = *it;
- ChannelMode *cm = ModeManager::FindChannelModeByName(ml->name);
-
- if (!cm || cm->type == MODE_LIST || cm->type == MODE_STATUS)
- continue;
-
- if (ml->set)
- pos += cm->mchar;
- else
- neg += cm->mchar;
-
- if (complete && ml->set && !ml->param.empty() && cm->type == MODE_PARAM)
- params += " " + ml->param;
- }
-
- if (pos.length() == 1)
- pos.clear();
- if (neg.length() == 1)
- neg.clear();
-
- return pos + neg + params;
- }
-
- void Check() anope_override
- {
- if (this->mlocks->empty())
- ci->Shrink<ModeLocks>("modelocks");
- }
-};
-
-void ModeLockImpl::Serialize(Serialize::Data &data) const
-{
- data["ci"] << this->ci;
- data["set"] << this->set;
- data["name"] << this->name;
- data["param"] << this->param;
- data["setter"] << this->setter;
- data.SetType("created", Serialize::Data::DT_INT); data["created"] << this->created;
-}
-
-Serializable* ModeLockImpl::Unserialize(Serializable *obj, Serialize::Data &data)
-{
- Anope::string sci;
-
- data["ci"] >> sci;
-
- ChannelInfo *ci = ChannelInfo::Find(sci);
- if (!ci)
- return NULL;
-
- ModeLockImpl *ml;
- if (obj)
- ml = anope_dynamic_static_cast<ModeLockImpl *>(obj);
- else
- {
- ml = new ModeLockImpl();
- ml->ci = ci->name;
- }
-
- data["set"] >> ml->set;
- data["created"] >> ml->created;
- data["setter"] >> ml->setter;
- data["name"] >> ml->name;
- data["param"] >> ml->param;
-
- if (!obj)
- ci->Require<ModeLocksImpl>("modelocks")->mlocks->push_back(ml);
-
- return ml;
-}
-
-class CommandCSMode : public Command
-{
- bool CanSet(CommandSource &source, ChannelInfo *ci, ChannelMode *cm, bool self)
- {
- if (!ci || !cm || cm->type != MODE_STATUS)
- return false;
-
- return source.AccessFor(ci).HasPriv(cm->name + (self ? "ME" : ""));
- }
-
- void DoLock(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
- {
- User *u = source.GetUser();
- const Anope::string &subcommand = params[2];
- const Anope::string &param = params.size() > 3 ? params[3] : "";
-
- bool override = !source.AccessFor(ci).HasPriv("MODE");
- ModeLocks *modelocks = ci->Require<ModeLocks>("modelocks");
-
- if (Anope::ReadOnly && !subcommand.equals_ci("LIST"))
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- if ((subcommand.equals_ci("ADD") || subcommand.equals_ci("SET")) && !param.empty())
- {
- /* If setting, remove the existing locks */
- if (subcommand.equals_ci("SET"))
- {
- const ModeLocks::ModeList mlocks = modelocks->GetMLock();
- for (ModeLocks::ModeList::const_iterator it = mlocks.begin(); it != mlocks.end(); ++it)
- {
- const ModeLock *ml = *it;
- ChannelMode *cm = ModeManager::FindChannelModeByName(ml->name);
- if (cm && cm->CanSet(source.GetUser()))
- modelocks->RemoveMLock(cm, ml->set, ml->param);
- }
- }
-
- spacesepstream sep(param);
- Anope::string modes;
-
- sep.GetToken(modes);
-
- Anope::string pos = "+", neg = "-", pos_params, neg_params;
-
- int adding = 1;
- bool needreply = true;
- for (size_t i = 0; i < modes.length(); ++i)
- {
- switch (modes[i])
- {
- case '+':
- adding = 1;
- break;
- case '-':
- adding = 0;
- break;
- default:
- needreply = false;
- ChannelMode *cm = ModeManager::FindChannelModeByChar(modes[i]);
- if (!cm)
- {
- source.Reply(_("Unknown mode character %c ignored."), modes[i]);
- break;
- }
- else if (u && !cm->CanSet(u))
- {
- source.Reply(_("You may not (un)lock mode %c."), modes[i]);
- break;
- }
-
- Anope::string mode_param;
- if (((cm->type == MODE_STATUS || cm->type == MODE_LIST) && !sep.GetToken(mode_param)) || (cm->type == MODE_PARAM && adding && !sep.GetToken(mode_param)))
- source.Reply(_("Missing parameter for mode %c."), cm->mchar);
- else if (cm->type == MODE_LIST && ci->c && IRCD->GetMaxListFor(ci->c) && ci->c->HasMode(cm->name) >= IRCD->GetMaxListFor(ci->c))
- source.Reply(_("List for mode %c is full."), cm->mchar);
- else if (modelocks->GetMLock().size() >= Config->GetModule(this->owner)->Get<unsigned>("max", "32"))
- source.Reply(_("The mode lock list of \002%s\002 is full."), ci->name.c_str());
- else
- {
- modelocks->SetMLock(cm, adding, mode_param, source.GetNick());
-
- if (adding)
- {
- pos += cm->mchar;
- if (!mode_param.empty())
- pos_params += " " + mode_param;
- }
- else
- {
- neg += cm->mchar;
- if (!mode_param.empty())
- neg_params += " " + mode_param;
- }
- }
- }
- }
-
- if (pos == "+")
- pos.clear();
- if (neg == "-")
- neg.clear();
- Anope::string reply = pos + neg + pos_params + neg_params;
-
- if (!reply.empty())
- {
- source.Reply(_("%s locked on %s."), reply.c_str(), ci->name.c_str());
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to lock " << reply;
- }
- else if (needreply)
- source.Reply(_("Nothing to do."));
-
- if (ci->c)
- ci->c->CheckModes();
- }
- else if (subcommand.equals_ci("DEL") && !param.empty())
- {
- spacesepstream sep(param);
- Anope::string modes;
-
- sep.GetToken(modes);
-
- int adding = 1;
- bool needreply = true;
- for (size_t i = 0; i < modes.length(); ++i)
- {
- switch (modes[i])
- {
- case '+':
- adding = 1;
- break;
- case '-':
- adding = 0;
- break;
- default:
- needreply = false;
- ChannelMode *cm = ModeManager::FindChannelModeByChar(modes[i]);
- if (!cm)
- {
- source.Reply(_("Unknown mode character %c ignored."), modes[i]);
- break;
- }
- else if (u && !cm->CanSet(u))
- {
- source.Reply(_("You may not (un)lock mode %c."), modes[i]);
- break;
- }
-
- Anope::string mode_param;
- if (cm->type != MODE_REGULAR && !sep.GetToken(mode_param))
- source.Reply(_("Missing parameter for mode %c."), cm->mchar);
- else
- {
- if (modelocks->RemoveMLock(cm, adding, mode_param))
- {
- if (!mode_param.empty())
- mode_param = " " + mode_param;
- source.Reply(_("%c%c%s has been unlocked from %s."), adding == 1 ? '+' : '-', cm->mchar, mode_param.c_str(), ci->name.c_str());
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to unlock " << (adding ? '+' : '-') << cm->mchar << mode_param;
- }
- else
- source.Reply(_("%c%c is not locked on %s."), adding == 1 ? '+' : '-', cm->mchar, ci->name.c_str());
- }
- }
- }
-
- if (needreply)
- source.Reply(_("Nothing to do."));
- }
- else if (subcommand.equals_ci("LIST"))
- {
- const ModeLocks::ModeList mlocks = modelocks->GetMLock();
- if (mlocks.empty())
- {
- source.Reply(_("Channel %s has no mode locks."), ci->name.c_str());
- }
- else
- {
- ListFormatter list(source.GetAccount());
- list.AddColumn(_("Mode")).AddColumn(_("Param")).AddColumn(_("Creator")).AddColumn(_("Created"));
-
- for (ModeLocks::ModeList::const_iterator it = mlocks.begin(), it_end = mlocks.end(); it != it_end; ++it)
- {
- const ModeLock *ml = *it;
- ChannelMode *cm = ModeManager::FindChannelModeByName(ml->name);
- if (!cm)
- continue;
-
- ListFormatter::ListEntry entry;
- entry["Mode"] = Anope::printf("%c%c", ml->set ? '+' : '-', cm->mchar);
- entry["Param"] = ml->param;
- entry["Creator"] = ml->setter;
- entry["Created"] = Anope::strftime(ml->created, NULL, true);
- list.AddEntry(entry);
- }
-
- source.Reply(_("Mode locks for %s:"), ci->name.c_str());
-
- std::vector<Anope::string> replies;
- list.Process(replies);
-
- for (unsigned i = 0; i < replies.size(); ++i)
- source.Reply(replies[i]);
- }
- }
- else
- this->OnSyntaxError(source, subcommand);
- }
-
- void DoSet(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
- {
- User *u = source.GetUser();
-
- bool has_access = source.AccessFor(ci).HasPriv("MODE") || source.HasPriv("chanserv/administration");
- bool can_override = source.HasPriv("chanserv/administration");
-
- spacesepstream sep(params.size() > 3 ? params[3] : "");
- Anope::string modes = params[2], param;
-
- bool override = !source.AccessFor(ci).HasPriv("MODE") && source.HasPriv("chanserv/administration");
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to set " << params[2] << (params.size() > 3 ? " " + params[3] : "");
-
- int adding = -1;
- for (size_t i = 0; i < modes.length(); ++i)
- {
- switch (modes[i])
- {
- case '+':
- adding = 1;
- break;
- case '-':
- adding = 0;
- break;
- case '*':
- if (adding == -1 || !has_access)
- break;
- for (unsigned j = 0; j < ModeManager::GetChannelModes().size() && ci->c; ++j)
- {
- ChannelMode *cm = ModeManager::GetChannelModes()[j];
-
- if (!u || cm->CanSet(u) || can_override)
- {
- if (cm->type == MODE_REGULAR || (!adding && cm->type == MODE_PARAM))
- {
- if (adding)
- ci->c->SetMode(NULL, cm);
- else
- ci->c->RemoveMode(NULL, cm);
- }
- }
- }
- break;
- default:
- if (adding == -1)
- break;
- ChannelMode *cm = ModeManager::FindChannelModeByChar(modes[i]);
- if (!cm || (u && !cm->CanSet(u) && !can_override))
- continue;
- switch (cm->type)
- {
- case MODE_REGULAR:
- if (!has_access)
- break;
- if (adding)
- ci->c->SetMode(NULL, cm);
- else
- ci->c->RemoveMode(NULL, cm);
- break;
- case MODE_PARAM:
- if (!has_access)
- break;
- if (adding && !sep.GetToken(param))
- break;
- if (adding)
- ci->c->SetMode(NULL, cm, param);
- else
- ci->c->RemoveMode(NULL, cm);
- break;
- case MODE_STATUS:
- {
- if (!sep.GetToken(param))
- param = source.GetNick();
-
- AccessGroup u_access = source.AccessFor(ci);
-
- if (param.find_first_of("*?") != Anope::string::npos)
- {
- if (!this->CanSet(source, ci, cm, false) && !can_override)
- {
- source.Reply(_("You do not have access to set mode %c."), cm->mchar);
- break;
- }
-
- for (Channel::ChanUserList::const_iterator it = ci->c->users.begin(), it_end = ci->c->users.end(); it != it_end;)
- {
- ChanUserContainer *uc = it->second;
- ++it;
-
- AccessGroup targ_access = ci->AccessFor(uc->user);
-
- if (uc->user->IsProtected() || (ci->HasExt("PEACE") && targ_access >= u_access && !can_override))
- {
- source.Reply(_("You do not have the access to change %s's modes."), uc->user->nick.c_str());
- continue;
- }
-
- if (Anope::Match(uc->user->GetMask(), param))
- {
- if (adding)
- ci->c->SetMode(NULL, cm, uc->user->GetUID());
- else
- ci->c->RemoveMode(NULL, cm, uc->user->GetUID());
- }
- }
- }
- else
- {
- User *target = User::Find(param, true);
- if (target == NULL)
- {
- source.Reply(NICK_X_NOT_IN_USE, param.c_str());
- break;
- }
-
- if (!this->CanSet(source, ci, cm, source.GetUser() == target) && !can_override)
- {
- source.Reply(_("You do not have access to set mode %c."), cm->mchar);
- break;
- }
-
- if (source.GetUser() != target)
- {
- AccessGroup targ_access = ci->AccessFor(target);
- if (ci->HasExt("PEACE") && targ_access >= u_access && !can_override)
- {
- source.Reply(_("You do not have the access to change %s's modes."), target->nick.c_str());
- break;
- }
- else if (target->IsProtected())
- {
- source.Reply(ACCESS_DENIED);
- break;
- }
- }
-
- if (adding)
- ci->c->SetMode(NULL, cm, target->GetUID());
- else
- ci->c->RemoveMode(NULL, cm, target->GetUID());
- }
- break;
- }
- case MODE_LIST:
- if (!has_access)
- break;
- if (!sep.GetToken(param))
- break;
- if (adding)
- {
- if (IRCD->GetMaxListFor(ci->c) && ci->c->HasMode(cm->name) < IRCD->GetMaxListFor(ci->c))
- ci->c->SetMode(NULL, cm, param);
- }
- else
- {
- std::vector<Anope::string> v = ci->c->GetModeList(cm->name);
- for (unsigned j = 0; j < v.size(); ++j)
- if (Anope::Match(v[j], param))
- ci->c->RemoveMode(NULL, cm, v[j]);
- }
- }
- }
- }
- }
-
- void DoClear(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
- {
- const Anope::string &param = params.size() > 2 ? params[2] : "";
-
- if (param.empty())
- {
- std::vector<Anope::string> new_params;
- new_params.push_back(params[0]);
- new_params.push_back("SET");
- new_params.push_back("-*");
- this->DoSet(source, ci, new_params);
- return;
- }
-
- ChannelMode *cm;
- if (param.length() == 1)
- cm = ModeManager::FindChannelModeByChar(param[0]);
- else
- {
- cm = ModeManager::FindChannelModeByName(param.upper());
- if (!cm)
- cm = ModeManager::FindChannelModeByName(param.substr(0, param.length() - 1).upper());
- }
-
- if (!cm)
- {
- source.Reply(_("There is no such mode %s."), param.c_str());
- return;
- }
-
- if (cm->type != MODE_STATUS && cm->type != MODE_LIST)
- {
- source.Reply(_("Mode %s is not a status or list mode."), param.c_str());
- return;
- }
-
- std::vector<Anope::string> new_params;
- new_params.push_back(params[0]);
- new_params.push_back("SET");
- new_params.push_back("-" + stringify(cm->mchar));
- new_params.push_back("*");
- this->DoSet(source, ci, new_params);
- }
-
- public:
- CommandCSMode(Module *creator) : Command(creator, "chanserv/mode", 2, 4)
- {
- this->SetDesc(_("Control modes and mode locks on a channel"));
- this->SetSyntax(_("\037channel\037 LOCK {ADD|DEL|SET|LIST} [\037what\037]"));
- this->SetSyntax(_("\037channel\037 SET \037modes\037"));
- this->SetSyntax(_("\037channel\037 CLEAR [\037what\037]"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &subcommand = params[1];
-
- ChannelInfo *ci = ChannelInfo::Find(params[0]);
-
- if (!ci)
- source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
- else if (subcommand.equals_ci("LOCK") && params.size() > 2)
- {
- if (!source.AccessFor(ci).HasPriv("MODE") && !source.HasPriv("chanserv/administration"))
- source.Reply(ACCESS_DENIED);
- else
- this->DoLock(source, ci, params);
- }
- else if (!ci->c)
- source.Reply(CHAN_X_NOT_IN_USE, params[0].c_str());
- else if (subcommand.equals_ci("SET") && params.size() > 2)
- this->DoSet(source, ci, params);
- else if (subcommand.equals_ci("CLEAR"))
- {
- if (!source.AccessFor(ci).HasPriv("MODE") && !source.HasPriv("chanserv/administration"))
- source.Reply(ACCESS_DENIED);
- else
- this->DoClear(source, ci, params);
- }
- else
- this->OnSyntaxError(source, "");
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Mainly controls mode locks and mode access (which is different from channel access)\n"
- "on a channel.\n"
- " \n"
- "The \002%s LOCK\002 command allows you to add, delete, and view mode locks on a channel.\n"
- "If a mode is locked on or off, services will not allow that mode to be changed. The \002SET\002\n"
- "command will clear all existing mode locks and set the new one given, while \002ADD\002 and \002DEL\002\n"
- "modify the existing mode lock.\n"
- "Example:\n"
- " \002MODE #channel LOCK ADD +bmnt *!*@*aol*\002\n"
- " \n"
- "The \002%s SET\002 command allows you to set modes through services. Wildcards * and ? may\n"
- "be given as parameters for list and status modes.\n"
- "Example:\n"
- " \002MODE #channel SET +v *\002\n"
- " Sets voice status to all users in the channel.\n"
- " \n"
- " \002MODE #channel SET -b ~c:*\n"
- " Clears all extended bans that start with ~c:\n"
- " \n"
- "The \002%s CLEAR\002 command is an easy way to clear modes on a channel. \037what\037 may be\n"
- "any mode name. Examples include bans, excepts, inviteoverrides, ops, halfops, and voices. If \037what\037\n"
- "is not given then all basic modes are removed."),
- source.command.upper().c_str(), source.command.upper().c_str(), source.command.upper().c_str());
- return true;
- }
-};
-
-static Anope::map<std::pair<bool, Anope::string> > modes;
-
-class CommandCSModes : public Command
-{
- public:
- CommandCSModes(Module *creator) : Command(creator, "chanserv/modes", 1, 2)
- {
- this->SetSyntax(_("\037channel\037 [\037user\037]"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- User *u = source.GetUser(),
- *targ = params.size() > 1 ? User::Find(params[1], true) : u;
- ChannelInfo *ci = ChannelInfo::Find(params[0]);
-
- if (!targ)
- {
- if (params.size() > 1)
- source.Reply(NICK_X_NOT_IN_USE, params[1].c_str());
- return;
- }
-
- if (!ci)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
- return;
- }
- else if (!ci->c)
- {
- source.Reply(CHAN_X_NOT_IN_USE, ci->name.c_str());
- return;
- }
-
- AccessGroup u_access = source.AccessFor(ci), targ_access = ci->AccessFor(targ);
- const std::pair<bool, Anope::string> &m = modes[source.command];
-
- bool can_override = source.HasPriv("chanserv/administration");
- bool override = false;
-
- if (m.second.empty())
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- if (u == targ ? !u_access.HasPriv(m.second + "ME") : !u_access.HasPriv(m.second))
- {
- if (!can_override)
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
- else
- override = true;
- }
-
- if (!override && !m.first && u != targ && (targ->IsProtected() || (ci->HasExt("PEACE") && targ_access >= u_access)))
- {
- if (!can_override)
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
- else
- override = true;
- }
-
- if (!ci->c->FindUser(targ))
- {
- source.Reply(NICK_X_NOT_ON_CHAN, targ->nick.c_str(), ci->name.c_str());
- return;
- }
-
- if (m.first)
- ci->c->SetMode(NULL, m.second, targ->GetUID());
- else
- ci->c->RemoveMode(NULL, m.second, targ->GetUID());
-
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "on " << targ->nick;
- }
-
- const Anope::string GetDesc(CommandSource &source) const anope_override
- {
- const std::pair<bool, Anope::string> &m = modes[source.command];
- if (!m.second.empty())
- {
- if (m.first)
- return Anope::printf(Language::Translate(source.GetAccount(), _("Gives you or the specified nick %s status on a channel")), m.second.c_str());
- else
- return Anope::printf(Language::Translate(source.GetAccount(), _("Removes %s status from you or the specified nick on a channel")), m.second.c_str());
- }
- else
- return "";
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- const std::pair<bool, Anope::string> &m = modes[source.command];
- if (m.second.empty())
- return false;
-
- this->SendSyntax(source);
- source.Reply(" ");
- if (m.first)
- source.Reply(_("Gives %s status to the selected nick on a channel. If \037nick\037 is\n"
- "not given, it will %s you."),
- m.second.upper().c_str(), m.second.lower().c_str());
- else
- source.Reply(_("Removes %s status from the selected nick on a channel. If \037nick\037 is\n"
- "not given, it will de%s you."),
- m.second.upper().c_str(), m.second.lower().c_str());
- source.Reply(" ");
- source.Reply(_("You must have the %s(ME) privilege on the channel to use this command."), m.second.upper().c_str());
-
- return true;
- }
-};
-
-class CSMode : public Module
-{
- CommandCSMode commandcsmode;
- CommandCSModes commandcsmodes;
- ExtensibleItem<ModeLocksImpl> modelocks;
- Serialize::Type modelocks_type;
-
- public:
- CSMode(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandcsmode(this), commandcsmodes(this),
- modelocks(this, "modelocks"),
- modelocks_type("ModeLock", ModeLockImpl::Unserialize)
- {
-
- }
-
- void OnReload(Configuration::Conf *conf) anope_override
- {
- modes.clear();
-
- for (int i = 0; i < conf->CountBlock("command"); ++i)
- {
- Configuration::Block *block = conf->GetBlock("command", i);
-
- const Anope::string &cname = block->Get<const Anope::string>("name"),
- &cmd = block->Get<const Anope::string>("command");
-
- if (cname.empty() || cmd != "chanserv/modes")
- continue;
-
- const Anope::string &set = block->Get<const Anope::string>("set"),
- &unset = block->Get<const Anope::string>("unset");
-
- if (set.empty() && unset.empty())
- continue;
-
- modes[cname] = std::make_pair(!set.empty(), !set.empty() ? set : unset);
- }
- }
-
- void OnCheckModes(Reference<Channel> &c) anope_override
- {
- if (!c || !c->ci)
- return;
-
- ModeLocks *locks = modelocks.Get(c->ci);
- if (locks)
- for (ModeLocks::ModeList::const_iterator it = locks->GetMLock().begin(), it_end = locks->GetMLock().end(); it != it_end; ++it)
- {
- const ModeLock *ml = *it;
- ChannelMode *cm = ModeManager::FindChannelModeByName(ml->name);
- if (!cm)
- continue;
-
- if (cm->type == MODE_REGULAR)
- {
- if (!c->HasMode(cm->name) && ml->set)
- c->SetMode(NULL, cm, "", false);
- else if (c->HasMode(cm->name) && !ml->set)
- c->RemoveMode(NULL, cm, "", false);
- }
- else if (cm->type == MODE_PARAM)
- {
- /* If the channel doesn't have the mode, or it does and it isn't set correctly */
- if (ml->set)
- {
- Anope::string param;
- c->GetParam(cm->name, param);
-
- if (!c->HasMode(cm->name) || (!param.empty() && !ml->param.empty() && !param.equals_cs(ml->param)))
- c->SetMode(NULL, cm, ml->param, false);
- }
- else
- {
- if (c->HasMode(cm->name))
- c->RemoveMode(NULL, cm, "", false);
- }
-
- }
- else if (cm->type == MODE_LIST || cm->type == MODE_STATUS)
- {
- if (ml->set)
- c->SetMode(NULL, cm, ml->param, false);
- else
- c->RemoveMode(NULL, cm, ml->param, false);
- }
- }
- }
-
- void OnChanRegistered(ChannelInfo *ci) anope_override
- {
- ModeLocks *ml = modelocks.Require(ci);
- Anope::string mlock;
- spacesepstream sep(Config->GetModule(this)->Get<const Anope::string>("mlock", "+nt"));
- if (sep.GetToken(mlock))
- {
- bool add = true;
- for (unsigned i = 0; i < mlock.length(); ++i)
- {
- if (mlock[i] == '+')
- {
- add = true;
- continue;
- }
-
- if (mlock[i] == '-')
- {
- add = false;
- continue;
- }
-
- ChannelMode *cm = ModeManager::FindChannelModeByChar(mlock[i]);
- if (!cm)
- continue;
-
- Anope::string param;
- if (cm->type == MODE_PARAM)
- {
- ChannelModeParam *cmp = anope_dynamic_static_cast<ChannelModeParam *>(cm);
- if (add || !cmp->minus_no_arg)
- {
- sep.GetToken(param);
- if (param.empty() || !cmp->IsValid(param))
- continue;
- }
- }
- else if (cm->type != MODE_REGULAR)
- {
- sep.GetToken(param);
- if (param.empty())
- continue;
- }
-
- ml->SetMLock(cm, add, param);
- }
- }
- ml->Check();
- }
-
- void OnChanInfo(CommandSource &source, ChannelInfo *ci, InfoFormatter &info, bool show_hidden) anope_override
- {
- if (!show_hidden)
- return;
-
- ModeLocks *ml = modelocks.Get(ci);
- if (ml)
- info[_("Mode lock")] = ml->GetMLockAsString(true);
- }
-};
-
-MODULE_INIT(CSMode)
diff --git a/modules/commands/cs_register.cpp b/modules/commands/cs_register.cpp
deleted file mode 100644
index 8b73c9f06..000000000
--- a/modules/commands/cs_register.cpp
+++ /dev/null
@@ -1,124 +0,0 @@
-/* ChanServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class CommandCSRegister : public Command
-{
- public:
- CommandCSRegister(Module *creator) : Command(creator, "chanserv/register", 1, 2)
- {
- this->SetDesc(_("Register a channel"));
- this->SetSyntax(_("\037channel\037 [\037description\037]"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &chan = params[0];
- const Anope::string &chdesc = params.size() > 1 ? params[1] : "";
- unsigned maxregistered = Config->GetModule("chanserv")->Get<unsigned>("maxregistered");
-
- User *u = source.GetUser();
- NickCore *nc = source.nc;
- Channel *c = Channel::Find(params[0]);
- ChannelInfo *ci = ChannelInfo::Find(params[0]);
-
- if (Anope::ReadOnly)
- source.Reply(_("Sorry, channel registration is temporarily disabled."));
- else if (nc->HasExt("UNCONFIRMED"))
- source.Reply(_("You must confirm your account before you can register a channel."));
- else if (chan[0] == '&')
- source.Reply(_("Local channels cannot be registered."));
- else if (chan[0] != '#')
- source.Reply(CHAN_SYMBOL_REQUIRED);
- else if (!IRCD->IsChannelValid(chan))
- source.Reply(CHAN_X_INVALID, chan.c_str());
- else if (!c && u)
- source.Reply(CHAN_X_NOT_IN_USE, chan.c_str());
- else if (ci)
- source.Reply(_("Channel \002%s\002 is already registered!"), chan.c_str());
- else if (c && u && !c->HasUserStatus(u, "OP"))
- source.Reply(_("You must be a channel operator to register the channel."));
- else if (maxregistered && nc->channelcount >= maxregistered && !source.HasPriv("chanserv/no-register-limit"))
- source.Reply(nc->channelcount > maxregistered ? CHAN_EXCEEDED_CHANNEL_LIMIT : CHAN_REACHED_CHANNEL_LIMIT, maxregistered);
- else
- {
- ci = new ChannelInfo(chan);
- ci->SetFounder(nc);
- ci->desc = chdesc;
-
- if (c && !c->topic.empty())
- {
- ci->last_topic = c->topic;
- ci->last_topic_setter = c->topic_setter;
- ci->last_topic_time = c->topic_time;
- }
- else
- ci->last_topic_setter = source.service->nick;
-
- Log(LOG_COMMAND, source, this, ci);
- source.Reply(_("Channel \002%s\002 registered under your account: %s"), chan.c_str(), nc->display.c_str());
-
- FOREACH_MOD(OnChanRegistered, (ci));
-
- /* Implement new mode lock */
- if (c)
- {
- c->CheckModes();
- if (u)
- c->SetCorrectModes(u, true);
- }
- }
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Registers a channel in the %s database. In order\n"
- "to use this command, you must first be a channel operator\n"
- "on the channel you're trying to register.\n"
- "The description, which is optional, is a\n"
- "general description of the channel's purpose.\n"
- " \n"
- "When you register a channel, you are recorded as the\n"
- "\"founder\" of the channel. The channel founder is allowed\n"
- "to change all of the channel settings for the channel;\n"
- "%s will also automatically give the founder\n"
- "channel-operator privileges when s/he enters the channel."),
- source.service->nick.c_str(), source.service->nick.c_str());
- BotInfo *bi;
- Anope::string cmd;
- if (Command::FindCommandFromService("chanserv/access", bi, cmd))
- source.Reply(_(" \n"
- "See the \002%s\002 command (\002%s%s HELP ACCESS\002) for\n"
- "information on giving a subset of these privileges to\n"
- "other channel users.\n"), cmd.c_str(), Config->StrictPrivmsg.c_str(), bi->nick.c_str());
- source.Reply(_(" \n"
- "NOTICE: In order to register a channel, you must have\n"
- "first registered your nickname."));
- return true;
- }
-};
-
-
-class CSRegister : public Module
-{
- CommandCSRegister commandcsregister;
-
- public:
- CSRegister(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandcsregister(this)
- {
- }
-};
-
-MODULE_INIT(CSRegister)
diff --git a/modules/commands/cs_set.cpp b/modules/commands/cs_set.cpp
deleted file mode 100644
index fb58f9efd..000000000
--- a/modules/commands/cs_set.cpp
+++ /dev/null
@@ -1,1356 +0,0 @@
-/* ChanServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-#include "modules/cs_mode.h"
-
-class CommandCSSet : public Command
-{
- public:
- CommandCSSet(Module *creator) : Command(creator, "chanserv/set", 2, 3)
- {
- this->SetDesc(_("Set channel options and information"));
- this->SetSyntax(_("\037option\037 \037channel\037 \037parameters\037"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- this->OnSyntaxError(source, "");
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Allows the channel founder to set various channel options\n"
- "and other information.\n"
- " \n"
- "Available options:"));
- Anope::string this_name = source.command;
- bool hide_privileged_commands = Config->GetBlock("options")->Get<bool>("hideprivilegedcommands"),
- hide_registered_commands = Config->GetBlock("options")->Get<bool>("hideregisteredcommands");
- for (CommandInfo::map::const_iterator it = source.service->commands.begin(), it_end = source.service->commands.end(); it != it_end; ++it)
- {
- const Anope::string &c_name = it->first;
- const CommandInfo &info = it->second;
- if (c_name.find_ci(this_name + " ") == 0)
- {
- ServiceReference<Command> c("Command", info.name);
-
- // XXX dup
- if (!c)
- continue;
- else if (hide_registered_commands && !c->AllowUnregistered() && !source.GetAccount())
- continue;
- else if (hide_privileged_commands && !info.permission.empty() && !source.HasCommand(info.permission))
- continue;
-
- source.command = it->first;
- c->OnServHelp(source);
- }
- }
- source.Reply(_("Type \002%s%s HELP %s \037option\037\002 for more information on a\n"
- "particular option."), Config->StrictPrivmsg.c_str(), source.service->nick.c_str(), this_name.c_str());
- return true;
- }
-};
-
-class CommandCSSetAutoOp : public Command
-{
- public:
- CommandCSSetAutoOp(Module *creator, const Anope::string &cname = "chanserv/set/autoop") : Command(creator, cname, 2, 2)
- {
- this->SetDesc(_("Should services automatically give status to users"));
- this->SetSyntax(_("\037channel\037 {ON | OFF}"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- ChannelInfo *ci = ChannelInfo::Find(params[0]);
- if (ci == NULL)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
- return;
- }
-
- EventReturn MOD_RESULT;
- FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, params[1]));
- if (MOD_RESULT == EVENT_STOP)
- return;
-
- if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- if (params[1].equals_ci("ON"))
- {
- Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable autoop";
- ci->Shrink<bool>("NOAUTOOP");
- source.Reply(_("Services will now automatically give modes to users in \002%s\002."), ci->name.c_str());
- }
- else if (params[1].equals_ci("OFF"))
- {
- Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable autoop";
- ci->Extend<bool>("NOAUTOOP");
- source.Reply(_("Services will no longer automatically give modes to users in \002%s\002."), ci->name.c_str());
- }
- else
- this->OnSyntaxError(source, "AUTOOP");
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Enables or disables %s's autoop feature for a\n"
- "channel. When disabled, users who join the channel will\n"
- "not automatically gain any status from %s."), source.service->nick.c_str(),
- source.service->nick.c_str(), this->name.c_str());
- return true;
- }
-};
-
-class CommandCSSetBanType : public Command
-{
- public:
- CommandCSSetBanType(Module *creator, const Anope::string &cname = "chanserv/set/bantype") : Command(creator, cname, 2, 2)
- {
- this->SetDesc(_("Set how Services make bans on the channel"));
- this->SetSyntax(_("\037channel\037 \037bantype\037"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- ChannelInfo *ci = ChannelInfo::Find(params[0]);
- if (ci == NULL)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
- return;
- }
-
- EventReturn MOD_RESULT;
- FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, params[1]));
- if (MOD_RESULT == EVENT_STOP)
- return;
-
- if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- try
- {
- int16_t new_type = convertTo<int16_t>(params[1]);
- if (new_type < 0 || new_type > 3)
- throw ConvertException("Invalid range");
- Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to change the ban type to " << new_type;
- ci->bantype = new_type;
- source.Reply(_("Ban type for channel %s is now #%d."), ci->name.c_str(), ci->bantype);
- }
- catch (const ConvertException &)
- {
- source.Reply(_("\002%s\002 is not a valid ban type."), params[1].c_str());
- }
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Sets the ban type that will be used by services whenever\n"
- "they need to ban someone from your channel.\n"
- " \n"
- "Bantype is a number between 0 and 3 that means:\n"
- " \n"
- "0: ban in the form *!user@host\n"
- "1: ban in the form *!*user@host\n"
- "2: ban in the form *!*@host\n"
- "3: ban in the form *!*user@*.domain"), this->name.c_str());
- return true;
- }
-};
-
-class CommandCSSetDescription : public Command
-{
- public:
- CommandCSSetDescription(Module *creator, const Anope::string &cname = "chanserv/set/description") : Command(creator, cname, 1, 2)
- {
- this->SetDesc(_("Set the channel description"));
- this->SetSyntax(_("\037channel\037 [\037description\037]"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- ChannelInfo *ci = ChannelInfo::Find(params[0]);
- const Anope::string &param = params.size() > 1 ? params[1] : "";
- if (ci == NULL)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
- return;
- }
-
- EventReturn MOD_RESULT;
- FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, param));
- if (MOD_RESULT == EVENT_STOP)
- return;
-
- if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- if (!param.empty())
- {
- ci->desc = param;
- Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to change the description to " << ci->desc;
- source.Reply(_("Description of %s changed to \002%s\002."), ci->name.c_str(), ci->desc.c_str());
- }
- else
- {
- ci->desc.clear();
- Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to unset the description";
- source.Reply(_("Description of %s unset."), ci->name.c_str());
- }
-
- return;
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Sets the description for the channel, which shows up with\n"
- "the \002LIST\002 and \002INFO\002 commands."), this->name.c_str());
- return true;
- }
-};
-
-class CommandCSSetFounder : public Command
-{
- public:
- CommandCSSetFounder(Module *creator, const Anope::string &cname = "chanserv/set/founder") : Command(creator, cname, 2, 2)
- {
- this->SetDesc(_("Set the founder of a channel"));
- this->SetSyntax(_("\037channel\037 \037nick\037"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- ChannelInfo *ci = ChannelInfo::Find(params[0]);
- if (ci == NULL)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
- return;
- }
-
- EventReturn MOD_RESULT;
- FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, params[1]));
- if (MOD_RESULT == EVENT_STOP)
- return;
-
- if (MOD_RESULT != EVENT_ALLOW && (ci->HasExt("SECUREFOUNDER") ? !source.IsFounder(ci) : !source.AccessFor(ci).HasPriv("FOUNDER")) && source.permission.empty() && !source.HasPriv("chanserv/administration"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- const NickAlias *na = NickAlias::Find(params[1]);
- if (!na)
- {
- source.Reply(NICK_X_NOT_REGISTERED, params[1].c_str());
- return;
- }
-
- NickCore *nc = na->nc;
- unsigned max_reg = Config->GetModule("chanserv")->Get<unsigned>("maxregistered");
- if (max_reg && nc->channelcount >= max_reg && !source.HasPriv("chanserv/no-register-limit"))
- {
- source.Reply(_("\002%s\002 has too many channels registered."), na->nick.c_str());
- return;
- }
-
- Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to change the founder from " << (ci->GetFounder() ? ci->GetFounder()->display : "(none)") << " to " << nc->display;
-
- ci->SetFounder(nc);
-
- source.Reply(_("Founder of \002%s\002 changed to \002%s\002."), ci->name.c_str(), na->nick.c_str());
-
- return;
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Changes the founder of a channel. The new nickname must\n"
- "be a registered one."), this->name.c_str());
- return true;
- }
-};
-
-class CommandCSSetKeepModes : public Command
-{
- public:
- CommandCSSetKeepModes(Module *creator, const Anope::string &cname = "chanserv/set/keepmodes") : Command(creator, cname, 2, 2)
- {
- this->SetDesc(_("Retain modes when channel is not in use"));
- this->SetSyntax(_("\037channel\037 {ON | OFF}"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- ChannelInfo *ci = ChannelInfo::Find(params[0]);
- if (ci == NULL)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
- return;
- }
-
- EventReturn MOD_RESULT;
- FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, params[1]));
- if (MOD_RESULT == EVENT_STOP)
- return;
-
- if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- if (params[1].equals_ci("ON"))
- {
- Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable keep modes";
- ci->Extend<bool>("CS_KEEP_MODES");
- source.Reply(_("Keep modes for %s is now \002on\002."), ci->name.c_str());
- if (ci->c)
- ci->last_modes = ci->c->GetModes();
- }
- else if (params[1].equals_ci("OFF"))
- {
- Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable keep modes";
- ci->Shrink<bool>("CS_KEEP_MODES");
- source.Reply(_("Keep modes for %s is now \002off\002."), ci->name.c_str());
- ci->last_modes.clear();
- }
- else
- this->OnSyntaxError(source, "KEEPMODES");
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Enables or disables keepmodes for the given channel. If keep\n"
- "modes is enabled, services will remember modes set on the channel\n"
- "and attempt to re-set them the next time the channel is created."));
- return true;
- }
-};
-
-class CommandCSSetPeace : public Command
-{
- public:
- CommandCSSetPeace(Module *creator, const Anope::string &cname = "chanserv/set/peace") : Command(creator, cname, 2, 2)
- {
- this->SetDesc(_("Regulate the use of critical commands"));
- this->SetSyntax(_("\037channel\037 {ON | OFF}"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- ChannelInfo *ci = ChannelInfo::Find(params[0]);
- if (ci == NULL)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
- return;
- }
-
- EventReturn MOD_RESULT;
- FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, params[1]));
- if (MOD_RESULT == EVENT_STOP)
- return;
-
- if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- if (params[1].equals_ci("ON"))
- {
- Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable peace";
- ci->Extend<bool>("PEACE");
- source.Reply(_("Peace option for %s is now \002on\002."), ci->name.c_str());
- }
- else if (params[1].equals_ci("OFF"))
- {
- Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable peace";
- ci->Shrink<bool>("PEACE");
- source.Reply(_("Peace option for %s is now \002off\002."), ci->name.c_str());
- }
- else
- this->OnSyntaxError(source, "PEACE");
-
- return;
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Enables or disables the \002peace\002 option for a channel.\n"
- "When \002peace\002 is set, a user won't be able to kick,\n"
- "ban or remove a channel status of a user that has\n"
- "a level superior or equal to his via %s commands."), source.service->nick.c_str());
- return true;
- }
-};
-
-inline static Anope::string BotModes()
-{
- return Config->GetModule("botserv")->Get<Anope::string>("botmodes",
- Config->GetModule("chanserv")->Get<Anope::string>("botmodes", "o")
- );
-}
-
-class CommandCSSetPersist : public Command
-{
- public:
- CommandCSSetPersist(Module *creator, const Anope::string &cname = "chanserv/set/persist") : Command(creator, cname, 2, 2)
- {
- this->SetDesc(_("Set the channel as permanent"));
- this->SetSyntax(_("\037channel\037 {ON | OFF}"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- ChannelInfo *ci = ChannelInfo::Find(params[0]);
- if (ci == NULL)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
- return;
- }
-
- EventReturn MOD_RESULT;
- FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, params[1]));
- if (MOD_RESULT == EVENT_STOP)
- return;
-
- if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- ChannelMode *cm = ModeManager::FindChannelModeByName("PERM");
-
- if (params[1].equals_ci("ON"))
- {
- if (!ci->HasExt("PERSIST"))
- {
- ci->Extend<bool>("PERSIST");
-
- /* Channel doesn't exist, create it */
- if (!ci->c)
- {
- bool created;
- Channel *c = Channel::FindOrCreate(ci->name, created);
- if (ci->bi)
- {
- ChannelStatus status(BotModes());
- ci->bi->Join(c, &status);
- }
- if (created)
- c->Sync();
- }
-
- /* Set the perm mode */
- if (cm)
- {
- if (ci->c && !ci->c->HasMode("PERM"))
- ci->c->SetMode(NULL, cm);
- /* Add it to the channels mlock */
- ModeLocks *ml = ci->Require<ModeLocks>("modelocks");
- if (ml)
- ml->SetMLock(cm, true, "", source.GetNick());
- }
- /* No botserv bot, no channel mode, give them ChanServ.
- * Yes, this works fine with no BotServ.
- */
- else if (!ci->bi)
- {
- BotInfo *ChanServ = Config->GetClient("ChanServ");
- if (!ChanServ)
- {
- source.Reply(_("ChanServ is required to enable persist on this network."));
- return;
- }
-
- ChanServ->Assign(NULL, ci);
- if (!ci->c->FindUser(ChanServ))
- {
- ChannelStatus status(BotModes());
- ChanServ->Join(ci->c, &status);
- }
- }
- }
-
- Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable persist";
- source.Reply(_("Channel \002%s\002 is now persistent."), ci->name.c_str());
- }
- else if (params[1].equals_ci("OFF"))
- {
- if (ci->HasExt("PERSIST"))
- {
- ci->Shrink<bool>("PERSIST");
-
- BotInfo *ChanServ = Config->GetClient("ChanServ"),
- *BotServ = Config->GetClient("BotServ");
-
- /* Unset perm mode */
- if (cm)
- {
- if (ci->c && ci->c->HasMode("PERM"))
- ci->c->RemoveMode(NULL, cm);
- /* Remove from mlock */
- ModeLocks *ml = ci->GetExt<ModeLocks>("modelocks");
- if (ml)
- ml->RemoveMLock(cm, true);
- }
- /* No channel mode, no BotServ, but using ChanServ as the botserv bot
- * which was assigned when persist was set on
- */
- else if (!cm && !BotServ && ci->bi)
- {
- if (!ChanServ)
- {
- source.Reply(_("ChanServ is required to enable persist on this network."));
- return;
- }
-
- /* Unassign bot */
- ChanServ->UnAssign(NULL, ci);
- }
- }
-
- Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable persist";
- source.Reply(_("Channel \002%s\002 is no longer persistent."), ci->name.c_str());
- }
- else
- this->OnSyntaxError(source, "PERSIST");
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
- {
- BotInfo *BotServ = Config->GetClient("BotServ");
- BotInfo *ChanServ = Config->GetClient("ChanServ");
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Enables or disables the persistent channel setting.\n"
- "When persistent is set, the service bot will remain\n"
- "in the channel when it has emptied of users.\n"
- " \n"
- "If your IRCd does not have a permanent (persistent) channel\n"
- "mode you must have a service bot in your channel to\n"
- "set persist on, and it can not be unassigned while persist\n"
- "is on.\n"
- " \n"
- "If this network does not have %s enabled and does\n"
- "not have a permanent channel mode, %s will\n"
- "join your channel when you set persist on (and leave when\n"
- "it has been set off).\n"
- " \n"
- "If your IRCd has a permanent (persistent) channel mode\n"
- "and it is set or unset (for any reason, including MODE LOCK),\n"
- "persist is automatically set and unset for the channel as well.\n"
- "Additionally, services will set or unset this mode when you\n"
- "set persist on or off."), BotServ ? BotServ->nick.c_str() : "BotServ",
- ChanServ ? ChanServ->nick.c_str() : "ChanServ");
- return true;
- }
-};
-
-class CommandCSSetRestricted : public Command
-{
- public:
- CommandCSSetRestricted(Module *creator, const Anope::string &cname = "chanserv/set/restricted") : Command(creator, cname, 2, 2)
- {
- this->SetDesc(_("Restrict access to the channel"));
- this->SetSyntax(_("\037channel\037 {ON | OFF}"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- ChannelInfo *ci = ChannelInfo::Find(params[0]);
- if (ci == NULL)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
- return;
- }
-
- EventReturn MOD_RESULT;
- FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, params[1]));
- if (MOD_RESULT == EVENT_STOP)
- return;
-
- if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- if (params[1].equals_ci("ON"))
- {
- Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable restricted";
- ci->Extend<bool>("RESTRICTED");
- source.Reply(_("Restricted access option for %s is now \002on\002."), ci->name.c_str());
- }
- else if (params[1].equals_ci("OFF"))
- {
- Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable restricted";
- ci->Shrink<bool>("RESTRICTED");
- source.Reply(_("Restricted access option for %s is now \002off\002."), ci->name.c_str());
- }
- else
- this->OnSyntaxError(source, "RESTRICTED");
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Enables or disables the \002restricted access\002 option for a\n"
- "channel. When \002restricted access\002 is set, users not on the access list will\n"
- "instead be kicked and banned from the channel."));
- return true;
- }
-};
-
-class CommandCSSetSecure : public Command
-{
- public:
- CommandCSSetSecure(Module *creator, const Anope::string &cname = "chanserv/set/secure") : Command(creator, cname, 2, 2)
- {
- this->SetDesc(_("Activate security features"));
- this->SetSyntax(_("\037channel\037 {ON | OFF}"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- ChannelInfo *ci = ChannelInfo::Find(params[0]);
- if (ci == NULL)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
- return;
- }
-
- EventReturn MOD_RESULT;
- FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, params[1]));
- if (MOD_RESULT == EVENT_STOP)
- return;
-
- if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- if (params[1].equals_ci("ON"))
- {
- Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable secure";
- ci->Extend<bool>("CS_SECURE");
- source.Reply(_("Secure option for %s is now \002on\002."), ci->name.c_str());
- }
- else if (params[1].equals_ci("OFF"))
- {
- Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable secure";
- ci->Shrink<bool>("CS_SECURE");
- source.Reply(_("Secure option for %s is now \002off\002."), ci->name.c_str());
- }
- else
- this->OnSyntaxError(source, "SECURE");
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Enables or disables security features for a\n"
- "channel. When \002SECURE\002 is set, only users who have\n"
- "identified to services, and are not only recognized, will be\n"
- "given access to channels from account-based access entries."));
- return true;
- }
-};
-
-class CommandCSSetSecureFounder : public Command
-{
- public:
- CommandCSSetSecureFounder(Module *creator, const Anope::string &cname = "chanserv/set/securefounder") : Command(creator, cname, 2, 2)
- {
- this->SetDesc(_("Stricter control of channel founder status"));
- this->SetSyntax(_("\037channel\037 {ON | OFF}"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- ChannelInfo *ci = ChannelInfo::Find(params[0]);
- if (ci == NULL)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
- return;
- }
-
- EventReturn MOD_RESULT;
- FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, params[1]));
- if (MOD_RESULT == EVENT_STOP)
- return;
-
- if (MOD_RESULT != EVENT_ALLOW && (ci->HasExt("SECUREFOUNDER") ? !source.IsFounder(ci) : !source.AccessFor(ci).HasPriv("FOUNDER")) && source.permission.empty() && !source.HasPriv("chanserv/administration"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- if (params[1].equals_ci("ON"))
- {
- Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable secure founder";
- ci->Extend<bool>("SECUREFOUNDER");
- source.Reply(_("Secure founder option for %s is now \002on\002."), ci->name.c_str());
- }
- else if (params[1].equals_ci("OFF"))
- {
- Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable secure founder";
- ci->Shrink<bool>("SECUREFOUNDER");
- source.Reply(_("Secure founder option for %s is now \002off\002."), ci->name.c_str());
- }
- else
- this->OnSyntaxError(source, "SECUREFOUNDER");
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Enables or disables the \002secure founder\002 option for a channel.\n"
- "When \002secure founder\002 is set, only the real founder will be\n"
- "able to drop the channel, change its founder and its successor,\n"
- "and not those who have founder level access through\n"
- "the access/qop command."));
- return true;
- }
-};
-
-class CommandCSSetSecureOps : public Command
-{
- public:
- CommandCSSetSecureOps(Module *creator, const Anope::string &cname = "chanserv/set/secureops") : Command(creator, cname, 2, 2)
- {
- this->SetDesc(_("Stricter control of chanop status"));
- this->SetSyntax(_("\037channel\037 {ON | OFF}"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- ChannelInfo *ci = ChannelInfo::Find(params[0]);
- if (ci == NULL)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
- return;
- }
-
- EventReturn MOD_RESULT;
- FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, params[1]));
- if (MOD_RESULT == EVENT_STOP)
- return;
-
- if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- if (params[1].equals_ci("ON"))
- {
- Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable secure ops";
- ci->Extend<bool>("SECUREOPS");
- source.Reply(_("Secure ops option for %s is now \002on\002."), ci->name.c_str());
- }
- else if (params[1].equals_ci("OFF"))
- {
- Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable secure ops";
- ci->Shrink<bool>("SECUREOPS");
- source.Reply(_("Secure ops option for %s is now \002off\002."), ci->name.c_str());
- }
- else
- this->OnSyntaxError(source, "SECUREOPS");
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Enables or disables the \002secure ops\002 option for a channel.\n"
- "When \002secure ops\002 is set, users who are not on the access list\n"
- "will not be allowed channel operator status."));
- return true;
- }
-};
-
-class CommandCSSetSignKick : public Command
-{
- public:
- CommandCSSetSignKick(Module *creator, const Anope::string &cname = "chanserv/set/signkick") : Command(creator, cname, 2, 2)
- {
- this->SetDesc(_("Sign kicks that are done with the KICK command"));
- this->SetSyntax(_("\037channel\037 {ON | LEVEL | OFF}"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- ChannelInfo *ci = ChannelInfo::Find(params[0]);
- if (ci == NULL)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
- return;
- }
-
- EventReturn MOD_RESULT;
- FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, params[1]));
- if (MOD_RESULT == EVENT_STOP)
- return;
-
- if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- if (params[1].equals_ci("ON"))
- {
- ci->Extend<bool>("SIGNKICK");
- ci->Shrink<bool>("SIGNKICK_LEVEL");
- source.Reply(_("Signed kick option for %s is now \002on\002."), ci->name.c_str());
- Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable sign kick";
- }
- else if (params[1].equals_ci("LEVEL"))
- {
- ci->Extend<bool>("SIGNKICK_LEVEL");
- ci->Shrink<bool>("SIGNKICK");
- source.Reply(_("Signed kick option for %s is now \002on\002, but depends of the\n"
- "level of the user that is using the command."), ci->name.c_str());
- Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable sign kick level";
- }
- else if (params[1].equals_ci("OFF"))
- {
- ci->Shrink<bool>("SIGNKICK");
- ci->Shrink<bool>("SIGNKICK_LEVEL");
- source.Reply(_("Signed kick option for %s is now \002off\002."), ci->name.c_str());
- Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable sign kick";
- }
- else
- this->OnSyntaxError(source, "SIGNKICK");
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Enables or disables signed kicks for a\n"
- "channel. When \002SIGNKICK\002 is set, kicks issued with\n"
- "the \002KICK\002 command will have the nick that used the\n"
- "command in their reason.\n"
- " \n"
- "If you use \002LEVEL\002, those who have a level that is superior\n"
- "or equal to the SIGNKICK level on the channel won't have their\n"
- "kicks signed."));
- return true;
- }
-};
-
-class CommandCSSetSuccessor : public Command
-{
- public:
- CommandCSSetSuccessor(Module *creator, const Anope::string &cname = "chanserv/set/successor") : Command(creator, cname, 1, 2)
- {
- this->SetDesc(_("Set the successor for a channel"));
- this->SetSyntax(_("\037channel\037 \037nick\037"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- ChannelInfo *ci = ChannelInfo::Find(params[0]);
- const Anope::string &param = params.size() > 1 ? params[1] : "";
- if (ci == NULL)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
- return;
- }
-
- EventReturn MOD_RESULT;
- FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, param));
- if (MOD_RESULT == EVENT_STOP)
- return;
-
- if (MOD_RESULT != EVENT_ALLOW && (ci->HasExt("SECUREFOUNDER") ? !source.IsFounder(ci) : !source.AccessFor(ci).HasPriv("FOUNDER")) && source.permission.empty() && !source.HasPriv("chanserv/administration"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- NickCore *nc;
-
- if (!param.empty())
- {
- const NickAlias *na = NickAlias::Find(param);
-
- if (!na)
- {
- source.Reply(NICK_X_NOT_REGISTERED, param.c_str());
- return;
- }
- if (na->nc == ci->GetFounder())
- {
- source.Reply(_("%s cannot be the successor on channel %s as they are the founder."), na->nick.c_str(), ci->name.c_str());
- return;
- }
- nc = na->nc;
- }
- else
- nc = NULL;
-
- Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to change the successor from " << (ci->GetSuccessor() ? ci->GetSuccessor()->display : "(none)") << " to " << (nc ? nc->display : "(none)");
-
- ci->SetSuccessor(nc);
-
- if (nc)
- source.Reply(_("Successor for \002%s\002 changed to \002%s\002."), ci->name.c_str(), nc->display.c_str());
- else
- source.Reply(_("Successor for \002%s\002 unset."), ci->name.c_str());
-
- return;
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Changes the successor of a channel. If the founder's\n"
- "nickname expires or is dropped while the channel is still\n"
- "registered, the successor will become the new founder of the\n"
- "channel. The new nickname must be a registered one."));
- unsigned max_reg = Config->GetModule("chanserv")->Get<unsigned>("maxregistered");
- if (max_reg)
- source.Reply(_("However, if the successor already has too many\n"
- "channels registered (%d), the channel will be dropped\n"
- "instead, just as if no successor had been set."), max_reg);
- return true;
- }
-};
-
-class CommandCSSetNoexpire : public Command
-{
- public:
- CommandCSSetNoexpire(Module *creator) : Command(creator, "chanserv/saset/noexpire", 2, 2)
- {
- this->SetDesc(_("Prevent the channel from expiring"));
- this->SetSyntax(_("\037channel\037 {ON | OFF}"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- ChannelInfo *ci = ChannelInfo::Find(params[0]);
- if (ci == NULL)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
- return;
- }
-
- if (source.permission.empty() && !source.AccessFor(ci).HasPriv("SET"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- if (params[1].equals_ci("ON"))
- {
- Log(LOG_ADMIN, source, this, ci) << "to enable noexpire";
- ci->Extend<bool>("CS_NO_EXPIRE");
- source.Reply(_("Channel %s \002will not\002 expire."), ci->name.c_str());
- }
- else if (params[1].equals_ci("OFF"))
- {
- Log(LOG_ADMIN, source, this, ci) << "to disable noexpire";
- ci->Shrink<bool>("CS_NO_EXPIRE");
- source.Reply(_("Channel %s \002will\002 expire."), ci->name.c_str());
- }
- else
- this->OnSyntaxError(source, "NOEXPIRE");
-
- return;
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Sets whether the given channel will expire. Setting this\n"
- "to ON prevents the channel from expiring."));
- return true;
- }
-};
-
-class CSSet : public Module
-{
- SerializableExtensibleItem<bool> noautoop, peace, securefounder,
- restricted, secure, secureops, signkick, signkick_level, noexpire;
-
- struct KeepModes : SerializableExtensibleItem<bool>
- {
- KeepModes(Module *m, const Anope::string &n) : SerializableExtensibleItem<bool>(m, n) { }
-
- void ExtensibleSerialize(const Extensible *e, const Serializable *s, Serialize::Data &data) const anope_override
- {
- SerializableExtensibleItem<bool>::ExtensibleSerialize(e, s, data);
-
- if (s->GetSerializableType()->GetName() != "ChannelInfo")
- return;
-
- const ChannelInfo *ci = anope_dynamic_static_cast<const ChannelInfo *>(s);
- Anope::string modes;
- for (Channel::ModeList::const_iterator it = ci->last_modes.begin(); it != ci->last_modes.end(); ++it)
- {
- if (!modes.empty())
- modes += " ";
- modes += it->first;
- if (!it->second.empty())
- modes += "," + it->second;
- }
- data["last_modes"] << modes;
- }
-
- void ExtensibleUnserialize(Extensible *e, Serializable *s, Serialize::Data &data) anope_override
- {
- SerializableExtensibleItem<bool>::ExtensibleUnserialize(e, s, data);
-
- if (s->GetSerializableType()->GetName() != "ChannelInfo")
- return;
-
- ChannelInfo *ci = anope_dynamic_static_cast<ChannelInfo *>(s);
- Anope::string modes;
- data["last_modes"] >> modes;
- for (spacesepstream sep(modes); sep.GetToken(modes);)
- {
- size_t c = modes.find(',');
- if (c == Anope::string::npos)
- ci->last_modes.insert(std::make_pair(modes, ""));
- else
- ci->last_modes.insert(std::make_pair(modes.substr(0, c), modes.substr(c + 1)));
- }
- }
- } keep_modes;
-
- struct Persist : SerializableExtensibleItem<bool>
- {
- Persist(Module *m, const Anope::string &n) : SerializableExtensibleItem<bool>(m, n) { }
-
- void ExtensibleUnserialize(Extensible *e, Serializable *s, Serialize::Data &data) anope_override
- {
- SerializableExtensibleItem<bool>::ExtensibleUnserialize(e, s, data);
-
- if (s->GetSerializableType()->GetName() != "ChannelInfo" || !this->HasExt(e))
- return;
-
- ChannelInfo *ci = anope_dynamic_static_cast<ChannelInfo *>(s);
- if (ci->c)
- return;
-
- bool created;
- Channel *c = Channel::FindOrCreate(ci->name, created);
-
- ChannelMode *cm = ModeManager::FindChannelModeByName("PERM");
- if (cm)
- {
- c->SetMode(NULL, cm);
- }
- /* on startup we might not know mode availibity here */
- else if (Me && Me->IsSynced())
- {
- if (!ci->bi)
- {
- BotInfo *ChanServ = Config->GetClient("ChanServ");
- if (ChanServ)
- ChanServ->Assign(NULL, ci);
- }
-
- if (ci->bi && !c->FindUser(ci->bi))
- {
- ChannelStatus status(BotModes());
- ci->bi->Join(c, &status);
- }
- }
-
- if (created)
- c->Sync();
- }
- } persist;
-
- CommandCSSet commandcsset;
- CommandCSSetAutoOp commandcssetautoop;
- CommandCSSetBanType commandcssetbantype;
- CommandCSSetDescription commandcssetdescription;
- CommandCSSetFounder commandcssetfounder;
- CommandCSSetKeepModes commandcssetkeepmodes;
- CommandCSSetPeace commandcssetpeace;
- CommandCSSetPersist commandcssetpersist;
- CommandCSSetRestricted commandcssetrestricted;
- CommandCSSetSecure commandcssetsecure;
- CommandCSSetSecureFounder commandcssetsecurefounder;
- CommandCSSetSecureOps commandcssetsecureops;
- CommandCSSetSignKick commandcssetsignkick;
- CommandCSSetSuccessor commandcssetsuccessor;
- CommandCSSetNoexpire commandcssetnoexpire;
-
- ExtensibleRef<bool> inhabit;
-
- bool persist_lower_ts;
-
- public:
- CSSet(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- noautoop(this, "NOAUTOOP"), peace(this, "PEACE"),
- securefounder(this, "SECUREFOUNDER"), restricted(this, "RESTRICTED"),
- secure(this, "CS_SECURE"), secureops(this, "SECUREOPS"), signkick(this, "SIGNKICK"),
- signkick_level(this, "SIGNKICK_LEVEL"), noexpire(this, "CS_NO_EXPIRE"),
- keep_modes(this, "CS_KEEP_MODES"), persist(this, "PERSIST"),
-
- commandcsset(this), commandcssetautoop(this), commandcssetbantype(this),
- commandcssetdescription(this), commandcssetfounder(this), commandcssetkeepmodes(this),
- commandcssetpeace(this), commandcssetpersist(this), commandcssetrestricted(this),
- commandcssetsecure(this), commandcssetsecurefounder(this), commandcssetsecureops(this), commandcssetsignkick(this),
- commandcssetsuccessor(this), commandcssetnoexpire(this),
-
- inhabit("inhabit")
- {
- }
-
- void OnReload(Configuration::Conf *conf) anope_override
- {
- persist_lower_ts = conf->GetModule(this)->Get<bool>("persist_lower_ts");
- }
-
- void OnCreateChan(ChannelInfo *ci) anope_override
- {
- ci->bantype = Config->GetModule(this)->Get<int>("defbantype", "2");
- }
-
- void OnChannelSync(Channel *c) anope_override
- {
- if (c->ci && keep_modes.HasExt(c->ci))
- {
- Channel::ModeList ml = c->ci->last_modes;
- for (Channel::ModeList::iterator it = ml.begin(); it != ml.end(); ++it)
- c->SetMode(c->ci->WhoSends(), it->first, it->second);
- }
- }
-
- EventReturn OnCheckKick(User *u, Channel *c, Anope::string &mask, Anope::string &reason) anope_override
- {
- if (!c->ci || !restricted.HasExt(c->ci) || c->MatchesList(u, "EXCEPT"))
- return EVENT_CONTINUE;
-
- if (c->ci->AccessFor(u).empty() && (!c->ci->GetFounder() || u->Account() != c->ci->GetFounder()))
- return EVENT_STOP;
-
- return EVENT_CONTINUE;
- }
-
- void OnDelChan(ChannelInfo *ci) anope_override
- {
- if (ci->c && persist.HasExt(ci))
- ci->c->RemoveMode(ci->WhoSends(), "PERM", "", false);
- persist.Unset(ci);
- }
-
- EventReturn OnChannelModeSet(Channel *c, MessageSource &setter, ChannelMode *mode, const Anope::string &param) anope_override
- {
- if (c->ci)
- {
- /* Channel mode +P or so was set, mark this channel as persistent */
- if (mode->name == "PERM")
- persist.Set(c->ci, true);
-
- if (mode->type != MODE_STATUS && !c->syncing && Me->IsSynced() && (!inhabit || !inhabit->HasExt(c)))
- c->ci->last_modes = c->GetModes();
- }
-
- return EVENT_CONTINUE;
- }
-
- EventReturn OnChannelModeUnset(Channel *c, MessageSource &setter, ChannelMode *mode, const Anope::string &param) anope_override
- {
- if (mode->name == "PERM")
- {
- if (c->ci)
- persist.Unset(c->ci);
- }
-
- if (c->ci && mode->type != MODE_STATUS && !c->syncing && Me->IsSynced() && (!inhabit || !inhabit->HasExt(c)))
- c->ci->last_modes = c->GetModes();
-
- return EVENT_CONTINUE;
- }
-
- void OnJoinChannel(User *u, Channel *c) anope_override
- {
- if (persist_lower_ts && c->ci && persist.HasExt(c->ci) && c->creation_time > c->ci->time_registered)
- {
- Log(LOG_DEBUG) << "Changing TS of " << c->name << " from " << c->creation_time << " to " << c->ci->time_registered;
- c->creation_time = c->ci->time_registered;
- IRCD->SendChannel(c);
- c->Reset();
- }
- }
-
- void OnSetCorrectModes(User *user, Channel *chan, AccessGroup &access, bool &give_modes, bool &take_modes) anope_override
- {
- if (chan->ci)
- {
- if (noautoop.HasExt(chan->ci))
- give_modes = false;
- if (secureops.HasExt(chan->ci))
- // This overrides what chanserv does because it is loaded after chanserv
- take_modes = true;
- }
- }
-
- void OnPreChanExpire(ChannelInfo *ci, bool &expire) anope_override
- {
- if (noexpire.HasExt(ci))
- expire = false;
- }
-
- void OnChanInfo(CommandSource &source, ChannelInfo *ci, InfoFormatter &info, bool show_all) anope_override
- {
- if (!show_all)
- return;
-
- if (peace.HasExt(ci))
- info.AddOption(_("Peace"));
- if (restricted.HasExt(ci))
- info.AddOption(_("Restricted access"));
- if (secure.HasExt(ci))
- info.AddOption(_("Security"));
- if (securefounder.HasExt(ci))
- info.AddOption(_("Secure founder"));
- if (secureops.HasExt(ci))
- info.AddOption(_("Secure ops"));
- if (signkick.HasExt(ci) || signkick_level.HasExt(ci))
- info.AddOption(_("Signed kicks"));
- if (persist.HasExt(ci))
- info.AddOption(_("Persistent"));
- if (noexpire.HasExt(ci))
- info.AddOption(_("No expire"));
- if (keep_modes.HasExt(ci))
- info.AddOption(_("Keep modes"));
- if (noautoop.HasExt(ci))
- info.AddOption(_("No auto-op"));
- }
-};
-
-MODULE_INIT(CSSet)
diff --git a/modules/commands/cs_set_misc.cpp b/modules/commands/cs_set_misc.cpp
deleted file mode 100644
index 038bd7cb5..000000000
--- a/modules/commands/cs_set_misc.cpp
+++ /dev/null
@@ -1,218 +0,0 @@
-/*
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-#include "modules/set_misc.h"
-
-static Module *me;
-
-static Anope::map<Anope::string> descriptions;
-
-struct CSMiscData;
-static Anope::map<ExtensibleItem<CSMiscData> *> items;
-
-static ExtensibleItem<CSMiscData> *GetItem(const Anope::string &name)
-{
- ExtensibleItem<CSMiscData>* &it = items[name];
- if (!it)
- try
- {
- it = new ExtensibleItem<CSMiscData>(me, name);
- }
- catch (const ModuleException &) { }
- return it;
-}
-
-struct CSMiscData : MiscData, Serializable
-{
- CSMiscData(Extensible *obj) : Serializable("CSMiscData") { }
-
- CSMiscData(ChannelInfo *c, const Anope::string &n, const Anope::string &d) : Serializable("CSMiscData")
- {
- object = c->name;
- name = n;
- data = d;
- }
-
- void Serialize(Serialize::Data &sdata) const anope_override
- {
- sdata["ci"] << this->object;
- sdata["name"] << this->name;
- sdata["data"] << this->data;
- }
-
- static Serializable* Unserialize(Serializable *obj, Serialize::Data &data)
- {
- Anope::string sci, sname, sdata;
-
- data["ci"] >> sci;
- data["name"] >> sname;
- data["data"] >> sdata;
-
- ChannelInfo *ci = ChannelInfo::Find(sci);
- if (ci == NULL)
- return NULL;
-
- CSMiscData *d = NULL;
- if (obj)
- {
- d = anope_dynamic_static_cast<CSMiscData *>(obj);
- d->object = ci->name;
- data["name"] >> d->name;
- data["data"] >> d->data;
- }
- else
- {
- ExtensibleItem<CSMiscData> *item = GetItem(sname);
- if (item)
- d = item->Set(ci, CSMiscData(ci, sname, sdata));
- }
-
- return d;
- }
-};
-
-static Anope::string GetAttribute(const Anope::string &command)
-{
- size_t sp = command.rfind(' ');
- if (sp != Anope::string::npos)
- return command.substr(sp + 1);
- return command;
-}
-
-class CommandCSSetMisc : public Command
-{
- public:
- CommandCSSetMisc(Module *creator, const Anope::string &cname = "chanserv/set/misc") : Command(creator, cname, 1, 2)
- {
- this->SetSyntax(_("\037channel\037 [\037parameters\037]"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- ChannelInfo *ci = ChannelInfo::Find(params[0]);
- const Anope::string &param = params.size() > 1 ? params[1] : "";
- if (ci == NULL)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
- return;
- }
-
- EventReturn MOD_RESULT;
- FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, param));
- if (MOD_RESULT == EVENT_STOP)
- return;
-
- if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- Anope::string scommand = GetAttribute(source.command);
- Anope::string key = "cs_set_misc:" + scommand;
- ExtensibleItem<CSMiscData> *item = GetItem(key);
- if (item == NULL)
- return;
-
- if (!param.empty())
- {
- item->Set(ci, CSMiscData(ci, key, param));
- Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to change it to " << param;
- source.Reply(CHAN_SETTING_CHANGED, scommand.c_str(), ci->name.c_str(), params[1].c_str());
- }
- else
- {
- item->Unset(ci);
- Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to unset it";
- source.Reply(CHAN_SETTING_UNSET, scommand.c_str(), ci->name.c_str());
- }
- }
-
- void OnServHelp(CommandSource &source) anope_override
- {
- if (descriptions.count(source.command))
- {
- this->SetDesc(descriptions[source.command]);
- Command::OnServHelp(source);
- }
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- if (descriptions.count(source.command))
- {
- source.Reply("%s", Language::Translate(source.nc, descriptions[source.command].c_str()));
- return true;
- }
- return false;
- }
-};
-
-class CSSetMisc : public Module
-{
- CommandCSSetMisc commandcssetmisc;
- Serialize::Type csmiscdata_type;
-
- public:
- CSSetMisc(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandcssetmisc(this), csmiscdata_type("CSMiscData", CSMiscData::Unserialize)
- {
- me = this;
- }
-
- ~CSSetMisc()
- {
- for (Anope::map<ExtensibleItem<CSMiscData> *>::iterator it = items.begin(); it != items.end(); ++it)
- delete it->second;
- }
-
- void OnReload(Configuration::Conf *conf) anope_override
- {
- descriptions.clear();
-
- for (int i = 0; i < conf->CountBlock("command"); ++i)
- {
- Configuration::Block *block = conf->GetBlock("command", i);
-
- if (block->Get<const Anope::string>("command") != "chanserv/set/misc")
- continue;
-
- Anope::string cname = block->Get<const Anope::string>("name");
- Anope::string desc = block->Get<const Anope::string>("misc_description");
-
- if (cname.empty() || desc.empty())
- continue;
-
- descriptions[cname] = desc;
- }
- }
-
- void OnChanInfo(CommandSource &source, ChannelInfo *ci, InfoFormatter &info, bool) anope_override
- {
- for (Anope::map<ExtensibleItem<CSMiscData> *>::iterator it = items.begin(); it != items.end(); ++it)
- {
- ExtensibleItem<CSMiscData> *e = it->second;
- MiscData *data = e->Get(ci);
-
- if (data != NULL)
- info[e->name.substr(12).replace_all_cs("_", " ")] = data->data;
- }
- }
-};
-
-MODULE_INIT(CSSetMisc)
diff --git a/modules/commands/cs_status.cpp b/modules/commands/cs_status.cpp
deleted file mode 100644
index b984d5952..000000000
--- a/modules/commands/cs_status.cpp
+++ /dev/null
@@ -1,125 +0,0 @@
-/* ChanServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class CommandCSStatus : public Command
-{
-public:
- CommandCSStatus(Module *creator) : Command(creator, "chanserv/status", 1, 2)
- {
- this->SetDesc(_("Find a user's status on a channel"));
- this->SetSyntax(_("\037channel\037 [\037user\037]"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &channel = params[0];
-
- ChannelInfo *ci = ChannelInfo::Find(channel);
- if (ci == NULL)
- source.Reply(CHAN_X_NOT_REGISTERED, channel.c_str());
- else if (!source.AccessFor(ci).HasPriv("ACCESS_CHANGE") && !source.HasPriv("chanserv/auspex"))
- source.Reply(ACCESS_DENIED);
- else
- {
- Anope::string nick = source.GetNick();
- if (params.size() > 1)
- nick = params[1];
-
- AccessGroup ag;
- User *u = User::Find(nick, true);
- NickAlias *na = NULL;
- if (u != NULL)
- ag = ci->AccessFor(u);
- else
- {
- na = NickAlias::Find(nick);
- if (na != NULL)
- ag = ci->AccessFor(na->nc);
- }
-
- if (ag.super_admin)
- source.Reply(_("\002%s\002 is a super administrator."), nick.c_str());
- else if (ag.founder)
- source.Reply(_("\002%s\002 is the founder of \002%s\002."), nick.c_str(), ci->name.c_str());
- else if (ag.empty())
- source.Reply(_("\002%s\002 has no access on \002%s\002."), nick.c_str(), ci->name.c_str());
- else
- {
- source.Reply(_("Access for \002%s\002 on \002%s\002:"), nick.c_str(), ci->name.c_str());
-
- for (unsigned i = 0; i < ag.paths.size(); ++i)
- {
- ChanAccess::Path &p = ag.paths[i];
-
- if (p.empty())
- continue;
-
- if (p.size() == 1)
- {
- ChanAccess *acc = p[0];
-
- source.Reply(_("\002%s\002 matches access entry %s, which has privilege %s."), nick.c_str(), acc->Mask().c_str(), acc->AccessSerialize().c_str());
- }
- else
- {
- ChanAccess *first = p[0];
- ChanAccess *acc = p[p.size() - 1];
-
- source.Reply(_("\002%s\002 matches access entry %s (from entry %s), which has privilege %s."), nick.c_str(), acc->Mask().c_str(), first->Mask().c_str(), acc->AccessSerialize().c_str());
- }
- }
- }
-
- for (unsigned j = 0, end = ci->GetAkickCount(); j < end; ++j)
- {
- AutoKick *autokick = ci->GetAkick(j);
-
- if (autokick->nc)
- {
- if (na && *autokick->nc == na->nc)
- source.Reply(_("\002%s\002 is on the auto kick list of \002%s\002 (%s)."), na->nc->display.c_str(), ci->name.c_str(), autokick->reason.c_str());
- }
- else if (u != NULL)
- {
- Entry akick_mask("", autokick->mask);
- if (akick_mask.Matches(u))
- source.Reply(_("\002%s\002 matches auto kick entry %s on \002%s\002 (%s)."), u->nick.c_str(), autokick->mask.c_str(), ci->name.c_str(), autokick->reason.c_str());
- }
- }
- }
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("This command tells you what a users access is on a channel\n"
- "and what access entries, if any, they match. Additionally it\n"
- "will tell you of any auto kick entries they match. Usage of\n"
- "this command is limited to users who have the ability to modify\n"
- "access entries on the channel."));
- return true;
- }
-};
-
-class CSStatus : public Module
-{
- CommandCSStatus commandcsstatus;
-
- public:
- CSStatus(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), commandcsstatus(this)
- {
- }
-};
-
-MODULE_INIT(CSStatus)
diff --git a/modules/commands/cs_suspend.cpp b/modules/commands/cs_suspend.cpp
deleted file mode 100644
index 6bab44207..000000000
--- a/modules/commands/cs_suspend.cpp
+++ /dev/null
@@ -1,285 +0,0 @@
-/* ChanServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-#include "modules/suspend.h"
-
-struct CSSuspendInfo : SuspendInfo, Serializable
-{
- CSSuspendInfo(Extensible *) : Serializable("CSSuspendInfo") { }
-
- void Serialize(Serialize::Data &data) const anope_override
- {
- data["chan"] << what;
- data["by"] << by;
- data["reason"] << reason;
- data["time"] << when;
- data["expires"] << expires;
- }
-
- static Serializable* Unserialize(Serializable *obj, Serialize::Data &data)
- {
- Anope::string schan;
- data["chan"] >> schan;
-
- CSSuspendInfo *si;
- if (obj)
- si = anope_dynamic_static_cast<CSSuspendInfo *>(obj);
- else
- {
- ChannelInfo *ci = ChannelInfo::Find(schan);
- if (!ci)
- return NULL;
- si = ci->Extend<CSSuspendInfo>("CS_SUSPENDED");
- data["chan"] >> si->what;
- }
-
- data["by"] >> si->by;
- data["reason"] >> si->reason;
- data["time"] >> si->when;
- data["expires"] >> si->expires;
- return si;
- }
-};
-
-class CommandCSSuspend : public Command
-{
- public:
- CommandCSSuspend(Module *creator) : Command(creator, "chanserv/suspend", 2, 3)
- {
- this->SetDesc(_("Prevent a channel from being used preserving channel data and settings"));
- this->SetSyntax(_("\037channel\037 [+\037expiry\037] [\037reason\037]"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &chan = params[0];
- Anope::string expiry = params[1];
- Anope::string reason = params.size() > 2 ? params[2] : "";
- time_t expiry_secs = Config->GetModule(this->owner)->Get<time_t>("expire");
-
- if (!expiry.empty() && expiry[0] != '+')
- {
- reason = expiry + " " + reason;
- reason.trim();
- expiry.clear();
- }
- else
- {
- expiry_secs = Anope::DoTime(expiry);
- if (expiry_secs == -1)
- {
- source.Reply(BAD_EXPIRY_TIME);
- return;
- }
- }
-
- if (Anope::ReadOnly)
- source.Reply(READ_ONLY_MODE);
-
- ChannelInfo *ci = ChannelInfo::Find(chan);
- if (ci == NULL)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, chan.c_str());
- return;
- }
-
- if (ci->HasExt("CS_SUSPENDED"))
- {
- source.Reply(_("\002%s\002 is already suspended."), ci->name.c_str());
- return;
- }
-
- CSSuspendInfo *si = ci->Extend<CSSuspendInfo>("CS_SUSPENDED");
- si->what = ci->name;
- si->by = source.GetNick();
- si->reason = reason;
- si->when = Anope::CurTime;
- si->expires = expiry_secs ? expiry_secs + Anope::CurTime : 0;
-
- if (ci->c)
- {
- std::vector<User *> users;
-
- for (Channel::ChanUserList::iterator it = ci->c->users.begin(), it_end = ci->c->users.end(); it != it_end; ++it)
- {
- ChanUserContainer *uc = it->second;
- User *user = uc->user;
- if (!user->HasMode("OPER") && user->server != Me)
- users.push_back(user);
- }
-
- for (unsigned i = 0; i < users.size(); ++i)
- ci->c->Kick(NULL, users[i], "%s", !reason.empty() ? reason.c_str() : Language::Translate(users[i], _("This channel has been suspended.")));
- }
-
- Log(LOG_ADMIN, source, this, ci) << "(" << (!reason.empty() ? reason : "No reason") << "), expires on " << (expiry_secs ? Anope::strftime(Anope::CurTime + expiry_secs) : "never");
- source.Reply(_("Channel \002%s\002 is now suspended."), ci->name.c_str());
-
- FOREACH_MOD(OnChanSuspend, (ci));
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Disallows anyone from using the given channel.\n"
- "May be cancelled by using the \002UNSUSPEND\002\n"
- "command to preserve all previous channel data/settings.\n"
- "If an expiry is given the channel will be unsuspended after\n"
- "that period of time, else the default expiry from the\n"
- "configuration is used.\n"
- " \n"
- "Reason may be required on certain networks."));
- return true;
- }
-};
-
-class CommandCSUnSuspend : public Command
-{
- public:
- CommandCSUnSuspend(Module *creator) : Command(creator, "chanserv/unsuspend", 1, 1)
- {
- this->SetDesc(_("Releases a suspended channel"));
- this->SetSyntax(_("\037channel\037"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
-
- if (Anope::ReadOnly)
- source.Reply(READ_ONLY_MODE);
-
- ChannelInfo *ci = ChannelInfo::Find(params[0]);
- if (ci == NULL)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
- return;
- }
-
- /* Only UNSUSPEND already suspended channels */
- CSSuspendInfo *si = ci->GetExt<CSSuspendInfo>("CS_SUSPENDED");
- if (!si)
- {
- source.Reply(_("Channel \002%s\002 isn't suspended."), ci->name.c_str());
- return;
- }
-
- Log(LOG_ADMIN, source, this, ci) << "which was suspended by " << si->by << " for: " << (!si->reason.empty() ? si->reason : "No reason");
-
- ci->Shrink<CSSuspendInfo>("CS_SUSPENDED");
-
- source.Reply(_("Channel \002%s\002 is now released."), ci->name.c_str());
-
- FOREACH_MOD(OnChanUnsuspend, (ci));
-
- return;
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Releases a suspended channel. All data and settings\n"
- "are preserved from before the suspension."));
- return true;
- }
-};
-
-class CSSuspend : public Module
-{
- CommandCSSuspend commandcssuspend;
- CommandCSUnSuspend commandcsunsuspend;
- ExtensibleItem<CSSuspendInfo> suspend;
- Serialize::Type suspend_type;
- std::vector<Anope::string> show;
-
- struct trim
- {
- Anope::string operator()(Anope::string s) const
- {
- return s.trim();
- }
- };
-
- bool Show(CommandSource &source, const Anope::string &what) const
- {
- return source.IsOper() || std::find(show.begin(), show.end(), what) != show.end();
- }
-
- public:
- CSSuspend(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandcssuspend(this), commandcsunsuspend(this), suspend(this, "CS_SUSPENDED"),
- suspend_type("CSSuspendInfo", CSSuspendInfo::Unserialize)
- {
- }
-
- void OnChanInfo(CommandSource &source, ChannelInfo *ci, InfoFormatter &info, bool show_hidden) anope_override
- {
- CSSuspendInfo *si = suspend.Get(ci);
- if (!si)
- return;
-
- if (show_hidden || Show(source, "suspended"))
- info[_("Suspended")] = _("This channel is \002suspended\002.");
- if (!si->by.empty() && (show_hidden || Show(source, "by")))
- info[_("Suspended by")] = si->by;
- if (!si->reason.empty() && (show_hidden || Show(source, "reason")))
- info[_("Suspend reason")] = si->reason;
- if (si->when && (show_hidden || Show(source, "on")))
- info[_("Suspended on")] = Anope::strftime(si->when, source.GetAccount());
- if (si->expires && (show_hidden || Show(source, "expires")))
- info[_("Suspension expires")] = Anope::strftime(si->expires, source.GetAccount());
- }
-
- void OnPreChanExpire(ChannelInfo *ci, bool &expire) anope_override
- {
- CSSuspendInfo *si = suspend.Get(ci);
- if (!si)
- return;
-
- expire = false;
-
- if (!si->expires)
- return;
-
- if (si->expires < Anope::CurTime)
- {
- ci->last_used = Anope::CurTime;
- suspend.Unset(ci);
-
- Log(this) << "Expiring suspend for " << ci->name;
- }
- }
-
- EventReturn OnCheckKick(User *u, Channel *c, Anope::string &mask, Anope::string &reason) anope_override
- {
- if (u->HasMode("OPER") || !c->ci || !suspend.HasExt(c->ci))
- return EVENT_CONTINUE;
-
- reason = Language::Translate(u, _("This channel may not be used."));
- return EVENT_STOP;
- }
-
- EventReturn OnChanDrop(CommandSource &source, ChannelInfo *ci) anope_override
- {
- CSSuspendInfo *si = suspend.Get(ci);
- if (si && !source.HasCommand("chanserv/drop"))
- {
- source.Reply(CHAN_X_SUSPENDED, ci->name.c_str());
- return EVENT_STOP;
- }
-
- return EVENT_CONTINUE;
- }
-};
-
-MODULE_INIT(CSSuspend)
diff --git a/modules/commands/cs_sync.cpp b/modules/commands/cs_sync.cpp
deleted file mode 100644
index a60e24cab..000000000
--- a/modules/commands/cs_sync.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class CommandCSSync : public Command
-{
- public:
- CommandCSSync(Module *creator) : Command(creator, "chanserv/sync", 1, 1)
- {
- this->SetDesc(_("Sync users channel modes"));
- this->SetSyntax(_("\037channel\037"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- ChannelInfo *ci = ChannelInfo::Find(params[0]);
-
- if (ci == NULL)
- source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
- else if (ci->c == NULL)
- source.Reply(CHAN_X_NOT_IN_USE, params[0].c_str());
- else if (!source.AccessFor(ci).HasPriv("ACCESS_CHANGE") && !source.HasPriv("chanserv/administration"))
- source.Reply(ACCESS_DENIED);
- else
- {
- bool override = !source.AccessFor(ci).HasPriv("ACCESS_CHANGE") && source.HasPriv("chanserv/administration");
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci);
-
- for (Channel::ChanUserList::iterator it = ci->c->users.begin(), it_end = ci->c->users.end(); it != it_end; ++it)
- ci->c->SetCorrectModes(it->second->user, true);
-
- source.Reply(_("All user modes on \002%s\002 have been synced."), ci->name.c_str());
- }
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &params) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Syncs all modes set on users on the channel with the modes\n"
- "they should have based on their access."));
- return true;
- }
-};
-
-class CSSync : public Module
-{
- CommandCSSync commandcssync;
- public:
- CSSync(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandcssync(this)
- {
-
- }
-};
-
-MODULE_INIT(CSSync)
diff --git a/modules/commands/cs_topic.cpp b/modules/commands/cs_topic.cpp
deleted file mode 100644
index 2db37a456..000000000
--- a/modules/commands/cs_topic.cpp
+++ /dev/null
@@ -1,270 +0,0 @@
-/* ChanServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-#include "modules/cs_mode.h"
-
-class CommandCSSetKeepTopic : public Command
-{
- public:
- CommandCSSetKeepTopic(Module *creator, const Anope::string &cname = "chanserv/set/keeptopic") : Command(creator, cname, 2, 2)
- {
- this->SetDesc(_("Retain topic when channel is not in use"));
- this->SetSyntax(_("\037channel\037 {ON | OFF}"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- ChannelInfo *ci = ChannelInfo::Find(params[0]);
- if (ci == NULL)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
- return;
- }
-
- EventReturn MOD_RESULT;
- FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, params[1]));
- if (MOD_RESULT == EVENT_STOP)
- return;
-
- if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- if (params[1].equals_ci("ON"))
- {
- Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable keeptopic";
- ci->Extend<bool>("KEEPTOPIC");
- source.Reply(_("Topic retention option for %s is now \002on\002."), ci->name.c_str());
- }
- else if (params[1].equals_ci("OFF"))
- {
- Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable keeptopic";
- ci->Shrink<bool>("KEEPTOPIC");
- source.Reply(_("Topic retention option for %s is now \002off\002."), ci->name.c_str());
- }
- else
- this->OnSyntaxError(source, "KEEPTOPIC");
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Enables or disables the \002topic retention\002 option for a\n"
- "channel. When \002%s\002 is set, the topic for the\n"
- "channel will be remembered by %s even after the\n"
- "last user leaves the channel, and will be restored the\n"
- "next time the channel is created."), source.command.c_str(), source.service->nick.c_str());
- return true;
- }
-};
-
-class CommandCSTopic : public Command
-{
- ExtensibleRef<bool> topiclock;
-
- void Lock(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
- {
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- EventReturn MOD_RESULT;
- FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, "topiclock on"));
- if (MOD_RESULT == EVENT_STOP)
- return;
-
- topiclock->Set(ci, true);
- source.Reply(_("Topic lock option for %s is now \002on\002."), ci->name.c_str());
- }
-
- void Unlock(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
- {
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- EventReturn MOD_RESULT;
- FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, "topiclock off"));
- if (MOD_RESULT == EVENT_STOP)
- return;
-
- topiclock->Unset(ci);
- source.Reply(_("Topic lock option for %s is now \002off\002."), ci->name.c_str());
- }
-
- void Set(CommandSource &source, ChannelInfo *ci, const Anope::string &topic)
- {
- bool has_topiclock = topiclock->HasExt(ci);
- topiclock->Unset(ci);
- ci->c->ChangeTopic(source.GetNick(), topic, Anope::CurTime);
- if (has_topiclock)
- topiclock->Set(ci);
-
- bool override = !source.AccessFor(ci).HasPriv("TOPIC");
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << (!topic.empty() ? "to change the topic to: " : "to unset the topic") << (!topic.empty() ? topic : "");
- }
-
- void Append(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
- {
- const Anope::string &topic = params[2];
-
- Anope::string new_topic;
- if (!ci->c->topic.empty())
- {
- new_topic = ci->c->topic + " " + topic;
- ci->last_topic.clear();
- }
- else
- new_topic = topic;
-
- this->Set(source, ci, new_topic);
- }
-
- public:
- CommandCSTopic(Module *creator) : Command(creator, "chanserv/topic", 2, 3),
- topiclock("TOPICLOCK")
- {
- this->SetDesc(_("Manipulate the topic of the specified channel"));
- this->SetSyntax(_("\037channel\037 [SET] [\037topic\037]"));
- this->SetSyntax(_("\037channel\037 APPEND \037topic\037"));
- this->SetSyntax(_("\037channel\037 [UNLOCK|LOCK]"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &subcmd = params[1];
-
- ChannelInfo *ci = ChannelInfo::Find(params[0]);
- if (ci == NULL)
- source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
- else if (!source.AccessFor(ci).HasPriv("TOPIC") && !source.HasCommand("chanserv/topic"))
- source.Reply(ACCESS_DENIED);
- else if (subcmd.equals_ci("LOCK"))
- this->Lock(source, ci, params);
- else if (subcmd.equals_ci("UNLOCK"))
- this->Unlock(source, ci, params);
- else if (!ci->c)
- source.Reply(CHAN_X_NOT_IN_USE, ci->name.c_str());
- else if (subcmd.equals_ci("APPEND") && params.size() > 2)
- this->Append(source, ci, params);
- else
- {
- Anope::string topic;
- if (subcmd.equals_ci("SET"))
- {
- topic = params.size() > 2 ? params[2] : "";
- }
- else
- {
- topic = subcmd;
- if (params.size() > 2)
- topic += " " + params[2];
- }
- this->Set(source, ci, topic);
- }
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Allows manipulating the topic of the specified channel.\n"
- "The \002SET\002 command changes the topic of the channel to the given topic\n"
- "or unsets the topic if no topic is given. The \002APPEND\002 command appends\n"
- "the given topic to the existing topic.\n"
- " \n"
- "\002LOCK\002 and \002UNLOCK\002 may be used to enable and disable topic lock. When\n"
- "topic lock is set, the channel topic will be unchangeable by users who do not have\n"
- "the \002TOPIC\002 privilege."));
- return true;
- }
-};
-
-class CSTopic : public Module
-{
- CommandCSTopic commandcstopic;
- CommandCSSetKeepTopic commandcssetkeeptopic;
-
- SerializableExtensibleItem<bool> topiclock, keeptopic;
-
- public:
- CSTopic(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandcstopic(this), commandcssetkeeptopic(this), topiclock(this, "TOPICLOCK"), keeptopic(this, "KEEPTOPIC")
- {
-
- }
-
- void OnChannelSync(Channel *c) anope_override
- {
- if (c->ci)
- {
- /* Update channel topic */
- if ((topiclock.HasExt(c->ci) || keeptopic.HasExt(c->ci)) && c->ci->last_topic != c->topic)
- {
- c->ChangeTopic(!c->ci->last_topic_setter.empty() ? c->ci->last_topic_setter : c->ci->WhoSends()->nick, c->ci->last_topic, c->ci->last_topic_time ? c->ci->last_topic_time : Anope::CurTime);
- }
- }
- }
-
- void OnTopicUpdated(User *source, Channel *c, const Anope::string &user, const Anope::string &topic) anope_override
- {
- if (!c->ci)
- return;
-
- /* We only compare the topics here, not the time or setter. This is because some (old) IRCds do not
- * allow us to set the topic as someone else, meaning we have to bump the TS and change the setter to us.
- * This desyncs what is really set with what we have stored, and we end up resetting the topic often when
- * it is not required
- */
- if (topiclock.HasExt(c->ci) && c->ci->last_topic != c->topic && (!source || !c->ci->AccessFor(source).HasPriv("TOPIC")))
- {
- c->ChangeTopic(c->ci->last_topic_setter, c->ci->last_topic, c->ci->last_topic_time);
- }
- else
- {
- c->ci->last_topic = c->topic;
- c->ci->last_topic_setter = c->topic_setter;
- c->ci->last_topic_time = c->topic_ts;
- }
- }
-
- void OnChanInfo(CommandSource &source, ChannelInfo *ci, InfoFormatter &info, bool show_all) anope_override
- {
- if (keeptopic.HasExt(ci))
- info.AddOption(_("Topic retention"));
- if (topiclock.HasExt(ci))
- info.AddOption(_("Topic lock"));
-
- ModeLocks *ml = ci->GetExt<ModeLocks>("modelocks");
- const ModeLock *secret = ml ? ml->GetMLock("SECRET") : NULL;
- if (!ci->last_topic.empty() && (show_all || ((!secret || secret->set == false) && (!ci->c || !ci->c->HasMode("SECRET")))))
- {
- info[_("Last topic")] = ci->last_topic;
- info[_("Topic set by")] = ci->last_topic_setter;
- }
- }
-};
-
-MODULE_INIT(CSTopic)
diff --git a/modules/commands/cs_xop.cpp b/modules/commands/cs_xop.cpp
deleted file mode 100644
index 3a6c4d484..000000000
--- a/modules/commands/cs_xop.cpp
+++ /dev/null
@@ -1,635 +0,0 @@
-/* ChanServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-namespace
-{
- std::vector<Anope::string> order;
- std::map<Anope::string, std::vector<Anope::string> > permissions;
-}
-
-class XOPChanAccess : public ChanAccess
-{
- public:
- Anope::string type;
-
- XOPChanAccess(AccessProvider *p) : ChanAccess(p)
- {
- }
-
- bool HasPriv(const Anope::string &priv) const anope_override
- {
- for (std::vector<Anope::string>::iterator it = std::find(order.begin(), order.end(), this->type); it != order.end(); ++it)
- {
- const std::vector<Anope::string> &privs = permissions[*it];
- if (std::find(privs.begin(), privs.end(), priv) != privs.end())
- return true;
- }
- return false;
- }
-
- Anope::string AccessSerialize() const
- {
- return this->type;
- }
-
- void AccessUnserialize(const Anope::string &data) anope_override
- {
- this->type = data;
- }
-
- static Anope::string DetermineLevel(const ChanAccess *access)
- {
- if (access->provider->name == "access/xop")
- {
- const XOPChanAccess *xaccess = anope_dynamic_static_cast<const XOPChanAccess *>(access);
- return xaccess->type;
- }
- else
- {
- std::map<Anope::string, int> count;
-
- for (std::map<Anope::string, std::vector<Anope::string> >::const_iterator it = permissions.begin(), it_end = permissions.end(); it != it_end; ++it)
- {
- int &c = count[it->first];
- const std::vector<Anope::string> &perms = it->second;
- for (unsigned i = 0; i < perms.size(); ++i)
- if (access->HasPriv(perms[i]))
- ++c;
- }
-
- Anope::string max;
- int maxn = 0;
- for (std::map<Anope::string, int>::iterator it = count.begin(), it_end = count.end(); it != it_end; ++it)
- if (it->second > maxn)
- {
- maxn = it->second;
- max = it->first;
- }
-
- return max;
- }
- }
-};
-
-class XOPAccessProvider : public AccessProvider
-{
- public:
- XOPAccessProvider(Module *o) : AccessProvider(o, "access/xop")
- {
- }
-
- ChanAccess *Create() anope_override
- {
- return new XOPChanAccess(this);
- }
-};
-
-class CommandCSXOP : public Command
-{
- private:
- void DoAdd(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
- {
- Anope::string mask = params.size() > 2 ? params[2] : "";
-
- if (mask.empty())
- {
- this->OnSyntaxError(source, "ADD");
- return;
- }
-
- if (Anope::ReadOnly)
- {
- source.Reply(_("Sorry, channel %s list modification is temporarily disabled."), source.command.c_str());
- return;
- }
-
- XOPChanAccess tmp_access(NULL);
- tmp_access.ci = ci;
- tmp_access.type = source.command.upper();
-
- AccessGroup access = source.AccessFor(ci);
- const ChanAccess *highest = access.Highest();
- bool override = false;
- const NickAlias *na = NULL;
-
- std::vector<Anope::string>::iterator cmd_it = std::find(order.begin(), order.end(), source.command.upper()),
- access_it = highest ? std::find(order.begin(), order.end(), XOPChanAccess::DetermineLevel(highest)) : order.end();
-
- if (!access.founder && (!access.HasPriv("ACCESS_CHANGE") || cmd_it <= access_it))
- {
- if (source.HasPriv("chanserv/access/modify"))
- override = true;
- else
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
- }
-
- if (IRCD->IsChannelValid(mask))
- {
- if (Config->GetModule("chanserv")->Get<bool>("disallow_channel_access"))
- {
- source.Reply(_("Channels may not be on access lists."));
- return;
- }
-
- ChannelInfo *targ_ci = ChannelInfo::Find(mask);
- if (targ_ci == NULL)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, mask.c_str());
- return;
- }
- else if (ci == targ_ci)
- {
- source.Reply(_("You can't add a channel to its own access list."));
- return;
- }
-
- mask = targ_ci->name;
- }
- else
- {
- na = NickAlias::Find(mask);
- if (!na && Config->GetModule("chanserv")->Get<bool>("disallow_hostmask_access"))
- {
- source.Reply(_("Masks and unregistered users may not be on access lists."));
- return;
- }
- else if (mask.find_first_of("!*@") == Anope::string::npos && !na)
- {
- User *targ = User::Find(mask, true);
- if (targ != NULL)
- mask = "*!*@" + targ->GetDisplayedHost();
- else
- {
- source.Reply(NICK_X_NOT_REGISTERED, mask.c_str());
- return;
- }
- }
-
- if (na)
- mask = na->nick;
- }
-
- for (unsigned i = 0; i < ci->GetAccessCount(); ++i)
- {
- const ChanAccess *a = ci->GetAccess(i);
-
- if ((na && na->nc == a->GetAccount()) || mask.equals_ci(a->Mask()))
- {
- if ((!highest || *a >= *highest) && !access.founder && !source.HasPriv("chanserv/access/modify"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- delete ci->EraseAccess(i);
- break;
- }
- }
-
- unsigned access_max = Config->GetModule("chanserv")->Get<unsigned>("accessmax", "1024");
- if (access_max && ci->GetDeepAccessCount() >= access_max)
- {
- source.Reply(_("Sorry, you can only have %d access entries on a channel, including access entries from other channels."), access_max);
- return;
- }
-
- ServiceReference<AccessProvider> provider("AccessProvider", "access/xop");
- if (!provider)
- return;
- XOPChanAccess *acc = anope_dynamic_static_cast<XOPChanAccess *>(provider->Create());
- acc->SetMask(mask, ci);
- acc->creator = source.GetNick();
- acc->type = source.command.upper();
- acc->last_seen = 0;
- acc->created = Anope::CurTime;
- ci->AddAccess(acc);
-
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to add " << mask;
-
- FOREACH_MOD(OnAccessAdd, (ci, source, acc));
- source.Reply(_("\002%s\002 added to %s %s list."), acc->Mask().c_str(), ci->name.c_str(), source.command.c_str());
- }
-
- void DoDel(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
- {
- NickCore *nc = source.nc;
- Anope::string mask = params.size() > 2 ? params[2] : "";
-
- if (mask.empty())
- {
- this->OnSyntaxError(source, "DEL");
- return;
- }
-
- if (Anope::ReadOnly)
- {
- source.Reply(_("Sorry, channel %s list modification is temporarily disabled."), source.command.c_str());
- return;
- }
-
- if (!ci->GetAccessCount())
- {
- source.Reply(_("%s %s list is empty."), ci->name.c_str(), source.command.c_str());
- return;
- }
-
- XOPChanAccess tmp_access(NULL);
- tmp_access.ci = ci;
- tmp_access.type = source.command.upper();
-
- AccessGroup access = source.AccessFor(ci);
- const ChanAccess *highest = access.Highest();
- bool override = false;
-
- if (!isdigit(mask[0]) && mask.find_first_of("#!*@") == Anope::string::npos && !NickAlias::Find(mask))
- {
- User *targ = User::Find(mask, true);
- if (targ != NULL)
- mask = "*!*@" + targ->GetDisplayedHost();
- else
- {
- source.Reply(NICK_X_NOT_REGISTERED, mask.c_str());
- return;
- }
- }
-
- std::vector<Anope::string>::iterator cmd_it = std::find(order.begin(), order.end(), source.command.upper()),
- access_it = highest ? std::find(order.begin(), order.end(), XOPChanAccess::DetermineLevel(highest)) : order.end();
-
- if (!mask.equals_ci(nc->display) && !access.founder && (!access.HasPriv("ACCESS_CHANGE") || cmd_it <= access_it))
- {
- if (source.HasPriv("chanserv/access/modify"))
- override = true;
- else
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
- }
-
- /* Special case: is it a number/list? Only do search if it isn't. */
- if (isdigit(mask[0]) && mask.find_first_not_of("1234567890,-") == Anope::string::npos)
- {
- class XOPDelCallback : public NumberList
- {
- CommandSource &source;
- ChannelInfo *ci;
- Command *c;
- unsigned deleted;
- Anope::string nicks;
- bool override;
- public:
- XOPDelCallback(CommandSource &_source, ChannelInfo *_ci, Command *_c, bool _override, const Anope::string &numlist) : NumberList(numlist, true), source(_source), ci(_ci), c(_c), deleted(0), override(_override)
- {
- }
-
- ~XOPDelCallback()
- {
- if (!deleted)
- source.Reply(_("No matching entries on %s %s list."), ci->name.c_str(), source.command.c_str());
- else
- {
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, c, ci) << "to delete " << nicks;
-
- if (deleted == 1)
- source.Reply(_("Deleted one entry from %s %s list."), ci->name.c_str(), source.command.c_str());
- else
- source.Reply(_("Deleted %d entries from %s %s list."), deleted, ci->name.c_str(), source.command.c_str());
- }
- }
-
- void HandleNumber(unsigned number) anope_override
- {
- if (!number || number > ci->GetAccessCount())
- return;
-
- ChanAccess *caccess = ci->GetAccess(number - 1);
-
- if (caccess->provider->name != "access/xop" || this->source.command.upper() != caccess->AccessSerialize())
- return;
-
- ++deleted;
- if (!nicks.empty())
- nicks += ", " + caccess->Mask();
- else
- nicks = caccess->Mask();
-
- ci->EraseAccess(number - 1);
- FOREACH_MOD(OnAccessDel, (ci, source, caccess));
- delete caccess;
- }
- }
- delcallback(source, ci, this, override, mask);
- delcallback.Process();
- }
- else
- {
- for (unsigned i = 0; i < ci->GetAccessCount(); ++i)
- {
- ChanAccess *a = ci->GetAccess(i);
-
- if (a->provider->name != "access/xop" || source.command.upper() != a->AccessSerialize())
- continue;
-
- if (a->Mask().equals_ci(mask))
- {
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to delete " << a->Mask();
-
- source.Reply(_("\002%s\002 deleted from %s %s list."), a->Mask().c_str(), ci->name.c_str(), source.command.c_str());
-
- ci->EraseAccess(i);
- FOREACH_MOD(OnAccessDel, (ci, source, a));
- delete a;
-
- return;
- }
- }
-
- source.Reply(_("\002%s\002 not found on %s %s list."), mask.c_str(), ci->name.c_str(), source.command.c_str());
- }
- }
-
- void DoList(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
- {
-
- const Anope::string &nick = params.size() > 2 ? params[2] : "";
-
- AccessGroup access = source.AccessFor(ci);
-
- if (!access.HasPriv("ACCESS_LIST") && !source.HasPriv("chanserv/access/list"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- if (!ci->GetAccessCount())
- {
- source.Reply(_("%s %s list is empty."), ci->name.c_str(), source.command.c_str());
- return;
- }
-
- ListFormatter list(source.GetAccount());
- list.AddColumn(_("Number")).AddColumn(_("Mask"));
-
- if (!nick.empty() && nick.find_first_not_of("1234567890,-") == Anope::string::npos)
- {
- class XOPListCallback : public NumberList
- {
- ListFormatter &list;
- ChannelInfo *ci;
- CommandSource &source;
- public:
- XOPListCallback(ListFormatter &_list, ChannelInfo *_ci, const Anope::string &numlist, CommandSource &src) : NumberList(numlist, false), list(_list), ci(_ci), source(src)
- {
- }
-
- void HandleNumber(unsigned Number) anope_override
- {
- if (!Number || Number > ci->GetAccessCount())
- return;
-
- const ChanAccess *a = ci->GetAccess(Number - 1);
-
- if (a->provider->name != "access/xop" || this->source.command.upper() != a->AccessSerialize())
- return;
-
- ListFormatter::ListEntry entry;
- entry["Number"] = stringify(Number);
- entry["Mask"] = a->Mask();
- this->list.AddEntry(entry);
- }
- } nl_list(list, ci, nick, source);
- nl_list.Process();
- }
- else
- {
- for (unsigned i = 0, end = ci->GetAccessCount(); i < end; ++i)
- {
- const ChanAccess *a = ci->GetAccess(i);
-
- if (a->provider->name != "access/xop" || source.command.upper() != a->AccessSerialize())
- continue;
- else if (!nick.empty() && !Anope::Match(a->Mask(), nick))
- continue;
-
- ListFormatter::ListEntry entry;
- entry["Number"] = stringify(i + 1);
- entry["Mask"] = a->Mask();
- list.AddEntry(entry);
- }
- }
-
- if (list.IsEmpty())
- source.Reply(_("No matching entries on %s access list."), ci->name.c_str());
- else
- {
- std::vector<Anope::string> replies;
- list.Process(replies);
-
- source.Reply(_("%s list for %s"), source.command.c_str(), ci->name.c_str());
- for (unsigned i = 0; i < replies.size(); ++i)
- source.Reply(replies[i]);
- }
- }
-
- void DoClear(CommandSource &source, ChannelInfo *ci)
- {
- if (Anope::ReadOnly)
- {
- source.Reply(_("Sorry, channel %s list modification is temporarily disabled."), source.command.c_str());
- return;
- }
-
- if (!ci->GetAccessCount())
- {
- source.Reply(_("%s %s list is empty."), ci->name.c_str(), source.command.c_str());
- return;
- }
-
- if (!source.AccessFor(ci).HasPriv("FOUNDER") && !source.HasPriv("chanserv/access/modify"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- bool override = !source.AccessFor(ci).HasPriv("FOUNDER");
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to clear the access list";
-
- for (unsigned i = ci->GetAccessCount(); i > 0; --i)
- {
- const ChanAccess *access = ci->GetAccess(i - 1);
-
- if (access->provider->name != "access/xop" || source.command.upper() != access->AccessSerialize())
- continue;
-
- delete ci->EraseAccess(i - 1);
- }
-
- FOREACH_MOD(OnAccessClear, (ci, source));
-
- source.Reply(_("Channel %s %s list has been cleared."), ci->name.c_str(), source.command.c_str());
- }
-
- public:
- CommandCSXOP(Module *modname) : Command(modname, "chanserv/xop", 2, 4)
- {
- this->SetSyntax(_("\037channel\037 ADD \037mask\037"));
- this->SetSyntax(_("\037channel\037 DEL {\037mask\037 | \037entry-num\037 | \037list\037}"));
- this->SetSyntax(_("\037channel\037 LIST [\037mask\037 | \037list\037]"));
- this->SetSyntax(_("\037channel\037 CLEAR"));
- }
-
- const Anope::string GetDesc(CommandSource &source) const anope_override
- {
- return Anope::printf(Language::Translate(source.GetAccount(), _("Modify the list of %s users")), source.command.upper().c_str());
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params)
- {
- ChannelInfo *ci = ChannelInfo::Find(params[0]);
- if (ci == NULL)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
- return;
- }
-
- const Anope::string &cmd = params[1];
-
- if (cmd.equals_ci("ADD"))
- return this->DoAdd(source, ci, params);
- else if (cmd.equals_ci("DEL"))
- return this->DoDel(source, ci, params);
- else if (cmd.equals_ci("LIST"))
- return this->DoList(source, ci, params);
- else if (cmd.equals_ci("CLEAR"))
- return this->DoClear(source, ci);
- else
- this->OnSyntaxError(source, "");
- }
-
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- const Anope::string &cmd = source.command.upper();
-
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Maintains the \002%s list\002 for a channel. Users who match an access entry\n"
- "on the %s list receive the following privileges:\n"
- " "), cmd.c_str(), cmd.c_str());
-
- Anope::string buf;
- for (unsigned i = 0; i < permissions[cmd].size(); ++i)
- {
- buf += ", " + permissions[cmd][i];
- if (buf.length() > 75)
- {
- source.Reply(" %s\n", buf.substr(2).c_str());
- buf.clear();
- }
- }
- if (!buf.empty())
- {
- source.Reply(" %s\n", buf.substr(2).c_str());
- buf.clear();
- }
-
- source.Reply(_(" \n"
- "The \002%s ADD\002 command adds the given nickname to the\n"
- "%s list.\n"
- " \n"
- "The \002%s DEL\002 command removes the given nick from the\n"
- "%s list. If a list of entry numbers is given, those\n"
- "entries are deleted. (See the example for LIST below.)\n"
- " \n"
- "The \002%s LIST\002 command displays the %s list. If\n"
- "a wildcard mask is given, only those entries matching the\n"
- "mask are displayed. If a list of entry numbers is given,\n"
- "only those entries are shown; for example:\n"
- " \002%s #channel LIST 2-5,7-9\002\n"
- " Lists %s entries numbered 2 through 5 and\n"
- " 7 through 9.\n"
- " \n"
- "The \002%s CLEAR\002 command clears all entries of the\n"
- "%s list."), cmd.c_str(), cmd.c_str(), cmd.c_str(), cmd.c_str(),
- cmd.c_str(), cmd.c_str(), cmd.c_str(), cmd.c_str(), cmd.c_str(), cmd.c_str());
- BotInfo *access_bi, *flags_bi;
- Anope::string access_cmd, flags_cmd;
- Command::FindCommandFromService("chanserv/access", access_bi, access_cmd);
- Command::FindCommandFromService("chanserv/flags", flags_bi, access_cmd);
- if (!access_cmd.empty() || !flags_cmd.empty())
- {
- source.Reply(_("Alternative methods of modifying channel access lists are\n"
- "available. "));
- if (!access_cmd.empty())
- source.Reply(_("See \002%s%s HELP %s\002 for more information\n"
- "about the access list."), Config->StrictPrivmsg.c_str(), access_bi->nick.c_str(), access_cmd.c_str());
- if (!flags_cmd.empty())
- source.Reply(_("See \002%s%s HELP %s\002 for more information\n"
- "about the flags system."), Config->StrictPrivmsg.c_str(), flags_bi->nick.c_str(), flags_cmd.c_str());
- }
- return true;
- }
-};
-
-class CSXOP : public Module
-{
- XOPAccessProvider accessprovider;
- CommandCSXOP commandcsxop;
-
- public:
- CSXOP(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- accessprovider(this), commandcsxop(this)
- {
- this->SetPermanent(true);
-
- }
-
- void OnReload(Configuration::Conf *conf) anope_override
- {
- order.clear();
- permissions.clear();
-
- for (int i = 0; i < conf->CountBlock("privilege"); ++i)
- {
- Configuration::Block *block = conf->GetBlock("privilege", i);
- const Anope::string &pname = block->Get<const Anope::string>("name");
-
- Privilege *p = PrivilegeManager::FindPrivilege(pname);
- if (p == NULL)
- continue;
-
- const Anope::string &xop = block->Get<const Anope::string>("xop");
- if (pname.empty() || xop.empty())
- continue;
-
- permissions[xop].push_back(pname);
- }
-
- for (int i = 0; i < conf->CountBlock("command"); ++i)
- {
- Configuration::Block *block = conf->GetBlock("command", i);
- const Anope::string &cname = block->Get<const Anope::string>("name"),
- &cserv = block->Get<const Anope::string>("command");
- if (cname.empty() || cserv != "chanserv/xop")
- continue;
-
- order.push_back(cname);
- }
- }
-};
-
-MODULE_INIT(CSXOP)
diff --git a/modules/commands/gl_global.cpp b/modules/commands/gl_global.cpp
deleted file mode 100644
index dcc7c4780..000000000
--- a/modules/commands/gl_global.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-/* Global core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class CommandGLGlobal : public Command
-{
- ServiceReference<GlobalService> GService;
-
- public:
- CommandGLGlobal(Module *creator) : Command(creator, "global/global", 1, 1), GService("GlobalService", "Global")
- {
- this->SetDesc(_("Send a message to all users"));
- this->SetSyntax(_("\037message\037"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &msg = params[0];
-
- if (!GService)
- source.Reply("No global reference, is global loaded?");
- else
- {
- Log(LOG_ADMIN, source, this);
- GService->SendGlobal(NULL, source.GetNick(), msg);
- }
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Allows Administrators to send messages to all users on the\n"
- "network. The message will be sent from the nick \002%s\002."), source.service->nick.c_str());
- return true;
- }
-};
-
-class GLGlobal : public Module
-{
- CommandGLGlobal commandglglobal;
-
- public:
- GLGlobal(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandglglobal(this)
- {
-
- }
-};
-
-MODULE_INIT(GLGlobal)
diff --git a/modules/commands/greet.cpp b/modules/commands/greet.cpp
deleted file mode 100644
index 0f61a1a6e..000000000
--- a/modules/commands/greet.cpp
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class CommandBSSetGreet : public Command
-{
- public:
- CommandBSSetGreet(Module *creator, const Anope::string &sname = "botserv/set/greet") : Command(creator, sname, 2, 2)
- {
- this->SetDesc(_("Enable greet messages"));
- this->SetSyntax(_("\037channel\037 {\037ON|OFF\037}"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- ChannelInfo *ci = ChannelInfo::Find(params[0]);
- const Anope::string &value = params[1];
-
- if (ci == NULL)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
- return;
- }
-
- if (!source.HasPriv("botserv/administration") && !source.AccessFor(ci).HasPriv("SET"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- if (value.equals_ci("ON"))
- {
- bool override = !source.AccessFor(ci).HasPriv("SET");
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to enable greets";
-
- ci->Extend<bool>("BS_GREET");
- source.Reply(_("Greet mode is now \002on\002 on channel %s."), ci->name.c_str());
- }
- else if (value.equals_ci("OFF"))
- {
- bool override = !source.AccessFor(ci).HasPriv("SET");
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to disable greets";
-
- ci->Shrink<bool>("BS_GREET");
- source.Reply(_("Greet mode is now \002off\002 on channel %s."), ci->name.c_str());
- }
- else
- this->OnSyntaxError(source, source.command);
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
- {
- this->SendSyntax(source);
- source.Reply(_(" \n"
- "Enables or disables \002greet\002 mode on a channel.\n"
- "When it is enabled, the bot will display greet\n"
- "messages of users joining the channel, provided\n"
- "they have enough access to the channel."));
- return true;
- }
-};
-
-class CommandNSSetGreet : public Command
-{
- public:
- CommandNSSetGreet(Module *creator, const Anope::string &sname = "nickserv/set/greet", size_t min = 0) : Command(creator, sname, min, min + 1)
- {
- this->SetDesc(_("Associate a greet message with your nickname"));
- this->SetSyntax(_("\037message\037"));
- }
-
- void Run(CommandSource &source, const Anope::string &user, const Anope::string &param)
- {
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- const NickAlias *na = NickAlias::Find(user);
- if (!na)
- {
- source.Reply(NICK_X_NOT_REGISTERED, user.c_str());
- return;
- }
- NickCore *nc = na->nc;
-
- EventReturn MOD_RESULT;
- FOREACH_RESULT(OnSetNickOption, MOD_RESULT, (source, this, nc, param));
- if (MOD_RESULT == EVENT_STOP)
- return;
-
- if (!param.empty())
- {
- Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to change the greet of " << nc->display;
- nc->Extend<Anope::string>("greet", param);
- source.Reply(_("Greet message for \002%s\002 changed to \002%s\002."), nc->display.c_str(), param.c_str());
- }
- else
- {
- Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to unset the greet of " << nc->display;
- nc->Shrink<Anope::string>("greet");
- source.Reply(_("Greet message for \002%s\002 unset."), nc->display.c_str());
- }
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- this->Run(source, source.nc->display, params.size() > 0 ? params[0] : "");
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Makes the given message the greet of your nickname, that\n"
- "will be displayed when joining a channel that has GREET\n"
- "option enabled, provided that you have the necessary\n"
- "access on it."));
- return true;
- }
-};
-
-class CommandNSSASetGreet : public CommandNSSetGreet
-{
- public:
- CommandNSSASetGreet(Module *creator) : CommandNSSetGreet(creator, "nickserv/saset/greet", 1)
- {
- this->ClearSyntax();
- this->SetSyntax(_("\037nickname\037 \037message\037"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- this->Run(source, params[0], params.size() > 1 ? params[1] : "");
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Makes the given message the greet of the nickname, that\n"
- "will be displayed when joining a channel that has GREET\n"
- "option enabled, provided that the user has the necessary\n"
- "access on it."));
- return true;
- }
-};
-
-class Greet : public Module
-{
- /* channel setting for whether or not greet should be shown */
- SerializableExtensibleItem<bool> bs_greet;
- /* user greets */
- SerializableExtensibleItem<Anope::string> ns_greet;
-
- CommandBSSetGreet commandbssetgreet;
- CommandNSSetGreet commandnssetgreet;
- CommandNSSASetGreet commandnssasetgreet;
-
- public:
- Greet(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- bs_greet(this, "BS_GREET"),
- ns_greet(this, "greet"),
- commandbssetgreet(this),
- commandnssetgreet(this), commandnssasetgreet(this)
- {
- }
-
- void OnJoinChannel(User *user, Channel *c) anope_override
- {
- /* Only display the greet if the main uplink we're connected
- * to has synced, or we'll get greet-floods when the net
- * recovers from a netsplit. -GD
- */
- if (!c->ci || !c->ci->bi || !user->server->IsSynced() || !user->Account())
- return;
-
- Anope::string *greet = ns_greet.Get(user->Account());
- if (bs_greet.HasExt(c->ci) && greet != NULL && !greet->empty() && c->FindUser(c->ci->bi) && c->ci->AccessFor(user).HasPriv("GREET"))
- {
- IRCD->SendPrivmsg(*c->ci->bi, c->name, "[%s] %s", user->Account()->display.c_str(), greet->c_str());
- c->ci->bi->lastmsg = Anope::CurTime;
- }
- }
-
- void OnNickInfo(CommandSource &source, NickAlias *na, InfoFormatter &info, bool show_hidden) anope_override
- {
- Anope::string *greet = ns_greet.Get(na->nc);
- if (greet != NULL)
- info[_("Greet")] = *greet;
- }
-
- void OnBotInfo(CommandSource &source, BotInfo *bi, ChannelInfo *ci, InfoFormatter &info) anope_override
- {
- if (bs_greet.HasExt(ci))
- info.AddOption(_("Greet"));
- }
-};
-
-MODULE_INIT(Greet)
diff --git a/modules/commands/hs_del.cpp b/modules/commands/hs_del.cpp
deleted file mode 100644
index 02b2a1324..000000000
--- a/modules/commands/hs_del.cpp
+++ /dev/null
@@ -1,113 +0,0 @@
-/* HostServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class CommandHSDel : public Command
-{
- public:
- CommandHSDel(Module *creator) : Command(creator, "hostserv/del", 1, 1)
- {
- this->SetDesc(_("Delete the vhost of another user"));
- this->SetSyntax(_("\037nick\037"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- const Anope::string &nick = params[0];
- NickAlias *na = NickAlias::Find(nick);
- if (na)
- {
- Log(LOG_ADMIN, source, this) << "for user " << na->nick;
- FOREACH_MOD(OnDeleteVhost, (na));
- na->RemoveVhost();
- source.Reply(_("Vhost for \002%s\002 removed."), nick.c_str());
- }
- else
- source.Reply(NICK_X_NOT_REGISTERED, nick.c_str());
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Deletes the vhost assigned to the given nick from the\n"
- "database."));
- return true;
- }
-};
-
-class CommandHSDelAll : public Command
-{
- public:
- CommandHSDelAll(Module *creator) : Command(creator, "hostserv/delall", 1, 1)
- {
- this->SetDesc(_("Deletes the vhost for all nicks in a group"));
- this->SetSyntax(_("\037nick\037"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- const Anope::string &nick = params[0];
- NickAlias *na = NickAlias::Find(nick);
- if (na)
- {
- FOREACH_MOD(OnDeleteVhost, (na));
- const NickCore *nc = na->nc;
- for (unsigned i = 0; i < nc->aliases->size(); ++i)
- {
- na = nc->aliases->at(i);
- na->RemoveVhost();
- }
- Log(LOG_ADMIN, source, this) << "for all nicks in group " << nc->display;
- source.Reply(_("vhosts for group \002%s\002 have been removed."), nc->display.c_str());
- }
- else
- source.Reply(NICK_X_NOT_REGISTERED, nick.c_str());
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Deletes the vhost for all nicks in the same group as\n"
- "that of the given nick."));
- return true;
- }
-};
-
-class HSDel : public Module
-{
- CommandHSDel commandhsdel;
- CommandHSDelAll commandhsdelall;
-
- public:
- HSDel(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandhsdel(this), commandhsdelall(this)
- {
- if (!IRCD || !IRCD->CanSetVHost)
- throw ModuleException("Your IRCd does not support vhosts");
- }
-};
-
-MODULE_INIT(HSDel)
diff --git a/modules/commands/hs_group.cpp b/modules/commands/hs_group.cpp
deleted file mode 100644
index e6211b118..000000000
--- a/modules/commands/hs_group.cpp
+++ /dev/null
@@ -1,117 +0,0 @@
-/* HostServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class CommandHSGroup : public Command
-{
- bool setting;
-
- public:
- void Sync(const NickAlias *na)
- {
- if (setting)
- return;
-
- if (!na || !na->HasVhost())
- return;
-
- setting = true;
- for (unsigned i = 0; i < na->nc->aliases->size(); ++i)
- {
- NickAlias *nick = na->nc->aliases->at(i);
- if (nick)
- {
- nick->SetVhost(na->GetVhostIdent(), na->GetVhostHost(), na->GetVhostCreator());
- FOREACH_MOD(OnSetVhost, (nick));
- }
- }
- setting = false;
- }
-
- CommandHSGroup(Module *creator) : Command(creator, "hostserv/group", 0, 0), setting(false)
- {
- this->SetDesc(_("Syncs the vhost for all nicks in a group"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- NickAlias *na = NickAlias::Find(source.GetNick());
- if (na && source.GetAccount() == na->nc && na->HasVhost())
- {
- this->Sync(na);
- if (!na->GetVhostIdent().empty())
- source.Reply(_("All vhosts in the group \002%s\002 have been set to \002%s\002@\002%s\002."), source.nc->display.c_str(), na->GetVhostIdent().c_str(), na->GetVhostHost().c_str());
- else
- source.Reply(_("All vhosts in the group \002%s\002 have been set to \002%s\002."), source.nc->display.c_str(), na->GetVhostHost().c_str());
- }
- else
- source.Reply(HOST_NOT_ASSIGNED);
-
- return;
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("This command allows users to set the vhost of their\n"
- "CURRENT nick to be the vhost for all nicks in the same\n"
- "group."));
- return true;
- }
-};
-
-class HSGroup : public Module
-{
- CommandHSGroup commandhsgroup;
- bool syncongroup;
- bool synconset;
-
- public:
- HSGroup(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandhsgroup(this)
- {
- if (!IRCD || !IRCD->CanSetVHost)
- throw ModuleException("Your IRCd does not support vhosts");
- }
-
- void OnSetVhost(NickAlias *na) anope_override
- {
- if (!synconset)
- return;
-
- commandhsgroup.Sync(na);
- }
-
- void OnNickGroup(User *u, NickAlias *na) anope_override
- {
- if (!syncongroup)
- return;
-
- commandhsgroup.Sync(na);
- }
-
- void OnReload(Configuration::Conf *conf) anope_override
- {
- Configuration::Block *block = conf->GetModule(this);
- syncongroup = block->Get<bool>("syncongroup");
- synconset = block->Get<bool>("synconset");
- }
-};
-
-MODULE_INIT(HSGroup)
diff --git a/modules/commands/hs_off.cpp b/modules/commands/hs_off.cpp
deleted file mode 100644
index 75c8cf62a..000000000
--- a/modules/commands/hs_off.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-/* HostServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class CommandHSOff : public Command
-{
- public:
- CommandHSOff(Module *creator) : Command(creator, "hostserv/off", 0, 0)
- {
- this->SetDesc(_("Deactivates your assigned vhost"));
- this->RequireUser(true);
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- User *u = source.GetUser();
-
- const NickAlias *na = NickAlias::Find(u->nick);
- if (!na || na->nc != u->Account() || !na->HasVhost())
- na = NickAlias::Find(u->Account()->display);
-
- if (!na || !na->HasVhost())
- source.Reply(HOST_NOT_ASSIGNED);
- else
- {
- u->vhost.clear();
- IRCD->SendVhostDel(u);
- u->UpdateHost();
- Log(LOG_COMMAND, source, this) << "to disable their vhost";
- source.Reply(_("Your vhost was removed and the normal cloaking restored."));
- }
-
- return;
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Deactivates the vhost currently assigned to the nick in use.\n"
- "When you use this command any user who performs a /whois\n"
- "on you will see your real host/IP address."));
- return true;
- }
-};
-
-class HSOff : public Module
-{
- CommandHSOff commandhsoff;
-
- public:
- HSOff(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandhsoff(this)
- {
- if (!IRCD || !IRCD->CanSetVHost)
- throw ModuleException("Your IRCd does not support vhosts");
- }
-};
-
-MODULE_INIT(HSOff)
diff --git a/modules/commands/hs_on.cpp b/modules/commands/hs_on.cpp
deleted file mode 100644
index 5aa20380c..000000000
--- a/modules/commands/hs_on.cpp
+++ /dev/null
@@ -1,75 +0,0 @@
-/* HostServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class CommandHSOn : public Command
-{
- public:
- CommandHSOn(Module *creator) : Command(creator, "hostserv/on", 0, 0)
- {
- this->SetDesc(_("Activates your assigned vhost"));
- this->RequireUser(true);
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (!IRCD->CanSetVHost)
- return; // HostServ wouldn't even be loaded at this point
-
- User *u = source.GetUser();
- const NickAlias *na = NickAlias::Find(u->nick);
- if (!na || na->nc != u->Account() || !na->HasVhost())
- na = NickAlias::Find(u->Account()->display);
- if (na && u->Account() == na->nc && na->HasVhost())
- {
- if (!na->GetVhostIdent().empty())
- source.Reply(_("Your vhost of \002%s\002@\002%s\002 is now activated."), na->GetVhostIdent().c_str(), na->GetVhostHost().c_str());
- else
- source.Reply(_("Your vhost of \002%s\002 is now activated."), na->GetVhostHost().c_str());
- Log(LOG_COMMAND, source, this) << "to enable their vhost of " << (!na->GetVhostIdent().empty() ? na->GetVhostIdent() + "@" : "") << na->GetVhostHost();
- IRCD->SendVhost(u, na->GetVhostIdent(), na->GetVhostHost());
- u->vhost = na->GetVhostHost();
- if (IRCD->CanSetVIdent && !na->GetVhostIdent().empty())
- u->SetVIdent(na->GetVhostIdent());
- u->UpdateHost();
- }
- else
- source.Reply(HOST_NOT_ASSIGNED);
-
- return;
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Activates the vhost currently assigned to the nick in use.\n"
- "When you use this command any user who performs a /whois\n"
- "on you will see the vhost instead of your real host/IP address."));
- return true;
- }
-};
-
-class HSOn : public Module
-{
- CommandHSOn commandhson;
-
- public:
- HSOn(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandhson(this)
- {
- if (!IRCD || !IRCD->CanSetVHost)
- throw ModuleException("Your IRCd does not support vhosts");
- }
-};
-
-MODULE_INIT(HSOn)
diff --git a/modules/commands/hs_request.cpp b/modules/commands/hs_request.cpp
deleted file mode 100644
index 12979e804..000000000
--- a/modules/commands/hs_request.cpp
+++ /dev/null
@@ -1,394 +0,0 @@
-/* hs_request.c - Add request and activate functionality to HostServ
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Based on the original module by Rob <rob@anope.org>
- * Included in the Anope module pack since Anope 1.7.11
- * Anope Coder: GeniusDex <geniusdex@anope.org>
- *
- * Please read COPYING and README for further details.
- *
- * Send bug reports to the Anope Coder instead of the module
- * author, because any changes since the inclusion into anope
- * are not supported by the original author.
- */
-
-#include "module.h"
-
-static ServiceReference<MemoServService> memoserv("MemoServService", "MemoServ");
-
-static void req_send_memos(Module *me, CommandSource &source, const Anope::string &vIdent, const Anope::string &vHost);
-
-struct HostRequest : Serializable
-{
- Anope::string nick;
- Anope::string ident;
- Anope::string host;
- time_t time;
-
- HostRequest(Extensible *) : Serializable("HostRequest") { }
-
- void Serialize(Serialize::Data &data) const anope_override
- {
- data["nick"] << this->nick;
- data["ident"] << this->ident;
- data["host"] << this->host;
- data.SetType("time", Serialize::Data::DT_INT); data["time"] << this->time;
- }
-
- static Serializable* Unserialize(Serializable *obj, Serialize::Data &data)
- {
- Anope::string snick;
- data["nick"] >> snick;
-
- NickAlias *na = NickAlias::Find(snick);
- if (na == NULL)
- return NULL;
-
- HostRequest *req;
- if (obj)
- req = anope_dynamic_static_cast<HostRequest *>(obj);
- else
- req = na->Extend<HostRequest>("hostrequest");
- if (req)
- {
- req->nick = na->nick;
- data["ident"] >> req->ident;
- data["host"] >> req->host;
- data["time"] >> req->time;
- }
-
- return req;
- }
-};
-
-class CommandHSRequest : public Command
-{
- bool isvalidchar(char c)
- {
- if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '.' || c == '-')
- return true;
- return false;
- }
-
- public:
- CommandHSRequest(Module *creator) : Command(creator, "hostserv/request", 1, 1)
- {
- this->SetDesc(_("Request a vHost for your nick"));
- this->SetSyntax(_("vhost"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- User *u = source.GetUser();
- NickAlias *na = NickAlias::Find(source.GetNick());
- if (!na || na->nc != source.GetAccount())
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- if (source.GetAccount()->HasExt("UNCONFIRMED"))
- {
- source.Reply(_("You must confirm your account before you may request a vhost."));
- return;
- }
-
- Anope::string rawhostmask = params[0];
-
- Anope::string user, host;
- size_t a = rawhostmask.find('@');
-
- if (a == Anope::string::npos)
- host = rawhostmask;
- else
- {
- user = rawhostmask.substr(0, a);
- host = rawhostmask.substr(a + 1);
- }
-
- if (host.empty())
- {
- this->OnSyntaxError(source, "");
- return;
- }
-
- if (!user.empty())
- {
- if (user.length() > Config->GetBlock("networkinfo")->Get<unsigned>("userlen"))
- {
- source.Reply(HOST_SET_IDENTTOOLONG, Config->GetBlock("networkinfo")->Get<unsigned>("userlen"));
- return;
- }
- else if (!IRCD->CanSetVIdent)
- {
- source.Reply(HOST_NO_VIDENT);
- return;
- }
- for (Anope::string::iterator s = user.begin(), s_end = user.end(); s != s_end; ++s)
- if (!isvalidchar(*s))
- {
- source.Reply(HOST_SET_IDENT_ERROR);
- return;
- }
- }
-
- if (host.length() > Config->GetBlock("networkinfo")->Get<unsigned>("hostlen"))
- {
- source.Reply(HOST_SET_TOOLONG, Config->GetBlock("networkinfo")->Get<unsigned>("hostlen"));
- return;
- }
-
- if (!IRCD->IsHostValid(host))
- {
- source.Reply(HOST_SET_ERROR);
- return;
- }
-
- time_t send_delay = Config->GetModule("memoserv")->Get<time_t>("senddelay");
- if (Config->GetModule(this->owner)->Get<bool>("memooper") && send_delay > 0 && u && u->lastmemosend + send_delay > Anope::CurTime)
- {
- source.Reply(_("Please wait %d seconds before requesting a new vHost."), send_delay);
- u->lastmemosend = Anope::CurTime;
- return;
- }
-
- HostRequest req(na);
- req.nick = source.GetNick();
- req.ident = user;
- req.host = host;
- req.time = Anope::CurTime;
- na->Extend<HostRequest>("hostrequest", req);
-
- source.Reply(_("Your vHost has been requested."));
- req_send_memos(owner, source, user, host);
- Log(LOG_COMMAND, source, this) << "to request new vhost " << (!user.empty() ? user + "@" : "") << host;
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Request the given vHost to be activated for your nick by the\n"
- "network administrators. Please be patient while your request\n"
- "is being considered."));
- return true;
- }
-};
-
-class CommandHSActivate : public Command
-{
- public:
- CommandHSActivate(Module *creator) : Command(creator, "hostserv/activate", 1, 1)
- {
- this->SetDesc(_("Approve the requested vHost of a user"));
- this->SetSyntax(_("\037nick\037"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- const Anope::string &nick = params[0];
-
- NickAlias *na = NickAlias::Find(nick);
- HostRequest *req = na ? na->GetExt<HostRequest>("hostrequest") : NULL;
- if (req)
- {
- na->SetVhost(req->ident, req->host, source.GetNick(), req->time);
- FOREACH_MOD(OnSetVhost, (na));
-
- if (Config->GetModule(this->owner)->Get<bool>("memouser") && memoserv)
- memoserv->Send(source.service->nick, na->nick, _("[auto memo] Your requested vHost has been approved."), true);
-
- source.Reply(_("vHost for %s has been activated."), na->nick.c_str());
- Log(LOG_COMMAND, source, this) << "for " << na->nick << " for vhost " << (!req->ident.empty() ? req->ident + "@" : "") << req->host;
- na->Shrink<HostRequest>("hostrequest");
- }
- else
- source.Reply(_("No request for nick %s found."), nick.c_str());
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Activate the requested vHost for the given nick."));
- if (Config->GetModule(this->owner)->Get<bool>("memouser"))
- source.Reply(_("A memo informing the user will also be sent."));
-
- return true;
- }
-};
-
-class CommandHSReject : public Command
-{
- public:
- CommandHSReject(Module *creator) : Command(creator, "hostserv/reject", 1, 2)
- {
- this->SetDesc(_("Reject the requested vHost of a user"));
- this->SetSyntax(_("\037nick\037 [\037reason\037]"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- const Anope::string &nick = params[0];
- const Anope::string &reason = params.size() > 1 ? params[1] : "";
-
- NickAlias *na = NickAlias::Find(nick);
- HostRequest *req = na ? na->GetExt<HostRequest>("hostrequest") : NULL;
- if (req)
- {
- na->Shrink<HostRequest>("hostrequest");
-
- if (Config->GetModule(this->owner)->Get<bool>("memouser") && memoserv)
- {
- Anope::string message;
- if (!reason.empty())
- message = Anope::printf(_("[auto memo] Your requested vHost has been rejected. Reason: %s"), reason.c_str());
- else
- message = _("[auto memo] Your requested vHost has been rejected.");
-
- memoserv->Send(source.service->nick, nick, Language::Translate(source.GetAccount(), message.c_str()), true);
- }
-
- source.Reply(_("vHost for %s has been rejected."), nick.c_str());
- Log(LOG_COMMAND, source, this) << "to reject vhost for " << nick << " (" << (!reason.empty() ? reason : "no reason") << ")";
- }
- else
- source.Reply(_("No request for nick %s found."), nick.c_str());
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Reject the requested vHost for the given nick."));
- if (Config->GetModule(this->owner)->Get<bool>("memouser"))
- source.Reply(_("A memo informing the user will also be sent, which includes the reason for the rejection if supplied."));
-
- return true;
- }
-};
-
-class CommandHSWaiting : public Command
-{
- public:
- CommandHSWaiting(Module *creator) : Command(creator, "hostserv/waiting", 0, 0)
- {
- this->SetDesc(_("Retrieves the vhost requests"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- unsigned counter = 0;
- unsigned display_counter = 0, listmax = Config->GetModule(this->owner)->Get<unsigned>("listmax");
- ListFormatter list(source.GetAccount());
-
- list.AddColumn(_("Number")).AddColumn(_("Nick")).AddColumn(_("Vhost")).AddColumn(_("Created"));
-
- for (nickalias_map::const_iterator it = NickAliasList->begin(), it_end = NickAliasList->end(); it != it_end; ++it)
- {
- const NickAlias *na = it->second;
- HostRequest *hr = na->GetExt<HostRequest>("hostrequest");
- if (!hr)
- continue;
-
- if (!listmax || display_counter < listmax)
- {
- ++display_counter;
-
- ListFormatter::ListEntry entry;
- entry["Number"] = stringify(display_counter);
- entry["Nick"] = it->first;
- if (!hr->ident.empty())
- entry["Vhost"] = hr->ident + "@" + hr->host;
- else
- entry["Vhost"] = hr->host;
- entry["Created"] = Anope::strftime(hr->time, NULL, true);
- list.AddEntry(entry);
- }
- ++counter;
- }
-
- std::vector<Anope::string> replies;
- list.Process(replies);
-
- for (unsigned i = 0; i < replies.size(); ++i)
- source.Reply(replies[i]);
-
- source.Reply(_("Displayed \002%d\002 records (\002%d\002 total)."), display_counter, counter);
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("This command retrieves the vhost requests."));
-
- return true;
- }
-};
-
-class HSRequest : public Module
-{
- CommandHSRequest commandhsrequest;
- CommandHSActivate commandhsactive;
- CommandHSReject commandhsreject;
- CommandHSWaiting commandhswaiting;
- ExtensibleItem<HostRequest> hostrequest;
- Serialize::Type request_type;
-
- public:
- HSRequest(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandhsrequest(this), commandhsactive(this),
- commandhsreject(this), commandhswaiting(this), hostrequest(this, "hostrequest"), request_type("HostRequest", HostRequest::Unserialize)
- {
- if (!IRCD || !IRCD->CanSetVHost)
- throw ModuleException("Your IRCd does not support vhosts");
- }
-};
-
-static void req_send_memos(Module *me, CommandSource &source, const Anope::string &vIdent, const Anope::string &vHost)
-{
- Anope::string host;
- std::list<std::pair<Anope::string, Anope::string> >::iterator it, it_end;
-
- if (!vIdent.empty())
- host = vIdent + "@" + vHost;
- else
- host = vHost;
-
- if (Config->GetModule(me)->Get<bool>("memooper") && memoserv)
- for (unsigned i = 0; i < Oper::opers.size(); ++i)
- {
- Oper *o = Oper::opers[i];
-
- const NickAlias *na = NickAlias::Find(o->name);
- if (!na)
- continue;
-
- Anope::string message = Anope::printf(_("[auto memo] vHost \002%s\002 has been requested by %s."), host.c_str(), source.GetNick().c_str());
-
- memoserv->Send(source.service->nick, na->nick, message, true);
- }
-}
-
-MODULE_INIT(HSRequest)
diff --git a/modules/commands/hs_set.cpp b/modules/commands/hs_set.cpp
deleted file mode 100644
index 4c736a77d..000000000
--- a/modules/commands/hs_set.cpp
+++ /dev/null
@@ -1,229 +0,0 @@
-/* HostServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class CommandHSSet : public Command
-{
- public:
- CommandHSSet(Module *creator) : Command(creator, "hostserv/set", 2, 2)
- {
- this->SetDesc(_("Set the vhost of another user"));
- this->SetSyntax(_("\037nick\037 \037hostmask\037"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- const Anope::string &nick = params[0];
-
- NickAlias *na = NickAlias::Find(nick);
- if (na == NULL)
- {
- source.Reply(NICK_X_NOT_REGISTERED, nick.c_str());
- return;
- }
-
- Anope::string rawhostmask = params[1];
-
- Anope::string user, host;
- size_t a = rawhostmask.find('@');
-
- if (a == Anope::string::npos)
- host = rawhostmask;
- else
- {
- user = rawhostmask.substr(0, a);
- host = rawhostmask.substr(a + 1);
- }
-
- if (host.empty())
- {
- this->OnSyntaxError(source, "");
- return;
- }
-
- if (!user.empty())
- {
- if (!IRCD->CanSetVIdent)
- {
- source.Reply(HOST_NO_VIDENT);
- return;
- }
- else if (!IRCD->IsIdentValid(user))
- {
- source.Reply(HOST_SET_IDENT_ERROR);
- return;
- }
- }
-
- if (host.length() > Config->GetBlock("networkinfo")->Get<unsigned>("hostlen"))
- {
- source.Reply(HOST_SET_TOOLONG, Config->GetBlock("networkinfo")->Get<unsigned>("hostlen"));
- return;
- }
-
- if (!IRCD->IsHostValid(host))
- {
- source.Reply(HOST_SET_ERROR);
- return;
- }
-
- Log(LOG_ADMIN, source, this) << "to set the vhost of " << na->nick << " to " << (!user.empty() ? user + "@" : "") << host;
-
- na->SetVhost(user, host, source.GetNick());
- FOREACH_MOD(OnSetVhost, (na));
- if (!user.empty())
- source.Reply(_("VHost for \002%s\002 set to \002%s\002@\002%s\002."), nick.c_str(), user.c_str(), host.c_str());
- else
- source.Reply(_("VHost for \002%s\002 set to \002%s\002."), nick.c_str(), host.c_str());
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Sets the vhost for the given nick to that of the given\n"
- "hostmask. If your IRCD supports vIdents, then using\n"
- "SET <nick> <ident>@<hostmask> set idents for users as\n"
- "well as vhosts."));
- return true;
- }
-};
-
-class CommandHSSetAll : public Command
-{
- void Sync(const NickAlias *na)
- {
- if (!na || !na->HasVhost())
- return;
-
- for (unsigned i = 0; i < na->nc->aliases->size(); ++i)
- {
- NickAlias *nick = na->nc->aliases->at(i);
- if (nick)
- nick->SetVhost(na->GetVhostIdent(), na->GetVhostHost(), na->GetVhostCreator());
- }
- }
-
- public:
- CommandHSSetAll(Module *creator) : Command(creator, "hostserv/setall", 2, 2)
- {
- this->SetDesc(_("Set the vhost for all nicks in a group"));
- this->SetSyntax(_("\037nick\037 \037hostmask\037"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- Anope::string nick = params[0];
-
- NickAlias *na = NickAlias::Find(nick);
- if (na == NULL)
- {
- source.Reply(NICK_X_NOT_REGISTERED, nick.c_str());
- return;
- }
-
- Anope::string rawhostmask = params[1];
-
- Anope::string user, host;
- size_t a = rawhostmask.find('@');
-
- if (a == Anope::string::npos)
- host = rawhostmask;
- else
- {
- user = rawhostmask.substr(0, a);
- host = rawhostmask.substr(a + 1);
- }
-
- if (host.empty())
- {
- this->OnSyntaxError(source, "");
- return;
- }
-
- if (!user.empty())
- {
- if (!IRCD->CanSetVIdent)
- {
- source.Reply(HOST_NO_VIDENT);
- return;
- }
- else if (!IRCD->IsIdentValid(user))
- {
- source.Reply(HOST_SET_IDENT_ERROR);
- return;
- }
- }
-
- if (host.length() > Config->GetBlock("networkinfo")->Get<unsigned>("hostlen"))
- {
- source.Reply(HOST_SET_TOOLONG, Config->GetBlock("networkinfo")->Get<unsigned>("hostlen"));
- return;
- }
-
- if (!IRCD->IsHostValid(host))
- {
- source.Reply(HOST_SET_ERROR);
- return;
- }
-
- Log(LOG_ADMIN, source, this) << "to set the vhost of " << na->nick << " to " << (!user.empty() ? user + "@" : "") << host;
-
- na->SetVhost(user, host, source.GetNick());
- this->Sync(na);
- FOREACH_MOD(OnSetVhost, (na));
- if (!user.empty())
- source.Reply(_("VHost for group \002%s\002 set to \002%s\002@\002%s\002."), nick.c_str(), user.c_str(), host.c_str());
- else
- source.Reply(_("VHost for group \002%s\002 set to \002%s\002."), nick.c_str(), host.c_str());
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Sets the vhost for all nicks in the same group as that\n"
- "of the given nick. If your IRCD supports vIdents, then\n"
- "using SETALL <nick> <ident>@<hostmask> will set idents\n"
- "for users as well as vhosts.\n"
- "* NOTE, this will not update the vhost for any nicks\n"
- "added to the group after this command was used."));
- return true;
- }
-};
-
-class HSSet : public Module
-{
- CommandHSSet commandhsset;
- CommandHSSetAll commandhssetall;
-
- public:
- HSSet(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), commandhsset(this), commandhssetall(this)
- {
- if (!IRCD || !IRCD->CanSetVHost)
- throw ModuleException("Your IRCd does not support vhosts");
- }
-};
-
-MODULE_INIT(HSSet)
diff --git a/modules/commands/ms_cancel.cpp b/modules/commands/ms_cancel.cpp
deleted file mode 100644
index 9e6f0ce63..000000000
--- a/modules/commands/ms_cancel.cpp
+++ /dev/null
@@ -1,81 +0,0 @@
-/* MemoServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class CommandMSCancel : public Command
-{
- public:
- CommandMSCancel(Module *creator) : Command(creator, "memoserv/cancel", 1, 1)
- {
- this->SetDesc(_("Cancel the last memo you sent"));
- this->SetSyntax(_("{\037nick\037 | \037channel\037}"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- const Anope::string &nname = params[0];
-
- bool ischan;
- MemoInfo *mi = MemoInfo::GetMemoInfo(nname, ischan);
-
- if (mi == NULL)
- source.Reply(ischan ? CHAN_X_NOT_REGISTERED : _(NICK_X_NOT_REGISTERED), nname.c_str());
- else
- {
- ChannelInfo *ci = NULL;
- NickAlias *na = NULL;
- if (ischan)
- ci = ChannelInfo::Find(nname);
- else
- na = NickAlias::Find(nname);
- for (int i = mi->memos->size() - 1; i >= 0; --i)
- if (mi->GetMemo(i)->unread && source.nc->display.equals_ci(mi->GetMemo(i)->sender))
- {
- FOREACH_MOD(OnMemoDel, (ischan ? ci->name : na->nc->display, mi, mi->GetMemo(i)));
- mi->Del(i);
- source.Reply(_("Last memo to \002%s\002 has been cancelled."), nname.c_str());
- return;
- }
-
- source.Reply(_("No memo was cancelable."));
- }
- return;
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Cancels the last memo you sent to the given nick or channel,\n"
- "provided it has not been read at the time you use the command."));
- return true;
- }
-};
-
-class MSCancel : public Module
-{
- CommandMSCancel commandmscancel;
-
- public:
- MSCancel(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandmscancel(this)
- {
- }
-};
-
-MODULE_INIT(MSCancel)
diff --git a/modules/commands/ms_check.cpp b/modules/commands/ms_check.cpp
deleted file mode 100644
index 707da95cd..000000000
--- a/modules/commands/ms_check.cpp
+++ /dev/null
@@ -1,87 +0,0 @@
-/* MemoServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class CommandMSCheck : public Command
-{
- public:
- CommandMSCheck(Module *creator) : Command(creator, "memoserv/check", 1, 1)
- {
- this->SetDesc(_("Checks if last memo to a nick was read"));
- this->SetSyntax(_("\037nick\037"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
-
- const Anope::string &recipient = params[0];
-
- bool found = false;
-
- const NickAlias *na = NickAlias::Find(recipient);
- if (!na)
- {
- source.Reply(NICK_X_NOT_REGISTERED, recipient.c_str());
- return;
- }
-
- MemoInfo *mi = &na->nc->memos;
-
- /* Okay, I know this looks strange but we want to get the LAST memo, so we
- have to loop backwards */
-
- for (unsigned i = mi->memos->size(); i > 0; --i)
- {
- Memo *m = mi->GetMemo(i - 1);
- NickAlias *na2 = NickAlias::Find(m->sender);
-
- if (na2 != NULL && na2->nc == source.GetAccount())
- {
- found = true; /* Yes, we've found the memo */
-
- if (m->unread)
- source.Reply(_("The last memo you sent to %s (sent on %s) has not yet been read."), na->nick.c_str(), Anope::strftime(m->time, source.GetAccount()).c_str());
- else
- source.Reply(_("The last memo you sent to %s (sent on %s) has been read."), na->nick.c_str(), Anope::strftime(m->time, source.GetAccount()).c_str());
- break;
- }
- }
-
- if (!found)
- source.Reply(_("Nick %s doesn't have a memo from you."), na->nick.c_str());
-
- return;
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Checks whether the _last_ memo you sent to \037nick\037 has been read\n"
- "or not. Note that this only works with nicks, not with channels."));
- return true;
- }
-};
-
-class MSCheck : public Module
-{
- CommandMSCheck commandmscheck;
-
- public:
- MSCheck(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandmscheck(this)
- {
-
- }
-};
-
-MODULE_INIT(MSCheck)
diff --git a/modules/commands/ms_del.cpp b/modules/commands/ms_del.cpp
deleted file mode 100644
index bc496d72e..000000000
--- a/modules/commands/ms_del.cpp
+++ /dev/null
@@ -1,151 +0,0 @@
-/* MemoServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class MemoDelCallback : public NumberList
-{
- CommandSource &source;
- ChannelInfo *ci;
- MemoInfo *mi;
- public:
- MemoDelCallback(CommandSource &_source, ChannelInfo *_ci, MemoInfo *_mi, const Anope::string &list) : NumberList(list, true), source(_source), ci(_ci), mi(_mi)
- {
- }
-
- void HandleNumber(unsigned number) anope_override
- {
- if (!number || number > mi->memos->size())
- return;
-
- FOREACH_MOD(OnMemoDel, (ci ? ci->name : source.nc->display, mi, mi->GetMemo(number - 1)));
-
- mi->Del(number - 1);
- source.Reply(_("Memo %d has been deleted."), number);
- }
-};
-
-class CommandMSDel : public Command
-{
- public:
- CommandMSDel(Module *creator) : Command(creator, "memoserv/del", 0, 2)
- {
- this->SetDesc(_("Delete a memo or memos"));
- this->SetSyntax(_("[\037channel\037] {\037num\037 | \037list\037 | LAST | ALL}"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- MemoInfo *mi;
- ChannelInfo *ci = NULL;
- Anope::string numstr = !params.empty() ? params[0] : "", chan;
-
- if (!numstr.empty() && numstr[0] == '#')
- {
- chan = numstr;
- numstr = params.size() > 1 ? params[1] : "";
-
- ci = ChannelInfo::Find(chan);
- if (!ci)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, chan.c_str());
- return;
- }
- else if (!source.AccessFor(ci).HasPriv("MEMO"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
- mi = &ci->memos;
- }
- else
- mi = &source.nc->memos;
- if (numstr.empty() || (!isdigit(numstr[0]) && !numstr.equals_ci("ALL") && !numstr.equals_ci("LAST")))
- this->OnSyntaxError(source, numstr);
- else if (mi->memos->empty())
- {
- if (!chan.empty())
- source.Reply(MEMO_X_HAS_NO_MEMOS, chan.c_str());
- else
- source.Reply(MEMO_HAVE_NO_MEMOS);
- }
- else
- {
- if (isdigit(numstr[0]))
- {
- MemoDelCallback list(source, ci, mi, numstr);
- list.Process();
- }
- else if (numstr.equals_ci("LAST"))
- {
- /* Delete last memo. */
- FOREACH_MOD(OnMemoDel, (ci ? ci->name : source.nc->display, mi, mi->GetMemo(mi->memos->size() - 1)));
- mi->Del(mi->memos->size() - 1);
- source.Reply(_("Memo %d has been deleted."), mi->memos->size() + 1);
- }
- else
- {
- /* Delete all memos. */
- for (unsigned i = mi->memos->size(); i > 0; --i)
- {
- FOREACH_MOD(OnMemoDel, (ci ? ci->name : source.nc->display, mi, mi->GetMemo(i)));
- mi->Del(i - 1);
- }
- if (!chan.empty())
- source.Reply(_("All memos for channel %s have been deleted."), chan.c_str());
- else
- source.Reply(_("All of your memos have been deleted."));
- }
- }
- return;
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Deletes the specified memo or memos. You can supply\n"
- "multiple memo numbers or ranges of numbers instead of a\n"
- "single number, as in the second example below.\n"
- " \n"
- "If \002LAST\002 is given, the last memo will be deleted.\n"
- "If \002ALL\002 is given, deletes all of your memos.\n"
- " \n"
- "Examples:\n"
- " \n"
- " \002DEL 1\002\n"
- " Deletes your first memo.\n"
- " \n"
- " \002DEL 2-5,7-9\002\n"
- " Deletes memos numbered 2 through 5 and 7 through 9."));
- return true;
- }
-};
-
-class MSDel : public Module
-{
- CommandMSDel commandmsdel;
-
- public:
- MSDel(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandmsdel(this)
- {
-
- }
-};
-
-MODULE_INIT(MSDel)
diff --git a/modules/commands/ms_ignore.cpp b/modules/commands/ms_ignore.cpp
deleted file mode 100644
index 6a2fa01cf..000000000
--- a/modules/commands/ms_ignore.cpp
+++ /dev/null
@@ -1,132 +0,0 @@
-/* MemoServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class CommandMSIgnore : public Command
-{
- public:
- CommandMSIgnore(Module *creator) : Command(creator, "memoserv/ignore", 1, 3)
- {
- this->SetDesc(_("Manage the memo ignore list"));
- this->SetSyntax(_("[\037channel\037] ADD \037entry\037"));
- this->SetSyntax(_("[\037channel\037] DEL \037entry\037"));
- this->SetSyntax(_("[\037channel\037] LIST"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- Anope::string channel = params[0];
- Anope::string command = (params.size() > 1 ? params[1] : "");
- Anope::string param = (params.size() > 2 ? params[2] : "");
-
- if (channel[0] != '#')
- {
- param = command;
- command = channel;
- channel = source.GetNick();
- }
-
- bool ischan;
- MemoInfo *mi = MemoInfo::GetMemoInfo(channel, ischan);
- ChannelInfo *ci = ChannelInfo::Find(channel);
- if (!mi)
- source.Reply(ischan ? CHAN_X_NOT_REGISTERED : _(NICK_X_NOT_REGISTERED), channel.c_str());
- else if (ischan && !source.AccessFor(ci).HasPriv("MEMO"))
- source.Reply(ACCESS_DENIED);
- else if (command.equals_ci("ADD") && !param.empty())
- {
- if (mi->ignores.size() >= Config->GetModule(this->owner)->Get<unsigned>("max", "32"))
- {
- source.Reply(_("Sorry, the memo ignore list for \002%s\002 is full."), channel.c_str());
- return;
- }
-
- if (std::find(mi->ignores.begin(), mi->ignores.end(), param.ci_str()) == mi->ignores.end())
- {
- mi->ignores.push_back(param.ci_str());
- source.Reply(_("\002%s\002 added to ignore list."), param.c_str());
- }
- else
- source.Reply(_("\002%s\002 is already on the ignore list."), param.c_str());
- }
- else if (command.equals_ci("DEL") && !param.empty())
- {
- std::vector<Anope::string>::iterator it = std::find(mi->ignores.begin(), mi->ignores.end(), param.ci_str());
-
- if (it != mi->ignores.end())
- {
- mi->ignores.erase(it);
- source.Reply(_("\002%s\002 removed from the ignore list."), param.c_str());
- }
- else
- source.Reply(_("\002%s\002 is not on the ignore list."), param.c_str());
- }
- else if (command.equals_ci("LIST"))
- {
- if (mi->ignores.empty())
- source.Reply(_("Memo ignore list is empty."));
- else
- {
- ListFormatter list(source.GetAccount());
- list.AddColumn(_("Mask"));
- for (unsigned i = 0; i < mi->ignores.size(); ++i)
- {
- ListFormatter::ListEntry entry;
- entry["Mask"] = mi->ignores[i];
- list.AddEntry(entry);
- }
-
- source.Reply(_("Ignore list:"));
-
- std::vector<Anope::string> replies;
- list.Process(replies);
-
- for (unsigned i = 0; i < replies.size(); ++i)
- source.Reply(replies[i]);
- }
- }
- else
- this->OnSyntaxError(source, "");
-
- return;
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Allows you to ignore users by nick or host from memoing\n"
- "you or a channel. If someone on the memo ignore list tries\n"
- "to memo you or a channel, they will not be told that you have\n"
- "them ignored."));
- return true;
- }
-};
-
-class MSIgnore : public Module
-{
- CommandMSIgnore commandmsignore;
-
- public:
- MSIgnore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandmsignore(this)
- {
- }
-};
-
-MODULE_INIT(MSIgnore)
diff --git a/modules/commands/ms_info.cpp b/modules/commands/ms_info.cpp
deleted file mode 100644
index 8f99557bf..000000000
--- a/modules/commands/ms_info.cpp
+++ /dev/null
@@ -1,231 +0,0 @@
-/* MemoServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class CommandMSInfo : public Command
-{
- public:
- CommandMSInfo(Module *creator) : Command(creator, "memoserv/info", 0, 1)
- {
- this->SetDesc(_("Displays information about your memos"));
- this->SetSyntax(_("[\037nick\037 | \037channel\037]"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- NickCore *nc = source.nc;
- const MemoInfo *mi;
- const NickAlias *na = NULL;
- ChannelInfo *ci = NULL;
- const Anope::string &nname = !params.empty() ? params[0] : "";
- bool hardmax;
-
- if (!nname.empty() && nname[0] != '#' && source.HasPriv("memoserv/info"))
- {
- na = NickAlias::Find(nname);
- if (!na)
- {
- source.Reply(NICK_X_NOT_REGISTERED, nname.c_str());
- return;
- }
- mi = &na->nc->memos;
- hardmax = na->nc->HasExt("MEMO_HARDMAX");
- }
- else if (!nname.empty() && nname[0] == '#')
- {
- ci = ChannelInfo::Find(nname);
- if (!ci)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, nname.c_str());
- return;
- }
- else if (!source.AccessFor(ci).HasPriv("MEMO"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
- mi = &ci->memos;
- hardmax = ci->HasExt("MEMO_HARDMAX");
- }
- else if (!nname.empty()) /* It's not a chan and we aren't services admin */
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
- else
- {
- mi = &nc->memos;
- hardmax = nc->HasExt("MEMO_HARDMAX");
- }
-
- if (!nname.empty() && (ci || na->nc != nc))
- {
- if (mi->memos->empty())
- source.Reply(_("%s currently has no memos."), nname.c_str());
- else if (mi->memos->size() == 1)
- {
- if (mi->GetMemo(0)->unread)
- source.Reply(_("%s currently has \0021\002 memo, and it has not yet been read."), nname.c_str());
- else
- source.Reply(_("%s currently has \0021\002 memo."), nname.c_str());
- }
- else
- {
- unsigned count = 0, i, end;
- for (i = 0, end = mi->memos->size(); i < end; ++i)
- if (mi->GetMemo(i)->unread)
- ++count;
- if (count == mi->memos->size())
- source.Reply(_("%s currently has \002%d\002 memos; all of them are unread."), nname.c_str(), count);
- else if (!count)
- source.Reply(_("%s currently has \002%d\002 memos."), nname.c_str(), mi->memos->size());
- else if (count == 1)
- source.Reply(_("%s currently has \002%d\002 memos, of which \0021\002 is unread."), nname.c_str(), mi->memos->size());
- else
- source.Reply(_("%s currently has \002%d\002 memos, of which \002%d\002 are unread."), nname.c_str(), mi->memos->size(), count);
- }
- if (!mi->memomax)
- {
- if (hardmax)
- source.Reply(_("%s's memo limit is \002%d\002, and may not be changed."), nname.c_str(), mi->memomax);
- else
- source.Reply(_("%s's memo limit is \002%d\002."), nname.c_str(), mi->memomax);
- }
- else if (mi->memomax > 0)
- {
- if (hardmax)
- source.Reply(_("%s's memo limit is \002%d\002, and may not be changed."), nname.c_str(), mi->memomax);
- else
- source.Reply(_("%s's memo limit is \002%d\002."), nname.c_str(), mi->memomax);
- }
- else
- source.Reply(_("%s has no memo limit."), nname.c_str());
-
- /* I ripped this code out of ircservices 4.4.5, since I didn't want
- to rewrite the whole thing (it pisses me off). */
- if (na)
- {
- if (na->nc->HasExt("MEMO_RECEIVE") && na->nc->HasExt("MEMO_SIGNON"))
- source.Reply(_("%s is notified of new memos at logon and when they arrive."), nname.c_str());
- else if (na->nc->HasExt("MEMO_RECEIVE"))
- source.Reply(_("%s is notified when new memos arrive."), nname.c_str());
- else if (na->nc->HasExt("MEMO_SIGNON"))
- source.Reply(_("%s is notified of news memos at logon."), nname.c_str());
- else
- source.Reply(_("%s is not notified of new memos."), nname.c_str());
- }
- }
- else /* !nname || (!ci || na->nc == nc) */
- {
- if (mi->memos->empty())
- source.Reply(_("You currently have no memos."));
- else if (mi->memos->size() == 1)
- {
- if (mi->GetMemo(0)->unread)
- source.Reply(_("You currently have \0021\002 memo, and it has not yet been read."));
- else
- source.Reply(_("You currently have \0021\002 memo."));
- }
- else
- {
- unsigned count = 0, i, end;
- for (i = 0, end = mi->memos->size(); i < end; ++i)
- if (mi->GetMemo(i)->unread)
- ++count;
- if (count == mi->memos->size())
- source.Reply(_("You currently have \002%d\002 memos; all of them are unread."), count);
- else if (!count)
- source.Reply(_("You currently have \002%d\002 memos."), mi->memos->size());
- else if (count == 1)
- source.Reply(_("You currently have \002%d\002 memos, of which \0021\002 is unread."), mi->memos->size());
- else
- source.Reply(_("You currently have \002%d\002 memos, of which \002%d\002 are unread."), mi->memos->size(), count);
- }
-
- if (!mi->memomax)
- {
- if (!source.IsServicesOper() && hardmax)
- source.Reply(_("Your memo limit is \0020\002; you will not receive any new memos. You cannot change this limit."));
- else
- source.Reply(_("Your memo limit is \0020\002; you will not receive any new memos."));
- }
- else if (mi->memomax > 0)
- {
- if (!source.IsServicesOper() && hardmax)
- source.Reply(_("Your memo limit is \002%d\002, and may not be changed."), mi->memomax);
- else
- source.Reply(_("Your memo limit is \002%d\002."), mi->memomax);
- }
- else
- source.Reply(_("You have no limit on the number of memos you may keep."));
-
- bool memo_mail = nc->HasExt("MEMO_MAIL");
- if (nc->HasExt("MEMO_RECEIVE") && nc->HasExt("MEMO_SIGNON"))
- {
- if (memo_mail)
- source.Reply(_("You will be notified of new memos at logon and when they arrive, and by mail when they arrive."));
- else
- source.Reply(_("You will be notified of new memos at logon and when they arrive."));
- }
- else if (nc->HasExt("MEMO_RECEIVE"))
- {
- if (memo_mail)
- source.Reply(_("You will be notified by message and by mail when new memos arrive."));
- else
- source.Reply(_("You will be notified when new memos arrive."));
- }
- else if (nc->HasExt("MEMO_SIGNON"))
- {
- if (memo_mail)
- source.Reply(_("You will be notified of new memos at logon, and by mail when they arrive."));
- else
- source.Reply(_("You will be notified of new memos at logon."));
- }
- else
- {
- source.Reply(_("You will not be notified of new memos."));
- }
- }
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Without a parameter, displays information on the number of\n"
- "memos you have, how many of them are unread, and how many\n"
- "total memos you can receive.\n"
- " \n"
- "With a channel parameter, displays the same information for\n"
- "the given channel.\n"
- " \n"
- "With a nickname parameter, displays the same information\n"
- "for the given nickname. This is limited to \002Services\002\n"
- "\002Operators\002."));
-
- return true;
- }
-};
-
-class MSInfo : public Module
-{
- CommandMSInfo commandmsinfo;
-
- public:
- MSInfo(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandmsinfo(this)
- {
-
- }
-};
-
-MODULE_INIT(MSInfo)
diff --git a/modules/commands/ms_list.cpp b/modules/commands/ms_list.cpp
deleted file mode 100644
index c386be81d..000000000
--- a/modules/commands/ms_list.cpp
+++ /dev/null
@@ -1,164 +0,0 @@
-/* MemoServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class CommandMSList : public Command
-{
- public:
- CommandMSList(Module *creator) : Command(creator, "memoserv/list", 0, 2)
- {
- this->SetDesc(_("List your memos"));
- this->SetSyntax(_("[\037channel\037] [\037list\037 | NEW]"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
-
- Anope::string param = !params.empty() ? params[0] : "", chan;
- ChannelInfo *ci = NULL;
- const MemoInfo *mi;
-
- if (!param.empty() && param[0] == '#')
- {
- chan = param;
- param = params.size() > 1 ? params[1] : "";
-
- ci = ChannelInfo::Find(chan);
- if (!ci)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, chan.c_str());
- return;
- }
- else if (!source.AccessFor(ci).HasPriv("MEMO"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
- mi = &ci->memos;
- }
- else
- mi = &source.nc->memos;
-
- if (!param.empty() && !isdigit(param[0]) && !param.equals_ci("NEW"))
- this->OnSyntaxError(source, param);
- else if (!mi->memos->size())
- {
- if (!chan.empty())
- source.Reply(MEMO_X_HAS_NO_MEMOS, chan.c_str());
- else
- source.Reply(MEMO_HAVE_NO_MEMOS);
- }
- else
- {
- ListFormatter list(source.GetAccount());
-
- list.AddColumn(_("Number")).AddColumn(_("Sender")).AddColumn(_("Date/Time"));
-
- if (!param.empty() && isdigit(param[0]))
- {
- class MemoListCallback : public NumberList
- {
- ListFormatter &list;
- CommandSource &source;
- const MemoInfo *mi;
- public:
- MemoListCallback(ListFormatter &_list, CommandSource &_source, const MemoInfo *_mi, const Anope::string &numlist) : NumberList(numlist, false), list(_list), source(_source), mi(_mi)
- {
- }
-
- void HandleNumber(unsigned number) anope_override
- {
- if (!number || number > mi->memos->size())
- return;
-
- const Memo *m = mi->GetMemo(number - 1);
-
- ListFormatter::ListEntry entry;
- entry["Number"] = (m->unread ? "* " : " ") + stringify(number);
- entry["Sender"] = m->sender;
- entry["Date/Time"] = Anope::strftime(m->time, source.GetAccount());
- this->list.AddEntry(entry);
- }
- }
- mlc(list, source, mi, param);
- mlc.Process();
- }
- else
- {
- if (!param.empty())
- {
- unsigned i, end;
- for (i = 0, end = mi->memos->size(); i < end; ++i)
- if (mi->GetMemo(i)->unread)
- break;
- if (i == end)
- {
- if (!chan.empty())
- source.Reply(MEMO_X_HAS_NO_NEW_MEMOS, chan.c_str());
- else
- source.Reply(MEMO_HAVE_NO_NEW_MEMOS);
- return;
- }
- }
-
- for (unsigned i = 0, end = mi->memos->size(); i < end; ++i)
- {
- if (!param.empty() && !mi->GetMemo(i)->unread)
- continue;
-
- const Memo *m = mi->GetMemo(i);
-
- ListFormatter::ListEntry entry;
- entry["Number"] = (m->unread ? "* " : " ") + stringify(i + 1);
- entry["Sender"] = m->sender;
- entry["Date/Time"] = Anope::strftime(m->time, source.GetAccount());
- list.AddEntry(entry);
- }
- }
-
- std::vector<Anope::string> replies;
- list.Process(replies);
-
- source.Reply(_("Memos for %s:"), ci ? ci->name.c_str() : source.GetNick().c_str());
- for (unsigned i = 0; i < replies.size(); ++i)
- source.Reply(replies[i]);
- }
- return;
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Lists any memos you currently have. With \002NEW\002, lists only\n"
- "new (unread) memos. Unread memos are marked with a \"*\"\n"
- "to the left of the memo number. You can also specify a list\n"
- "of numbers, as in the example below:\n"
- " \002LIST 2-5,7-9\002\n"
- " Lists memos numbered 2 through 5 and 7 through 9."));
- return true;
- }
-};
-
-class MSList : public Module
-{
- CommandMSList commandmslist;
-
- public:
- MSList(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandmslist(this)
- {
-
- }
-};
-
-MODULE_INIT(MSList)
diff --git a/modules/commands/ms_read.cpp b/modules/commands/ms_read.cpp
deleted file mode 100644
index 96103d30b..000000000
--- a/modules/commands/ms_read.cpp
+++ /dev/null
@@ -1,216 +0,0 @@
-/* MemoServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-static ServiceReference<MemoServService> MemoServService("MemoServService", "MemoServ");
-
-static void rsend_notify(CommandSource &source, MemoInfo *mi, Memo *m, const Anope::string &targ)
-{
- /* Only send receipt if memos are allowed */
- if (MemoServService && !Anope::ReadOnly)
- {
- /* Get nick alias for sender */
- const NickAlias *na = NickAlias::Find(m->sender);
-
- if (!na)
- return;
-
- /* Get nick core for sender */
- const NickCore *nc = na->nc;
-
- if (!nc)
- return;
-
- /* Text of the memo varies if the recipient was a
- nick or channel */
- Anope::string text = Anope::printf(Language::Translate(na->nc, _("\002[auto-memo]\002 The memo you sent to %s has been viewed.")), targ.c_str());
-
- /* Send notification */
- MemoServService->Send(source.GetNick(), m->sender, text, true);
-
- /* Notify recipient of the memo that a notification has
- been sent to the sender */
- source.Reply(_("A notification memo has been sent to %s informing him/her you have\n"
- "read his/her memo."), nc->display.c_str());
- }
-
- /* Remove receipt flag from the original memo */
- m->receipt = false;
-}
-
-class MemoListCallback : public NumberList
-{
- CommandSource &source;
- MemoInfo *mi;
- const ChannelInfo *ci;
- bool found;
- public:
- MemoListCallback(CommandSource &_source, MemoInfo *_mi, const ChannelInfo *_ci, const Anope::string &numlist) : NumberList(numlist, false), source(_source), mi(_mi), ci(_ci)
- {
- found = false;
- }
-
- ~MemoListCallback()
- {
- if (!found)
- source.Reply(_("No memos to display."));
- }
-
- void HandleNumber(unsigned number) anope_override
- {
- if (!number || number > mi->memos->size())
- return;
-
- MemoListCallback::DoRead(source, mi, ci, number - 1);
- found = true;
- }
-
- static void DoRead(CommandSource &source, MemoInfo *mi, const ChannelInfo *ci, unsigned index)
- {
- Memo *m = mi->GetMemo(index);
- if (!m)
- return;
-
- if (ci)
- source.Reply(_("Memo %d from %s (%s)."), index + 1, m->sender.c_str(), Anope::strftime(m->time, source.GetAccount()).c_str());
- else
- source.Reply(_("Memo %d from %s (%s)."), index + 1, m->sender.c_str(), Anope::strftime(m->time, source.GetAccount()).c_str());
-
- BotInfo *bi;
- Anope::string cmd;
- if (Command::FindCommandFromService("memoserv/del", bi, cmd))
- {
- if (ci)
- source.Reply(_("To delete, type: \002%s%s %s %s %d\002"), Config->StrictPrivmsg.c_str(), bi->nick.c_str(), cmd.c_str(), ci->name.c_str(), index + 1);
- else
- source.Reply(_("To delete, type: \002%s%s %s %d\002"), Config->StrictPrivmsg.c_str(), bi->nick.c_str(), cmd.c_str(), index + 1);
- }
-
- source.Reply("%s", m->text.c_str());
- m->unread = false;
-
- /* Check if a receipt notification was requested */
- if (m->receipt)
- rsend_notify(source, mi, m, ci ? ci->name : source.GetNick());
- }
-};
-
-class CommandMSRead : public Command
-{
- public:
- CommandMSRead(Module *creator) : Command(creator, "memoserv/read", 1, 2)
- {
- this->SetDesc(_("Read a memo or memos"));
- this->SetSyntax(_("[\037channel\037] {\037num\037 | \037list\037 | LAST | NEW}"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
-
- MemoInfo *mi;
- ChannelInfo *ci = NULL;
- Anope::string numstr = params[0], chan;
-
- if (!numstr.empty() && numstr[0] == '#')
- {
- chan = numstr;
- numstr = params.size() > 1 ? params[1] : "";
-
- ci = ChannelInfo::Find(chan);
- if (!ci)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, chan.c_str());
- return;
- }
- else if (!source.AccessFor(ci).HasPriv("MEMO"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
- mi = &ci->memos;
- }
- else
- mi = &source.nc->memos;
-
- if (numstr.empty() || (!numstr.equals_ci("LAST") && !numstr.equals_ci("NEW") && numstr.find_first_not_of("0123456789.,-") != Anope::string::npos))
- this->OnSyntaxError(source, numstr);
- else if (mi->memos->empty())
- {
- if (!chan.empty())
- source.Reply(MEMO_X_HAS_NO_MEMOS, chan.c_str());
- else
- source.Reply(MEMO_HAVE_NO_MEMOS);
- }
- else
- {
- int i, end;
-
- if (numstr.equals_ci("NEW"))
- {
- int readcount = 0;
- for (i = 0, end = mi->memos->size(); i < end; ++i)
- if (mi->GetMemo(i)->unread)
- {
- MemoListCallback::DoRead(source, mi, ci, i);
- ++readcount;
- }
- if (!readcount)
- {
- if (!chan.empty())
- source.Reply(MEMO_X_HAS_NO_NEW_MEMOS, chan.c_str());
- else
- source.Reply(MEMO_HAVE_NO_NEW_MEMOS);
- }
- }
- else if (numstr.equals_ci("LAST"))
- {
- for (i = 0, end = mi->memos->size() - 1; i < end; ++i);
- MemoListCallback::DoRead(source, mi, ci, i);
- }
- else /* number[s] */
- {
- MemoListCallback list(source, mi, ci, numstr);
- list.Process();
- }
- }
- return;
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Sends you the text of the memos specified. If LAST is\n"
- "given, sends you the memo you most recently received. If\n"
- "NEW is given, sends you all of your new memos. Otherwise,\n"
- "sends you memo number \037num\037. You can also give a list of\n"
- "numbers, as in this example:\n"
- " \n"
- " \002READ 2-5,7-9\002\n"
- " Displays memos numbered 2 through 5 and 7 through 9."));
- return true;
- }
-};
-
-class MSRead : public Module
-{
- CommandMSRead commandmsread;
-
- public:
- MSRead(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandmsread(this)
- {
-
- }
-};
-
-MODULE_INIT(MSRead)
diff --git a/modules/commands/ms_rsend.cpp b/modules/commands/ms_rsend.cpp
deleted file mode 100644
index b7744131a..000000000
--- a/modules/commands/ms_rsend.cpp
+++ /dev/null
@@ -1,104 +0,0 @@
-/* MemoServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-namespace
-{
- ServiceReference<MemoServService> memoserv("MemoServService", "MemoServ");
-}
-
-class CommandMSRSend : public Command
-{
- public:
- CommandMSRSend(Module *creator) : Command(creator, "memoserv/rsend", 2, 2)
- {
- this->SetDesc(_("Sends a memo and requests a read receipt"));
- this->SetSyntax(_("{\037nick\037 | \037channel\037} \037memo-text\037"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (!memoserv)
- return;
-
- if (Anope::ReadOnly && !source.IsOper())
- {
- source.Reply(MEMO_SEND_DISABLED);
- return;
- }
-
- const Anope::string &nick = params[0];
- const Anope::string &text = params[1];
- const NickAlias *na = NULL;
-
- /* prevent user from rsend to themselves */
- if ((na = NickAlias::Find(nick)) && na->nc == source.GetAccount())
- {
- source.Reply(_("You can not request a receipt when sending a memo to yourself."));
- return;
- }
-
- if (Config->GetModule(this->owner)->Get<bool>("operonly") && !source.IsServicesOper())
- source.Reply(ACCESS_DENIED);
- else
- {
- MemoServService::MemoResult result = memoserv->Send(source.GetNick(), nick, text);
- if (result == MemoServService::MEMO_INVALID_TARGET)
- source.Reply(_("\002%s\002 is not a registered unforbidden nick or channel."), nick.c_str());
- else if (result == MemoServService::MEMO_TOO_FAST)
- source.Reply(_("Please wait %d seconds before using the %s command again."), Config->GetModule("memoserv")->Get<time_t>("senddelay"), source.command.c_str());
- else if (result == MemoServService::MEMO_TARGET_FULL)
- source.Reply(_("Sorry, %s currently has too many memos and cannot receive more."), nick.c_str());
- else
- {
- source.Reply(_("Memo sent to \002%s\002."), nick.c_str());
-
- bool ischan;
- MemoInfo *mi = MemoInfo::GetMemoInfo(nick, ischan);
- if (mi == NULL)
- throw CoreException("NULL mi in ms_rsend");
- Memo *m = (mi->memos->size() ? mi->GetMemo(mi->memos->size() - 1) : NULL);
- if (m != NULL)
- m->receipt = true;
- }
- }
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Sends the named \037nick\037 or \037channel\037 a memo containing\n"
- "\037memo-text\037. When sending to a nickname, the recipient will\n"
- "receive a notice that he/she has a new memo. The target\n"
- "nickname/channel must be registered.\n"
- "Once the memo is read by its recipient, an automatic notification\n"
- "memo will be sent to the sender informing him/her that the memo\n"
- "has been read."));
- return true;
- }
-};
-
-class MSRSend : public Module
-{
- CommandMSRSend commandmsrsend;
-
- public:
- MSRSend(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandmsrsend(this)
- {
- if (!memoserv)
- throw ModuleException("No MemoServ!");
- }
-};
-
-MODULE_INIT(MSRSend)
diff --git a/modules/commands/ms_send.cpp b/modules/commands/ms_send.cpp
deleted file mode 100644
index b028706d3..000000000
--- a/modules/commands/ms_send.cpp
+++ /dev/null
@@ -1,88 +0,0 @@
-/* MemoServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-namespace
-{
- ServiceReference<MemoServService> memoserv("MemoServService", "MemoServ");
-}
-
-class CommandMSSend : public Command
-{
- public:
- CommandMSSend(Module *creator) : Command(creator, "memoserv/send", 2, 2)
- {
- this->SetDesc(_("Send a memo to a nick or channel"));
- this->SetSyntax(_("{\037nick\037 | \037channel\037} \037memo-text\037"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (!memoserv)
- return;
-
- const Anope::string &nick = params[0];
- const Anope::string &text = params[1];
-
- if (Anope::ReadOnly && !source.IsOper())
- {
- source.Reply(MEMO_SEND_DISABLED);
- return;
- }
-
- if (source.GetAccount()->HasExt("UNCONFIRMED"))
- {
- source.Reply(_("You must confirm your account before you may send a memo."));
- return;
- }
-
- MemoServService::MemoResult result = memoserv->Send(source.GetNick(), nick, text);
- if (result == MemoServService::MEMO_SUCCESS)
- {
- source.Reply(_("Memo sent to \002%s\002."), nick.c_str());
- Log(LOG_COMMAND, source, this) << "to send a memo to " << nick;
- }
- else if (result == MemoServService::MEMO_INVALID_TARGET)
- source.Reply(_("\002%s\002 is not a registered unforbidden nick or channel."), nick.c_str());
- else if (result == MemoServService::MEMO_TOO_FAST)
- source.Reply(_("Please wait %d seconds before using the %s command again."), Config->GetModule("memoserv")->Get<time_t>("senddelay"), source.command.c_str());
- else if (result == MemoServService::MEMO_TARGET_FULL)
- source.Reply(_("Sorry, %s currently has too many memos and cannot receive more."), nick.c_str());
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Sends the named \037nick\037 or \037channel\037 a memo containing\n"
- "\037memo-text\037. When sending to a nickname, the recipient will\n"
- "receive a notice that he/she has a new memo. The target\n"
- "nickname/channel must be registered."));
- return true;
- }
-};
-
-class MSSend : public Module
-{
- CommandMSSend commandmssend;
-
- public:
- MSSend(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandmssend(this)
- {
-
- if (!memoserv)
- throw ModuleException("No MemoServ!");
- }
-};
-
-MODULE_INIT(MSSend)
diff --git a/modules/commands/ms_sendall.cpp b/modules/commands/ms_sendall.cpp
deleted file mode 100644
index 4cc0f350c..000000000
--- a/modules/commands/ms_sendall.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-/* MemoServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-namespace
-{
- ServiceReference<MemoServService> memoserv("MemoServService", "MemoServ");
-}
-
-class CommandMSSendAll : public Command
-{
- public:
- CommandMSSendAll(Module *creator) : Command(creator, "memoserv/sendall", 1, 1)
- {
- this->SetDesc(_("Send a memo to all registered users"));
- this->SetSyntax(_("\037memo-text\037"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (!memoserv)
- return;
-
- const Anope::string &text = params[0];
-
- Log(LOG_ADMIN, source, this) << "to send " << text;
-
- for (nickcore_map::const_iterator it = NickCoreList->begin(), it_end = NickCoreList->end(); it != it_end; ++it)
- {
- const NickCore *nc = it->second;
-
- if (nc != source.nc)
- memoserv->Send(source.GetNick(), nc->display, text);
- }
-
- source.Reply(_("A massmemo has been sent to all registered users."));
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Sends all registered users a memo containing \037memo-text\037."));
- return true;
- }
-};
-
-class MSSendAll : public Module
-{
- CommandMSSendAll commandmssendall;
-
- public:
- MSSendAll(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandmssendall(this)
- {
- if (!memoserv)
- throw ModuleException("No MemoServ!");
- }
-};
-
-MODULE_INIT(MSSendAll)
diff --git a/modules/commands/ms_set.cpp b/modules/commands/ms_set.cpp
deleted file mode 100644
index d407132c7..000000000
--- a/modules/commands/ms_set.cpp
+++ /dev/null
@@ -1,315 +0,0 @@
-/* MemoServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class CommandMSSet : public Command
-{
- private:
- void DoNotify(CommandSource &source, const std::vector<Anope::string> &params, MemoInfo *mi)
- {
- const Anope::string &param = params[1];
- NickCore *nc = source.nc;
- BotInfo *MemoServ = Config->GetClient("MemoServ");
-
- if (!MemoServ)
- return;
-
- if (param.equals_ci("ON"))
- {
- nc->Extend<bool>("MEMO_SIGNON");
- nc->Extend<bool>("MEMO_RECEIVE");
- source.Reply(_("%s will now notify you of memos when you log on and when they are sent to you."), MemoServ->nick.c_str());
- }
- else if (param.equals_ci("LOGON"))
- {
- nc->Extend<bool>("MEMO_SIGNON");
- nc->Shrink<bool>("MEMO_RECEIVE");
- source.Reply(_("%s will now notify you of memos when you log on or unset /AWAY."), MemoServ->nick.c_str());
- }
- else if (param.equals_ci("NEW"))
- {
- nc->Shrink<bool>("MEMO_SIGNON");
- nc->Extend<bool>("MEMO_RECEIVE");
- source.Reply(_("%s will now notify you of memos when they are sent to you."), MemoServ->nick.c_str());
- }
- else if (param.equals_ci("MAIL"))
- {
- if (!nc->email.empty())
- {
- nc->Extend<bool>("MEMO_MAIL");
- source.Reply(_("You will now be informed about new memos via email."));
- }
- else
- source.Reply(_("There's no email address set for your nick."));
- }
- else if (param.equals_ci("NOMAIL"))
- {
- nc->Shrink<bool>("MEMO_MAIL");
- source.Reply(_("You will no longer be informed via email."));
- }
- else if (param.equals_ci("OFF"))
- {
- nc->Shrink<bool>("MEMO_SIGNON");
- nc->Shrink<bool>("MEMO_RECEIVE");
- nc->Shrink<bool>("MEMO_MAIL");
- source.Reply(_("%s will not send you any notification of memos."), MemoServ->nick.c_str());
- }
- else
- this->OnSyntaxError(source, "");
- }
-
- void DoLimit(CommandSource &source, const std::vector<Anope::string> &params, MemoInfo *mi)
- {
-
- Anope::string p1 = params[1];
- Anope::string p2 = params.size() > 2 ? params[2] : "";
- Anope::string p3 = params.size() > 3 ? params[3] : "";
- Anope::string user, chan;
- int16_t limit;
- NickCore *nc = source.nc;
- ChannelInfo *ci = NULL;
- bool is_servadmin = source.HasPriv("memoserv/set-limit");
-
- if (p1[0] == '#')
- {
- chan = p1;
- p1 = p2;
- p2 = p3;
- p3 = params.size() > 4 ? params[4] : "";
-
- ci = ChannelInfo::Find(chan);
- if (!ci)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, chan.c_str());
- return;
- }
- else if (!is_servadmin && !source.AccessFor(ci).HasPriv("MEMO"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
- mi = &ci->memos;
- }
- if (is_servadmin)
- {
- if (!p2.empty() && !p2.equals_ci("HARD") && chan.empty())
- {
- const NickAlias *na;
- if (!(na = NickAlias::Find(p1)))
- {
- source.Reply(NICK_X_NOT_REGISTERED, p1.c_str());
- return;
- }
- user = p1;
- mi = &na->nc->memos;
- nc = na->nc;
- p1 = p2;
- p2 = p3;
- }
- else if (p1.empty() || (!p1.is_pos_number_only() && !p1.equals_ci("NONE")) || (!p2.empty() && !p2.equals_ci("HARD")))
- {
- this->OnSyntaxError(source, "");
- return;
- }
- if (!chan.empty())
- {
- if (!p2.empty())
- ci->Extend<bool>("MEMO_HARDMAX");
- else
- ci->Shrink<bool>("MEMO_HARDMAX");
- }
- else
- {
- if (!p2.empty())
- nc->Extend<bool>("MEMO_HARDMAX");
- else
- nc->Shrink<bool>("MEMO_HARDMAX");
- }
- limit = -1;
- try
- {
- limit = convertTo<int16_t>(p1);
- }
- catch (const ConvertException &) { }
- }
- else
- {
- if (p1.empty() || !p2.empty() || !isdigit(p1[0]))
- {
- this->OnSyntaxError(source, "");
- return;
- }
- if (!chan.empty() && ci->HasExt("MEMO_HARDMAX"))
- {
- source.Reply(_("The memo limit for %s may not be changed."), chan.c_str());
- return;
- }
- else if (chan.empty() && nc->HasExt("MEMO_HARDMAX"))
- {
- source.Reply(_("You are not permitted to change your memo limit."));
- return;
- }
- int max_memos = Config->GetModule("memoserv")->Get<int>("maxmemos");
- limit = -1;
- try
- {
- limit = convertTo<int16_t>(p1);
- }
- catch (const ConvertException &) { }
- /* The first character is a digit, but we could still go negative
- * from overflow... watch out! */
- if (limit < 0 || (max_memos > 0 && limit > max_memos))
- {
- if (!chan.empty())
- source.Reply(_("You cannot set the memo limit for %s higher than %d."), chan.c_str(), max_memos);
- else
- source.Reply(_("You cannot set your memo limit higher than %d."), max_memos);
- return;
- }
- }
- mi->memomax = limit;
- if (limit > 0)
- {
- if (chan.empty() && nc == source.nc)
- source.Reply(_("Your memo limit has been set to \002%d\002."), limit);
- else
- source.Reply(_("Memo limit for %s set to \002%d\002."), !chan.empty() ? chan.c_str() : user.c_str(), limit);
- }
- else if (!limit)
- {
- if (chan.empty() && nc == source.nc)
- source.Reply(_("You will no longer be able to receive memos."));
- else
- source.Reply(_("Memo limit for %s set to \0020\002."), !chan.empty() ? chan.c_str() : user.c_str());
- }
- else
- {
- if (chan.empty() && nc == source.nc)
- source.Reply(_("Your memo limit has been disabled."));
- else
- source.Reply(_("Memo limit \002disabled\002 for %s."), !chan.empty() ? chan.c_str() : user.c_str());
- }
- return;
- }
- public:
- CommandMSSet(Module *creator) : Command(creator, "memoserv/set", 2, 5)
- {
- this->SetDesc(_("Set options related to memos"));
- this->SetSyntax(_("\037option\037 \037parameters\037"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &cmd = params[0];
- MemoInfo *mi = &source.nc->memos;
-
- if (Anope::ReadOnly)
- source.Reply(_("Sorry, memo option setting is temporarily disabled."));
- else if (cmd.equals_ci("NOTIFY"))
- return this->DoNotify(source, params, mi);
- else if (cmd.equals_ci("LIMIT"))
- return this->DoLimit(source, params, mi);
- else
- {
- this->OnSyntaxError(source, "");
- }
-
- return;
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- if (subcommand.empty())
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Sets various memo options. \037option\037 can be one of:\n"
- " \n"
- " NOTIFY Changes when you will be notified about\n"
- " new memos (only for nicknames)\n"
- " LIMIT Sets the maximum number of memos you can\n"
- " receive\n"
- " \n"
- "Type \002%s%s HELP %s \037option\037\002 for more information\n"
- "on a specific option."), Config->StrictPrivmsg.c_str(), source.service->nick.c_str(), source.command.c_str());
- }
- else if (subcommand.equals_ci("NOTIFY"))
- source.Reply(_("Syntax: \002NOTIFY {ON | LOGON | NEW | MAIL | NOMAIL | OFF}\002\n"
- " \n"
- "Changes when you will be notified about new memos:\n"
- " \n"
- " ON You will be notified of memos when you log on,\n"
- " when you unset /AWAY, and when they are sent\n"
- " to you.\n"
- " LOGON You will only be notified of memos when you log\n"
- " on or when you unset /AWAY.\n"
- " NEW You will only be notified of memos when they\n"
- " are sent to you.\n"
- " MAIL You will be notified of memos by email as well as\n"
- " any other settings you have.\n"
- " NOMAIL You will not be notified of memos by email.\n"
- " OFF You will not receive any notification of memos.\n"
- " \n"
- "\002ON\002 is essentially \002LOGON\002 and \002NEW\002 combined."));
- else if (subcommand.equals_ci("LIMIT"))
- {
- int max_memos = Config->GetModule("memoserv")->Get<int>("maxmemos");
- if (source.IsServicesOper())
- source.Reply(_("Syntax: \002LIMIT [\037user\037 | \037channel\037] {\037limit\037 | NONE} [HARD]\002\n"
- " \n"
- "Sets the maximum number of memos a user or channel is\n"
- "allowed to have. Setting the limit to 0 prevents the user\n"
- "from receiving any memos; setting it to \002NONE\002 allows the\n"
- "user to receive and keep as many memos as they want. If\n"
- "you do not give a nickname or channel, your own limit is\n"
- "set.\n"
- " \n"
- "Adding \002HARD\002 prevents the user from changing the limit. Not\n"
- "adding \002HARD\002 has the opposite effect, allowing the user to\n"
- "change the limit (even if a previous limit was set with\n"
- "\002HARD\002).\n"
- " \n"
- "This use of the \002SET LIMIT\002 command is limited to \002Services\002\n"
- "\002Operators\002. Other users may only enter a limit for themselves\n"
- "or a channel on which they have such privileges, may not\n"
- "remove their limit, may not set a limit above %d, and may\n"
- "not set a hard limit."), max_memos);
- else
- source.Reply(_("Syntax: \002LIMIT [\037channel\037] \037limit\037\002\n"
- " \n"
- "Sets the maximum number of memos you (or the given channel)\n"
- "are allowed to have. If you set this to 0, no one will be\n"
- "able to send any memos to you. However, you cannot set\n"
- "this any higher than %d."), max_memos);
- }
- else
- return false;
-
- return true;
- }
-};
-
-class MSSet : public Module
-{
- CommandMSSet commandmsset;
- SerializableExtensibleItem<bool> memo_signon, memo_receive, memo_mail, memo_hardmax;
-
- public:
- MSSet(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandmsset(this), memo_signon(this, "MEMO_SIGNON"), memo_receive(this, "MEMO_RECEIVE"), memo_mail(this, "MEMO_MAIL"),
- memo_hardmax(this, "MEMO_HARDMAX")
- {
-
- }
-};
-
-MODULE_INIT(MSSet)
diff --git a/modules/commands/ms_staff.cpp b/modules/commands/ms_staff.cpp
deleted file mode 100644
index b3554fab8..000000000
--- a/modules/commands/ms_staff.cpp
+++ /dev/null
@@ -1,67 +0,0 @@
-/* MemoServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-namespace
-{
- ServiceReference<MemoServService> memoserv("MemoServService", "MemoServ");
-}
-
-class CommandMSStaff : public Command
-{
- public:
- CommandMSStaff(Module *creator) : Command(creator, "memoserv/staff", 1, 1)
- {
- this->SetDesc(_("Send a memo to all opers/admins"));
- this->SetSyntax(_("\037memo-text\037"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (!memoserv)
- return;
-
- const Anope::string &text = params[0];
-
- for (nickcore_map::const_iterator it = NickCoreList->begin(), it_end = NickCoreList->end(); it != it_end; ++it)
- {
- const NickCore *nc = it->second;
-
- if (source.nc != nc && nc->IsServicesOper())
- memoserv->Send(source.GetNick(), nc->display, text, true);
- }
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Sends all services staff a memo containing \037memo-text\037."));
-
- return true;
- }
-};
-
-class MSStaff : public Module
-{
- CommandMSStaff commandmsstaff;
-
- public:
- MSStaff(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandmsstaff(this)
- {
- if (!memoserv)
- throw ModuleException("No MemoServ!");
- }
-};
-
-MODULE_INIT(MSStaff)
diff --git a/modules/commands/ns_access.cpp b/modules/commands/ns_access.cpp
deleted file mode 100644
index e54252c65..000000000
--- a/modules/commands/ns_access.cpp
+++ /dev/null
@@ -1,206 +0,0 @@
-/* NickServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class CommandNSAccess : public Command
-{
- private:
- void DoAdd(CommandSource &source, NickCore *nc, const Anope::string &mask)
- {
- if (mask.empty())
- {
- this->OnSyntaxError(source, "ADD");
- return;
- }
-
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- if (nc->access.size() >= Config->GetModule(this->owner)->Get<unsigned>("accessmax", "32"))
- {
- source.Reply(_("Sorry, the maximum of %d access entries has been reached."), Config->GetModule(this->owner)->Get<unsigned>("accessmax"));
- return;
- }
-
- if (nc->FindAccess(mask))
- {
- source.Reply(_("Mask \002%s\002 already present on %s's access list."), mask.c_str(), nc->display.c_str());
- return;
- }
-
- nc->AddAccess(mask);
- Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to ADD mask " << mask << " to " << nc->display;
- source.Reply(_("\002%s\002 added to %s's access list."), mask.c_str(), nc->display.c_str());
-
- return;
- }
-
- void DoDel(CommandSource &source, NickCore *nc, const Anope::string &mask)
- {
- if (mask.empty())
- {
- this->OnSyntaxError(source, "DEL");
- return;
- }
-
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- if (!nc->FindAccess(mask))
- {
- source.Reply(_("\002%s\002 not found on %s's access list."), mask.c_str(), nc->display.c_str());
- return;
- }
-
- nc->EraseAccess(mask);
- Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to DELETE mask " << mask << " from " << nc->display;
- source.Reply(_("\002%s\002 deleted from %s's access list."), mask.c_str(), nc->display.c_str());
-
- return;
- }
-
- void DoList(CommandSource &source, NickCore *nc, const Anope::string &mask)
- {
- unsigned i, end;
-
- if (nc->access.empty())
- {
- source.Reply(_("%s's access list is empty."), nc->display.c_str());
- return;
- }
-
- source.Reply(_("Access list for %s:"), nc->display.c_str());
- for (i = 0, end = nc->access.size(); i < end; ++i)
- {
- Anope::string access = nc->GetAccess(i);
- if (!mask.empty() && !Anope::Match(access, mask))
- continue;
- source.Reply(" %s", access.c_str());
- }
-
- return;
- }
- public:
- CommandNSAccess(Module *creator) : Command(creator, "nickserv/access", 1, 3)
- {
- this->SetDesc(_("Modify the list of authorized addresses"));
- this->SetSyntax(_("ADD [\037nickname\037] \037mask\037"));
- this->SetSyntax(_("DEL [\037nickname\037] \037mask\037"));
- this->SetSyntax(_("LIST [\037nickname\037]"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &cmd = params[0];
- Anope::string nick, mask;
-
- if (cmd.equals_ci("LIST"))
- nick = params.size() > 1 ? params[1] : "";
- else
- {
- nick = params.size() == 3 ? params[1] : "";
- mask = params.size() > 1 ? params[params.size() - 1] : "";
- }
-
- NickCore *nc;
- if (!nick.empty())
- {
- const NickAlias *na = NickAlias::Find(nick);
- if (na == NULL)
- {
- source.Reply(NICK_X_NOT_REGISTERED, nick.c_str());
- return;
- }
- else if (na->nc != source.GetAccount() && !source.HasPriv("nickserv/access"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
- else if (Config->GetModule("nickserv")->Get<bool>("secureadmins", "yes") && source.GetAccount() != na->nc && na->nc->IsServicesOper() && !cmd.equals_ci("LIST"))
- {
- source.Reply(_("You may view but not modify the access list of other Services Operators."));
- return;
- }
-
- nc = na->nc;
- }
- else
- nc = source.nc;
-
- if (!mask.empty() && (mask.find('@') == Anope::string::npos || mask.find('!') != Anope::string::npos))
- {
- source.Reply(BAD_USERHOST_MASK);
- source.Reply(MORE_INFO, Config->StrictPrivmsg.c_str(), source.service->nick.c_str(), source.command.c_str());
- }
- else if (cmd.equals_ci("LIST"))
- return this->DoList(source, nc, mask);
- else if (nc->HasExt("NS_SUSPENDED"))
- source.Reply(NICK_X_SUSPENDED, nc->display.c_str());
- else if (cmd.equals_ci("ADD"))
- return this->DoAdd(source, nc, mask);
- else if (cmd.equals_ci("DEL"))
- return this->DoDel(source, nc, mask);
- else
- this->OnSyntaxError(source, "");
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Modifies or displays the access list for your nick. This\n"
- "is the list of addresses which will be automatically\n"
- "recognized by %s as allowed to use the nick. If\n"
- "you want to use the nick from a different address, you\n"
- "need to send an \002IDENTIFY\002 command to make %s\n"
- "recognize you. Services Operators may provide a nick\n"
- "to modify other users' access lists.\n"
- " \n"
- "Examples:\n"
- " \n"
- " \002ACCESS ADD anyone@*.bepeg.com\002\n"
- " Allows access to user \002anyone\002 from any machine in\n"
- " the \002bepeg.com\002 domain.\n"
- " \n"
- " \002ACCESS DEL anyone@*.bepeg.com\002\n"
- " Reverses the previous command.\n"
- " \n"
- " \002ACCESS LIST\002\n"
- " Displays the current access list."), source.service->nick.c_str(), source.service->nick.c_str());
- return true;
- }
-};
-
-class NSAccess : public Module
-{
- CommandNSAccess commandnsaccess;
-
- public:
- NSAccess(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandnsaccess(this)
- {
- }
-
- void OnNickRegister(User *u, NickAlias *na, const Anope::string &) anope_override
- {
- if (u && Config->GetModule(this)->Get<bool>("addaccessonreg"))
- na->nc->AddAccess(u->Mask());
- }
-};
-
-MODULE_INIT(NSAccess)
diff --git a/modules/commands/ns_ajoin.cpp b/modules/commands/ns_ajoin.cpp
deleted file mode 100644
index b62029fad..000000000
--- a/modules/commands/ns_ajoin.cpp
+++ /dev/null
@@ -1,405 +0,0 @@
-/* NickServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-struct AJoinEntry;
-
-struct AJoinList : Serialize::Checker<std::vector<AJoinEntry *> >
-{
- AJoinList(Extensible *) : Serialize::Checker<std::vector<AJoinEntry *> >("AJoinEntry") { }
- ~AJoinList();
-};
-
-struct AJoinEntry : Serializable
-{
- Serialize::Reference<NickCore> owner;
- Anope::string channel;
- Anope::string key;
-
- AJoinEntry(Extensible *) : Serializable("AJoinEntry") { }
-
- ~AJoinEntry()
- {
- AJoinList *channels = owner->GetExt<AJoinList>("ajoinlist");
- if (channels)
- {
- std::vector<AJoinEntry *>::iterator it = std::find((*channels)->begin(), (*channels)->end(), this);
- if (it != (*channels)->end())
- (*channels)->erase(it);
- }
- }
-
- void Serialize(Serialize::Data &sd) const anope_override
- {
- if (!this->owner)
- return;
-
- sd["owner"] << this->owner->display;
- sd["channel"] << this->channel;
- sd["key"] << this->key;
- }
-
- static Serializable* Unserialize(Serializable *obj, Serialize::Data &sd)
- {
- Anope::string sowner;
-
- sd["owner"] >> sowner;
-
- NickCore *nc = NickCore::Find(sowner);
- if (nc == NULL)
- return NULL;
-
- AJoinEntry *aj;
- if (obj)
- aj = anope_dynamic_static_cast<AJoinEntry *>(obj);
- else
- {
- aj = new AJoinEntry(nc);
- aj->owner = nc;
- }
-
- sd["channel"] >> aj->channel;
- sd["key"] >> aj->key;
-
- if (!obj)
- {
- AJoinList *channels = nc->Require<AJoinList>("ajoinlist");
- (*channels)->push_back(aj);
- }
-
- return aj;
- }
-};
-
-AJoinList::~AJoinList()
-{
- for (unsigned i = 0; i < (*this)->size(); ++i)
- delete (*this)->at(i);
-}
-
-class CommandNSAJoin : public Command
-{
- void DoList(CommandSource &source, NickCore *nc)
- {
- AJoinList *channels = nc->Require<AJoinList>("ajoinlist");
-
- if ((*channels)->empty())
- source.Reply(_("%s's auto join list is empty."), nc->display.c_str());
- else
- {
- ListFormatter list(source.GetAccount());
- list.AddColumn(_("Number")).AddColumn(_("Channel")).AddColumn(_("Key"));
- for (unsigned i = 0; i < (*channels)->size(); ++i)
- {
- AJoinEntry *aj = (*channels)->at(i);
- ListFormatter::ListEntry entry;
- entry["Number"] = stringify(i + 1);
- entry["Channel"] = aj->channel;
- entry["Key"] = aj->key;
- list.AddEntry(entry);
- }
-
- source.Reply(_("%s's auto join list:"), nc->display.c_str());
-
- std::vector<Anope::string> replies;
- list.Process(replies);
-
- for (unsigned i = 0; i < replies.size(); ++i)
- source.Reply(replies[i]);
- }
- }
-
- void DoAdd(CommandSource &source, NickCore *nc, const Anope::string &chans, const Anope::string &keys)
- {
- AJoinList *channels = nc->Require<AJoinList>("ajoinlist");
-
- Anope::string addedchans;
- Anope::string alreadyadded;
- Anope::string invalidkey;
- commasepstream ksep(keys, true);
- commasepstream csep(chans);
- for (Anope::string chan, key; csep.GetToken(chan);)
- {
- ksep.GetToken(key);
-
- unsigned i = 0;
- for (; i < (*channels)->size(); ++i)
- if ((*channels)->at(i)->channel.equals_ci(chan))
- break;
-
- if ((*channels)->size() >= Config->GetModule(this->owner)->Get<unsigned>("ajoinmax"))
- {
- source.Reply(_("Sorry, the maximum of %d auto join entries has been reached."), Config->GetModule(this->owner)->Get<unsigned>("ajoinmax"));
- return;
- }
- else if (i != (*channels)->size())
- alreadyadded += chan + ", ";
- else if (IRCD->IsChannelValid(chan) == false)
- source.Reply(CHAN_X_INVALID, chan.c_str());
- else
- {
- Channel *c = Channel::Find(chan);
- Anope::string k;
- if (c && c->GetParam("KEY", k) && key != k)
- {
- invalidkey += chan + ", ";
- continue;
- }
-
- AJoinEntry *entry = new AJoinEntry(nc);
- entry->owner = nc;
- entry->channel = chan;
- entry->key = key;
- (*channels)->push_back(entry);
- addedchans += chan + ", ";
- }
- }
-
- if (!alreadyadded.empty())
- {
- alreadyadded = alreadyadded.substr(0, alreadyadded.length() - 2);
- source.Reply(_("%s is already on %s's auto join list."), alreadyadded.c_str(), nc->display.c_str());
- }
-
- if (!invalidkey.empty())
- {
- invalidkey = invalidkey.substr(0, invalidkey.length() - 2);
- source.Reply(_("%s had an invalid key specified, and was thus ignored."), invalidkey.c_str());
- }
-
- if (addedchans.empty())
- return;
-
- addedchans = addedchans.substr(0, addedchans.length() - 2);
- Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to ADD channel " << addedchans << " to " << nc->display;
- source.Reply(_("%s added to %s's auto join list."), addedchans.c_str(), nc->display.c_str());
- }
-
- void DoDel(CommandSource &source, NickCore *nc, const Anope::string &chans)
- {
- AJoinList *channels = nc->Require<AJoinList>("ajoinlist");
- Anope::string delchans;
- Anope::string notfoundchans;
- commasepstream sep(chans);
-
- for (Anope::string chan; sep.GetToken(chan);)
- {
- unsigned i = 0;
- for (; i < (*channels)->size(); ++i)
- if ((*channels)->at(i)->channel.equals_ci(chan))
- break;
-
- if (i == (*channels)->size())
- notfoundchans += chan + ", ";
- else
- {
- delete (*channels)->at(i);
- delchans += chan + ", ";
- }
- }
-
- if (!notfoundchans.empty())
- {
- notfoundchans = notfoundchans.substr(0, notfoundchans.length() - 2);
- source.Reply(_("%s was not found on %s's auto join list."), notfoundchans.c_str(), nc->display.c_str());
- }
-
- if (delchans.empty())
- return;
-
- delchans = delchans.substr(0, delchans.length() - 2);
- Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to DELETE channel " << delchans << " from " << nc->display;
- source.Reply(_("%s was removed from %s's auto join list."), delchans.c_str(), nc->display.c_str());
-
- if ((*channels)->empty())
- nc->Shrink<AJoinList>("ajoinlist");
- }
-
- public:
- CommandNSAJoin(Module *creator) : Command(creator, "nickserv/ajoin", 1, 4)
- {
- this->SetDesc(_("Manage your auto join list"));
- this->SetSyntax(_("ADD [\037nickname\037] \037channel\037 [\037key\037]"));
- this->SetSyntax(_("DEL [\037nickname\037] \037channel\037"));
- this->SetSyntax(_("LIST [\037nickname\037]"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &cmd = params[0];
- Anope::string nick, param, param2;
-
- if (cmd.equals_ci("LIST"))
- nick = params.size() > 1 ? params[1] : "";
- else
- nick = (params.size() > 2 && IRCD->IsChannelValid(params[2])) ? params[1] : "";
-
- NickCore *nc;
- if (!nick.empty())
- {
- const NickAlias *na = NickAlias::Find(nick);
- if (na == NULL)
- {
- source.Reply(NICK_X_NOT_REGISTERED, nick.c_str());
- return;
- }
- else if (na->nc != source.GetAccount() && !source.HasCommand("nickserv/ajoin"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- nc = na->nc;
- param = params.size() > 2 ? params[2] : "";
- param2 = params.size() > 3 ? params[3] : "";
- }
- else
- {
- nc = source.nc;
- param = params.size() > 1 ? params[1] : "";
- param2 = params.size() > 2 ? params[2] : "";
- }
-
- if (cmd.equals_ci("LIST"))
- return this->DoList(source, nc);
- else if (nc->HasExt("NS_SUSPENDED"))
- source.Reply(NICK_X_SUSPENDED, nc->display.c_str());
- else if (param.empty())
- this->OnSyntaxError(source, "");
- else if (Anope::ReadOnly)
- source.Reply(READ_ONLY_MODE);
- else if (cmd.equals_ci("ADD"))
- return this->DoAdd(source, nc, param, param2);
- else if (cmd.equals_ci("DEL"))
- return this->DoDel(source, nc, param);
- else
- this->OnSyntaxError(source, "");
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("This command manages your auto join list. When you identify\n"
- "you will automatically join the channels on your auto join list.\n"
- "Services Operators may provide a nick to modify other users'\n"
- "auto join lists."));
- return true;
- }
-};
-
-class NSAJoin : public Module
-{
- CommandNSAJoin commandnsajoin;
- ExtensibleItem<AJoinList> ajoinlist;
- Serialize::Type ajoinentry_type;
-
- public:
- NSAJoin(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandnsajoin(this), ajoinlist(this, "ajoinlist"),
- ajoinentry_type("AJoinEntry", AJoinEntry::Unserialize)
- {
-
- if (!IRCD || !IRCD->CanSVSJoin)
- throw ModuleException("Your IRCd does not support SVSJOIN");
-
- }
-
- void OnUserLogin(User *u) anope_override
- {
- BotInfo *NickServ = Config->GetClient("NickServ");
- if (!NickServ)
- return;
-
- AJoinList *channels = u->Account()->GetExt<AJoinList>("ajoinlist");
- if (channels == NULL)
- return;
-
- /* Set +r now, so we can ajoin users into +R channels */
- ModeManager::ProcessModes();
-
- for (unsigned i = 0; i < (*channels)->size(); ++i)
- {
- AJoinEntry *entry = (*channels)->at(i);
- Channel *c = Channel::Find(entry->channel);
- ChannelInfo *ci;
-
- if (c)
- ci = c->ci;
- else
- ci = ChannelInfo::Find(entry->channel);
-
- bool need_invite = false;
- Anope::string key = entry->key;
- AccessGroup u_access;
-
- if (ci != NULL)
- {
- if (ci->HasExt("CS_SUSPENDED"))
- continue;
- u_access = ci->AccessFor(u);
- }
- if (c != NULL)
- {
- if (c->FindUser(u) != NULL)
- continue;
- else if (c->HasMode("OPERONLY") && !u->HasMode("OPER"))
- continue;
- else if (c->HasMode("ADMINONLY") && !u->HasMode("ADMIN"))
- continue;
- else if (c->HasMode("SSL") && !(u->HasMode("SSL") || u->HasExt("ssl")))
- continue;
- else if (c->MatchesList(u, "BAN") == true && c->MatchesList(u, "EXCEPT") == false)
- need_invite = true;
- else if (c->HasMode("INVITE") && c->MatchesList(u, "INVITEOVERRIDE") == false)
- need_invite = true;
-
- if (c->HasMode("KEY"))
- {
- Anope::string k;
- if (c->GetParam("KEY", k))
- {
- if (u_access.HasPriv("GETKEY"))
- key = k;
- else if (key != k)
- need_invite = true;
- }
- }
- if (c->HasMode("LIMIT"))
- {
- Anope::string l;
- if (c->GetParam("LIMIT", l))
- {
- try
- {
- unsigned limit = convertTo<unsigned>(l);
- if (c->users.size() >= limit)
- need_invite = true;
- }
- catch (const ConvertException &) { }
- }
- }
- }
-
- if (need_invite && c != NULL)
- {
- if (!u_access.HasPriv("INVITE"))
- continue;
- IRCD->SendInvite(NickServ, c, u);
- }
-
- IRCD->SendSVSJoin(NickServ, u, entry->channel, key);
- }
- }
-};
-
-MODULE_INIT(NSAJoin)
diff --git a/modules/commands/ns_alist.cpp b/modules/commands/ns_alist.cpp
deleted file mode 100644
index 5f5efc7ea..000000000
--- a/modules/commands/ns_alist.cpp
+++ /dev/null
@@ -1,149 +0,0 @@
-/* NickServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class CommandNSAList : public Command
-{
- static bool ChannelSort(ChannelInfo *ci1, ChannelInfo *ci2)
- {
- return ci::less()(ci1->name, ci2->name);
- }
-
- public:
- CommandNSAList(Module *creator) : Command(creator, "nickserv/alist", 0, 2)
- {
- this->SetDesc(_("List channels you have access on"));
- this->SetSyntax(_("[\037nickname\037]"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- Anope::string nick = source.GetNick();
- NickCore *nc = source.nc;
-
- if (params.size() && source.HasPriv("nickserv/alist"))
- {
- nick = params[0];
- const NickAlias *na = NickAlias::Find(nick);
- if (!na)
- {
- source.Reply(NICK_X_NOT_REGISTERED, nick.c_str());
- return;
- }
- nc = na->nc;
- }
-
- ListFormatter list(source.GetAccount());
- int chan_count = 0;
-
- list.AddColumn(_("Number")).AddColumn(_("Channel")).AddColumn(_("Access")).AddColumn(_("Description"));
-
- std::deque<ChannelInfo *> queue;
- nc->GetChannelReferences(queue);
- std::sort(queue.begin(), queue.end(), ChannelSort);
-
- for (unsigned i = 0; i < queue.size(); ++i)
- {
- ChannelInfo *ci = queue[i];
- ListFormatter::ListEntry entry;
-
- if (ci->GetFounder() == nc)
- {
- ++chan_count;
- entry["Number"] = stringify(chan_count);
- entry["Channel"] = (ci->HasExt("CS_NO_EXPIRE") ? "!" : "") + ci->name;
- entry["Access"] = Language::Translate(source.GetAccount(), _("Founder"));
- entry["Description"] = ci->desc;
- list.AddEntry(entry);
- continue;
- }
-
- if (ci->GetSuccessor() == nc)
- {
- ++chan_count;
- entry["Number"] = stringify(chan_count);
- entry["Channel"] = (ci->HasExt("CS_NO_EXPIRE") ? "!" : "") + ci->name;
- entry["Access"] = Language::Translate(source.GetAccount(), _("Successor"));
- entry["Description"] = ci->desc;
- list.AddEntry(entry);
- continue;
- }
-
- AccessGroup access = ci->AccessFor(nc, false);
- if (access.empty())
- continue;
-
- ++chan_count;
-
- entry["Number"] = stringify(chan_count);
- entry["Channel"] = (ci->HasExt("CS_NO_EXPIRE") ? "!" : "") + ci->name;
- for (unsigned j = 0; j < access.paths.size(); ++j)
- {
- ChanAccess::Path &p = access.paths[j];
-
- // not interested in indirect access
- if (p.size() != 1)
- continue;
-
- ChanAccess *a = p[0];
- entry["Access"] = entry["Access"] + ", " + a->AccessSerialize();
- }
- entry["Access"] = entry["Access"].substr(2);
- entry["Description"] = ci->desc;
- list.AddEntry(entry);
- }
-
- std::vector<Anope::string> replies;
- list.Process(replies);
-
- if (!chan_count)
- {
- source.Reply(_("\002%s\002 has no access in any channels."), nc->display.c_str());
- }
- else
- {
- source.Reply(_("Channels that \002%s\002 has access on:"), nc->display.c_str());
-
- for (unsigned i = 0; i < replies.size(); ++i)
- source.Reply(replies[i]);
-
- source.Reply(_("End of list - %d channels shown."), chan_count);
- }
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Lists all channels you have access on.\n"
- " \n"
- "Channels that have the \037NOEXPIRE\037 option set will be\n"
- "prefixed by an exclamation mark. The nickname parameter is\n"
- "limited to Services Operators"));
-
- return true;
- }
-};
-
-class NSAList : public Module
-{
- CommandNSAList commandnsalist;
-
- public:
- NSAList(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandnsalist(this)
- {
-
- }
-};
-
-MODULE_INIT(NSAList)
diff --git a/modules/commands/ns_cert.cpp b/modules/commands/ns_cert.cpp
deleted file mode 100644
index 6fa5decab..000000000
--- a/modules/commands/ns_cert.cpp
+++ /dev/null
@@ -1,407 +0,0 @@
-/* NickServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-#include "modules/ns_cert.h"
-
-static Anope::hash_map<NickCore *> certmap;
-
-struct CertServiceImpl : CertService
-{
- CertServiceImpl(Module *o) : CertService(o) { }
-
- NickCore* FindAccountFromCert(const Anope::string &cert) anope_override
- {
- Anope::hash_map<NickCore *>::iterator it = certmap.find(cert);
- if (it != certmap.end())
- return it->second;
- return NULL;
- }
-};
-
-struct NSCertListImpl : NSCertList
-{
- Serialize::Reference<NickCore> nc;
- std::vector<Anope::string> certs;
-
- public:
- NSCertListImpl(Extensible *obj) : nc(anope_dynamic_static_cast<NickCore *>(obj)) { }
-
- ~NSCertListImpl()
- {
- ClearCert();
- }
-
- /** Add an entry to the nick's certificate list
- *
- * @param entry The fingerprint to add to the cert list
- *
- * Adds a new entry into the cert list.
- */
- void AddCert(const Anope::string &entry) anope_override
- {
- this->certs.push_back(entry);
- certmap[entry] = nc;
- FOREACH_MOD(OnNickAddCert, (this->nc, entry));
- }
-
- /** Get an entry from the nick's cert list by index
- *
- * @param entry Index in the certificaate list vector to retrieve
- * @return The fingerprint entry of the given index if within bounds, an empty string if the vector is empty or the index is out of bounds
- *
- * Retrieves an entry from the certificate list corresponding to the given index.
- */
- Anope::string GetCert(unsigned entry) const anope_override
- {
- if (entry >= this->certs.size())
- return "";
- return this->certs[entry];
- }
-
- unsigned GetCertCount() const anope_override
- {
- return this->certs.size();
- }
-
- /** Find an entry in the nick's cert list
- *
- * @param entry The fingerprint to search for
- * @return True if the fingerprint is found in the cert list, false otherwise
- *
- * Search for an fingerprint within the cert list.
- */
- bool FindCert(const Anope::string &entry) const anope_override
- {
- return std::find(this->certs.begin(), this->certs.end(), entry) != this->certs.end();
- }
-
- /** Erase a fingerprint from the nick's certificate list
- *
- * @param entry The fingerprint to remove
- *
- * Removes the specified fingerprint from the cert list.
- */
- void EraseCert(const Anope::string &entry) anope_override
- {
- std::vector<Anope::string>::iterator it = std::find(this->certs.begin(), this->certs.end(), entry);
- if (it != this->certs.end())
- {
- FOREACH_MOD(OnNickEraseCert, (this->nc, entry));
- certmap.erase(entry);
- this->certs.erase(it);
- }
- }
-
- /** Clears the entire nick's cert list
- *
- * Deletes all the memory allocated in the certificate list vector and then clears the vector.
- */
- void ClearCert() anope_override
- {
- FOREACH_MOD(OnNickClearCert, (this->nc));
- for (unsigned i = 0; i < certs.size(); ++i)
- certmap.erase(certs[i]);
- this->certs.clear();
- }
-
- void Check() anope_override
- {
- if (this->certs.empty())
- nc->Shrink<NSCertList>("certificates");
- }
-
- struct ExtensibleItem : ::ExtensibleItem<NSCertListImpl>
- {
- ExtensibleItem(Module *m, const Anope::string &ename) : ::ExtensibleItem<NSCertListImpl>(m, ename) { }
-
- void ExtensibleSerialize(const Extensible *e, const Serializable *s, Serialize::Data &data) const anope_override
- {
- if (s->GetSerializableType()->GetName() != "NickCore")
- return;
-
- const NickCore *n = anope_dynamic_static_cast<const NickCore *>(e);
- NSCertList *c = this->Get(n);
- if (c == NULL || !c->GetCertCount())
- return;
-
- for (unsigned i = 0; i < c->GetCertCount(); ++i)
- data["cert"] << c->GetCert(i) << " ";
- }
-
- void ExtensibleUnserialize(Extensible *e, Serializable *s, Serialize::Data &data) anope_override
- {
- if (s->GetSerializableType()->GetName() != "NickCore")
- return;
-
- NickCore *n = anope_dynamic_static_cast<NickCore *>(e);
- NSCertListImpl *c = this->Require(n);
-
- Anope::string buf;
- data["cert"] >> buf;
- spacesepstream sep(buf);
- for (unsigned i = 0; i < c->certs.size(); ++i)
- certmap.erase(c->certs[i]);
- c->certs.clear();
- while (sep.GetToken(buf))
- {
- c->certs.push_back(buf);
- certmap[buf] = n;
- }
- }
- };
-};
-
-class CommandNSCert : public Command
-{
- private:
- void DoAdd(CommandSource &source, NickCore *nc, Anope::string certfp)
- {
- NSCertList *cl = nc->Require<NSCertList>("certificates");
- unsigned max = Config->GetModule(this->owner)->Get<unsigned>("max", "5");
-
- if (cl->GetCertCount() >= max)
- {
- source.Reply(_("Sorry, the maximum of %d certificate entries has been reached."), max);
- return;
- }
-
- if (source.GetAccount() == nc)
- {
- User *u = source.GetUser();
-
- if (!u || u->fingerprint.empty())
- {
- source.Reply(_("You are not using a client certificate."));
- return;
- }
-
- certfp = u->fingerprint;
- }
-
- if (cl->FindCert(certfp))
- {
- source.Reply(_("Fingerprint \002%s\002 already present on %s's certificate list."), certfp.c_str(), nc->display.c_str());
- return;
- }
-
- if (certmap.find(certfp) != certmap.end())
- {
- source.Reply(_("Fingerprint \002%s\002 is already in use."), certfp.c_str());
- return;
- }
-
- cl->AddCert(certfp);
- Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to ADD certificate fingerprint " << certfp << " to " << nc->display;
- source.Reply(_("\002%s\002 added to %s's certificate list."), certfp.c_str(), nc->display.c_str());
- }
-
- void DoDel(CommandSource &source, NickCore *nc, Anope::string certfp)
- {
- NSCertList *cl = nc->Require<NSCertList>("certificates");
-
- if (certfp.empty())
- {
- User *u = source.GetUser();
- if (u)
- certfp = u->fingerprint;
- }
-
- if (certfp.empty())
- {
- this->OnSyntaxError(source, "DEL");
- return;
- }
-
- if (!cl->FindCert(certfp))
- {
- source.Reply(_("\002%s\002 not found on %s's certificate list."), certfp.c_str(), nc->display.c_str());
- return;
- }
-
- cl->EraseCert(certfp);
- cl->Check();
- Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to DELETE certificate fingerprint " << certfp << " from " << nc->display;
- source.Reply(_("\002%s\002 deleted from %s's certificate list."), certfp.c_str(), nc->display.c_str());
- }
-
- void DoList(CommandSource &source, const NickCore *nc)
- {
- NSCertList *cl = nc->GetExt<NSCertList>("certificates");
-
- if (!cl || !cl->GetCertCount())
- {
- source.Reply(_("%s's certificate list is empty."), nc->display.c_str());
- return;
- }
-
- source.Reply(_("Certificate list for %s:"), nc->display.c_str());
- for (unsigned i = 0; i < cl->GetCertCount(); ++i)
- {
- Anope::string fingerprint = cl->GetCert(i);
- source.Reply(" %s", fingerprint.c_str());
- }
- }
-
- public:
- CommandNSCert(Module *creator) : Command(creator, "nickserv/cert", 1, 3)
- {
- this->SetDesc(_("Modify the nickname client certificate list"));
- this->SetSyntax(_("ADD [\037nickname\037] [\037fingerprint\037]"));
- this->SetSyntax(_("DEL [\037nickname\037] \037fingerprint\037"));
- this->SetSyntax(_("LIST [\037nickname\037]"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &cmd = params[0];
- Anope::string nick, certfp;
-
- if (cmd.equals_ci("LIST"))
- nick = params.size() > 1 ? params[1] : "";
- else
- {
- nick = params.size() == 3 ? params[1] : "";
- certfp = params.size() > 1 ? params[params.size() - 1] : "";
- }
-
- NickCore *nc;
- if (!nick.empty())
- {
- const NickAlias *na = NickAlias::Find(nick);
- if (na == NULL)
- {
- source.Reply(NICK_X_NOT_REGISTERED, nick.c_str());
- return;
- }
- else if (na->nc != source.GetAccount() && !source.HasPriv("nickserv/access"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
- else if (Config->GetModule("nickserv")->Get<bool>("secureadmins", "yes") && source.GetAccount() != na->nc && na->nc->IsServicesOper() && !cmd.equals_ci("LIST"))
- {
- source.Reply(_("You may view but not modify the certificate list of other Services Operators."));
- return;
- }
-
- nc = na->nc;
- }
- else
- nc = source.nc;
-
- if (cmd.equals_ci("LIST"))
- return this->DoList(source, nc);
- else if (nc->HasExt("NS_SUSPENDED"))
- source.Reply(NICK_X_SUSPENDED, nc->display.c_str());
- else if (Anope::ReadOnly)
- source.Reply(READ_ONLY_MODE);
- else if (cmd.equals_ci("ADD"))
- return this->DoAdd(source, nc, certfp);
- else if (cmd.equals_ci("DEL"))
- return this->DoDel(source, nc, certfp);
- else
- this->OnSyntaxError(source, "");
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Modifies or displays the certificate list for your nick.\n"
- "If you connect to IRC and provide a client certificate with a\n"
- "matching fingerprint in the cert list, you will be\n"
- "automatically identified to services. Services Operators\n"
- "may provide a nick to modify other users' certificate lists.\n"
- " \n"));
- source.Reply(_("Examples:\n"
- " \n"
- " \002CERT ADD\002\n"
- " Adds your current fingerprint to the certificate list and\n"
- " automatically identifies you when you connect to IRC\n"
- " using this fingerprint.\n"
- " \n"
- " \002CERT DEL <fingerprint>\002\n"
- " Removes the fingerprint <fingerprint> from your certificate list.\n"
- " \n"
- " \002CERT LIST\002\n"
- " Displays the current certificate list."));
- return true;
- }
-};
-
-class NSCert : public Module
-{
- CommandNSCert commandnscert;
- NSCertListImpl::ExtensibleItem certs;
- CertServiceImpl cs;
-
- public:
- NSCert(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandnscert(this), certs(this, "certificates"), cs(this)
- {
- if (!IRCD || !IRCD->CanCertFP)
- throw ModuleException("Your IRCd does not support ssl client certificates");
- }
-
- void OnFingerprint(User *u) anope_override
- {
- BotInfo *NickServ = Config->GetClient("NickServ");
- if (!NickServ || u->IsIdentified())
- return;
-
- NickCore *nc = cs.FindAccountFromCert(u->fingerprint);
- if (!nc || nc->HasExt("NS_SUSPENDED"))
- return;
-
- unsigned int maxlogins = Config->GetModule("ns_identify")->Get<unsigned int>("maxlogins");
- if (maxlogins && nc->users.size() >= maxlogins)
- {
- u->SendMessage(NickServ, _("Account \002%s\002 has already reached the maximum number of simultaneous logins (%u)."), nc->display.c_str(), maxlogins);
- return;
- }
-
- NickAlias *na = NickAlias::Find(u->nick);
- if (na && na->nc == nc)
- u->Identify(na);
- else
- u->Login(nc);
-
- u->SendMessage(NickServ, _("SSL certificate fingerprint accepted, you are now identified to \002%s\002."), nc->display.c_str());
- Log(NickServ) << u->GetMask() << " automatically identified for account " << nc->display << " via SSL certificate fingerprint";
- }
-
- EventReturn OnNickValidate(User *u, NickAlias *na) anope_override
- {
- NSCertList *cl = certs.Get(na->nc);
- if (!u->fingerprint.empty() && cl && cl->FindCert(u->fingerprint))
- {
- BotInfo *NickServ = Config->GetClient("NickServ");
-
- unsigned int maxlogins = Config->GetModule("ns_identify")->Get<unsigned int>("maxlogins");
- if (maxlogins && na->nc->users.size() >= maxlogins)
- {
- u->SendMessage(NickServ, _("Account \002%s\002 has already reached the maximum number of simultaneous logins (%u)."), na->nc->display.c_str(), maxlogins);
- return EVENT_CONTINUE;
- }
-
- u->Identify(na);
-
- u->SendMessage(NickServ, _("SSL certificate fingerprint accepted, you are now identified."));
- Log(NickServ) << u->GetMask() << " automatically identified for account " << na->nc->display << " via SSL certificate fingerprint";
- return EVENT_ALLOW;
- }
-
- return EVENT_CONTINUE;
- }
-};
-
-MODULE_INIT(NSCert)
diff --git a/modules/commands/ns_drop.cpp b/modules/commands/ns_drop.cpp
deleted file mode 100644
index df632fd24..000000000
--- a/modules/commands/ns_drop.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-/* NickServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class CommandNSDrop : public Command
-{
- public:
- CommandNSDrop(Module *creator) : Command(creator, "nickserv/drop", 1, 1)
- {
- this->SetSyntax(_("\037nickname\037"));
- this->SetDesc(_("Cancel the registration of a nickname"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &nick = params[0];
-
- if (Anope::ReadOnly && !source.HasPriv("nickserv/drop"))
- {
- source.Reply(_("Sorry, nickname de-registration is temporarily disabled."));
- return;
- }
-
- NickAlias *na = NickAlias::Find(nick);
- if (!na)
- {
- source.Reply(NICK_X_NOT_REGISTERED, nick.c_str());
- return;
- }
-
- bool is_mine = source.GetAccount() == na->nc;
-
- if (!is_mine && !source.HasPriv("nickserv/drop"))
- source.Reply(ACCESS_DENIED);
- else if (Config->GetModule("nickserv")->Get<bool>("secureadmins", "yes") && !is_mine && na->nc->IsServicesOper())
- source.Reply(_("You may not drop other Services Operators' nicknames."));
- else
- {
- FOREACH_MOD(OnNickDrop, (source, na));
-
- Log(!is_mine ? LOG_ADMIN : LOG_COMMAND, source, this) << "to drop nickname " << na->nick << " (group: " << na->nc->display << ") (email: " << (!na->nc->email.empty() ? na->nc->email : "none") << ")";
- delete na;
-
- source.Reply(_("Nickname \002%s\002 has been dropped."), nick.c_str());
- }
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Drops the given nick from the database. Once your nickname\n"
- "is dropped you may lose all of your access and channels that\n"
- "you may own. Any other user will be able to gain control of\n"
- "this nick."));
- if (!source.HasPriv("nickserv/drop"))
- source.Reply(_("You may drop any nick within your group."));
- else
- source.Reply(_("As a Services Operator, you may drop any nick."));
-
- return true;
- }
-};
-
-class NSDrop : public Module
-{
- CommandNSDrop commandnsdrop;
-
- public:
- NSDrop(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandnsdrop(this)
- {
-
- }
-};
-
-MODULE_INIT(NSDrop)
diff --git a/modules/commands/ns_getemail.cpp b/modules/commands/ns_getemail.cpp
deleted file mode 100644
index 0e782938f..000000000
--- a/modules/commands/ns_getemail.cpp
+++ /dev/null
@@ -1,74 +0,0 @@
-/* NickServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- *
- * A simple call to check for all emails that a user may have registered
- * with. It returns the nicks that match the email you provide. Wild
- * Cards are not excepted. Must use user@email-host.
- */
-
-#include "module.h"
-
-class CommandNSGetEMail : public Command
-{
- public:
- CommandNSGetEMail(Module *creator) : Command(creator, "nickserv/getemail", 1, 1)
- {
- this->SetDesc(_("Matches and returns all users that registered using given email"));
- this->SetSyntax(_("\037email\037"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &email = params[0];
- int j = 0;
-
- Log(LOG_ADMIN, source, this) << "on " << email;
-
- for (nickcore_map::const_iterator it = NickCoreList->begin(), it_end = NickCoreList->end(); it != it_end; ++it)
- {
- const NickCore *nc = it->second;
-
- if (!nc->email.empty() && Anope::Match(nc->email, email))
- {
- ++j;
- source.Reply(_("Email matched: \002%s\002 (\002%s\002) to \002%s\002."), nc->display.c_str(), nc->email.c_str(), email.c_str());
- }
- }
-
- if (j <= 0)
- {
- source.Reply(_("No registrations matching \002%s\002 were found."), email.c_str());
- return;
- }
-
- return;
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Returns the matching accounts that used given email."));
- return true;
- }
-};
-
-class NSGetEMail : public Module
-{
- CommandNSGetEMail commandnsgetemail;
- public:
- NSGetEMail(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandnsgetemail(this)
- {
-
- }
-};
-
-MODULE_INIT(NSGetEMail)
diff --git a/modules/commands/ns_getpass.cpp b/modules/commands/ns_getpass.cpp
deleted file mode 100644
index ae32fc2ef..000000000
--- a/modules/commands/ns_getpass.cpp
+++ /dev/null
@@ -1,74 +0,0 @@
-/* NickServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class CommandNSGetPass : public Command
-{
- public:
- CommandNSGetPass(Module *creator) : Command(creator, "nickserv/getpass", 1, 1)
- {
- this->SetDesc(_("Retrieve the password for a nickname"));
- this->SetSyntax(_("\037nickname\037"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &nick = params[0];
- Anope::string tmp_pass;
- const NickAlias *na;
-
- if (!(na = NickAlias::Find(nick)))
- source.Reply(NICK_X_NOT_REGISTERED, nick.c_str());
- else if (Config->GetModule("nickserv")->Get<bool>("secureadmins", "yes") && na->nc->IsServicesOper())
- source.Reply(_("You may not get the password of other Services Operators."));
- else
- {
- if (Anope::Decrypt(na->nc->pass, tmp_pass) == 1)
- {
- Log(LOG_ADMIN, source, this) << "for " << nick;
- source.Reply(_("Password for %s is \002%s\002."), nick.c_str(), tmp_pass.c_str());
- }
- else
- source.Reply(_("GETPASS command unavailable because encryption is in use."));
- }
- return;
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Returns the password for the given nickname. \002Note\002 that\n"
- "whenever this command is used, a message including the\n"
- "person who issued the command and the nickname it was used\n"
- "on will be logged and sent out as a WALLOPS/GLOBOPS."));
- return true;
- }
-};
-
-class NSGetPass : public Module
-{
- CommandNSGetPass commandnsgetpass;
-
- public:
- NSGetPass(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandnsgetpass(this)
- {
-
- Anope::string tmp_pass = "plain:tmp";
- if (!Anope::Decrypt(tmp_pass, tmp_pass))
- throw ModuleException("Incompatible with the encryption module being used");
-
- }
-};
-
-MODULE_INIT(NSGetPass)
diff --git a/modules/commands/ns_group.cpp b/modules/commands/ns_group.cpp
deleted file mode 100644
index 63d0dff10..000000000
--- a/modules/commands/ns_group.cpp
+++ /dev/null
@@ -1,384 +0,0 @@
-/* NickServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-#include "modules/ns_cert.h"
-
-class NSGroupRequest : public IdentifyRequest
-{
- CommandSource source;
- Command *cmd;
- Anope::string nick;
- Reference<NickAlias> target;
-
- public:
- NSGroupRequest(Module *o, CommandSource &src, Command *c, const Anope::string &n, NickAlias *targ, const Anope::string &pass) : IdentifyRequest(o, targ->nc->display, pass), source(src), cmd(c), nick(n), target(targ) { }
-
- void OnSuccess() anope_override
- {
- if (!source.GetUser() || source.GetUser()->nick != nick || !target || !target->nc)
- return;
-
- User *u = source.GetUser();
- NickAlias *na = NickAlias::Find(nick);
- /* If the nick is already registered, drop it. */
- if (na)
- {
- FOREACH_MOD(OnChangeCoreDisplay, (na->nc, u->nick));
- delete na;
- }
-
- na = new NickAlias(nick, target->nc);
-
- Anope::string last_usermask = u->GetIdent() + "@" + u->GetDisplayedHost();
- na->last_usermask = last_usermask;
- na->last_realname = u->realname;
- na->time_registered = na->last_seen = Anope::CurTime;
-
- u->Login(target->nc);
- FOREACH_MOD(OnNickGroup, (u, target));
-
- Log(LOG_COMMAND, source, cmd) << "to make " << nick << " join group of " << target->nick << " (" << target->nc->display << ") (email: " << (!target->nc->email.empty() ? target->nc->email : "none") << ")";
- source.Reply(_("You are now in the group of \002%s\002."), target->nick.c_str());
-
- u->lastnickreg = Anope::CurTime;
-
- }
-
- void OnFail() anope_override
- {
- if (!source.GetUser())
- return;
-
- Log(LOG_COMMAND, source, cmd) << "and failed to group to " << target->nick;
- if (NickAlias::Find(GetAccount()) != NULL)
- {
- source.Reply(PASSWORD_INCORRECT);
- source.GetUser()->BadPassword();
- }
- else
- source.Reply(NICK_X_NOT_REGISTERED, GetAccount().c_str());
- }
-};
-
-class CommandNSGroup : public Command
-{
- public:
- CommandNSGroup(Module *creator) : Command(creator, "nickserv/group", 0, 2)
- {
- this->SetDesc(_("Join a group"));
- this->SetSyntax(_("\037[target]\037 \037[password]\037"));
- this->AllowUnregistered(true);
- this->RequireUser(true);
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- User *u = source.GetUser();
-
- Anope::string nick;
- if (params.empty())
- {
- NickCore* core = u->Account();
- if (core)
- nick = core->display;
- }
- else
- nick = params[0];
-
- if (nick.empty())
- {
- this->SendSyntax(source);
- return;
- }
-
- const Anope::string &pass = params.size() > 1 ? params[1] : "";
-
- if (Anope::ReadOnly)
- {
- source.Reply(_("Sorry, nickname grouping is temporarily disabled."));
- return;
- }
-
- if (!IRCD->IsNickValid(u->nick))
- {
- source.Reply(NICK_CANNOT_BE_REGISTERED, u->nick.c_str());
- return;
- }
-
- if (Config->GetModule("nickserv")->Get<bool>("restrictopernicks"))
- for (unsigned i = 0; i < Oper::opers.size(); ++i)
- {
- Oper *o = Oper::opers[i];
-
- if (!u->HasMode("OPER") && u->nick.find_ci(o->name) != Anope::string::npos)
- {
- source.Reply(NICK_CANNOT_BE_REGISTERED, u->nick.c_str());
- return;
- }
- }
-
- NickAlias *target, *na = NickAlias::Find(u->nick);
- const Anope::string &guestnick = Config->GetModule("nickserv")->Get<const Anope::string>("guestnickprefix", "Guest");
- time_t reg_delay = Config->GetModule("nickserv")->Get<time_t>("regdelay");
- unsigned maxaliases = Config->GetModule(this->owner)->Get<unsigned>("maxaliases");
- if (!(target = NickAlias::Find(nick)))
- source.Reply(NICK_X_NOT_REGISTERED, nick.c_str());
- else if (Anope::CurTime < u->lastnickreg + reg_delay)
- source.Reply(_("Please wait %d seconds before using the GROUP command again."), (reg_delay + u->lastnickreg) - Anope::CurTime);
- else if (target->nc->HasExt("NS_SUSPENDED"))
- {
- Log(LOG_COMMAND, source, this) << "and tried to group to SUSPENDED nick " << target->nick;
- source.Reply(NICK_X_SUSPENDED, target->nick.c_str());
- }
- else if (na && Config->GetModule(this->owner)->Get<bool>("nogroupchange"))
- source.Reply(_("Your nick is already registered."));
- else if (na && *target->nc == *na->nc)
- source.Reply(_("You are already a member of the group of \002%s\002."), target->nick.c_str());
- else if (na && na->nc != u->Account())
- source.Reply(NICK_IDENTIFY_REQUIRED);
- else if (maxaliases && target->nc->aliases->size() >= maxaliases && !target->nc->IsServicesOper())
- source.Reply(_("There are too many nicks in your group."));
- else if (u->nick.length() <= guestnick.length() + 7 &&
- u->nick.length() >= guestnick.length() + 1 &&
- !u->nick.find_ci(guestnick) && !u->nick.substr(guestnick.length()).find_first_not_of("1234567890"))
- {
- source.Reply(NICK_CANNOT_BE_REGISTERED, u->nick.c_str());
- }
- else
- {
- bool ok = false;
- if (!na && u->Account() == target->nc)
- ok = true;
-
- NSCertList *cl = target->nc->GetExt<NSCertList>("certificates");
- if (!u->fingerprint.empty() && cl && cl->FindCert(u->fingerprint))
- ok = true;
-
- if (ok == false && !pass.empty())
- {
- NSGroupRequest *req = new NSGroupRequest(owner, source, this, u->nick, target, pass);
- FOREACH_MOD(OnCheckAuthentication, (source.GetUser(), req));
- req->Dispatch();
- }
- else
- {
- NSGroupRequest req(owner, source, this, u->nick, target, pass);
-
- if (ok)
- req.OnSuccess();
- else
- req.OnFail();
- }
- }
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("This command makes your nickname join the \037target\037 nickname's\n"
- "group. \037password\037 is the password of the target nickname.\n"
- " \n"
- "Joining a group will allow you to share your configuration,\n"
- "memos, and channel privileges with all the nicknames in the\n"
- "group, and much more!\n"
- " \n"
- "A group exists as long as it is useful. This means that even\n"
- "if a nick of the group is dropped, you won't lose the\n"
- "shared things described above, as long as there is at\n"
- "least one nick remaining in the group.\n"
- " \n"
- "You may be able to use this command even if you have not registered\n"
- "your nick yet. If your nick is already registered, you'll\n"
- "need to identify yourself before using this command.\n"
- " \n"
- "It is recommended to use this command with a non-registered\n"
- "nick because it will be registered automatically when\n"
- "using this command. You may use it with a registered nick (to\n"
- "change your group) only if your network administrators allowed\n"
- "it.\n"
- " \n"
- "You can only be in one group at a time. Group merging is\n"
- "not possible.\n"
- " \n"
- "\037Note\037: all the nicknames of a group have the same password."));
- return true;
- }
-};
-
-class CommandNSUngroup : public Command
-{
- public:
- CommandNSUngroup(Module *creator) : Command(creator, "nickserv/ungroup", 0, 1)
- {
- this->SetDesc(_("Remove a nick from a group"));
- this->SetSyntax(_("[\037nick\037]"));
- this->RequireUser(true);
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- User *u = source.GetUser();
- Anope::string nick = !params.empty() ? params[0] : "";
- NickAlias *na = NickAlias::Find(!nick.empty() ? nick : u->nick);
-
- if (u->Account()->aliases->size() == 1)
- source.Reply(_("Your nick is not grouped to anything, you can't ungroup it."));
- else if (!na)
- source.Reply(NICK_X_NOT_REGISTERED, !nick.empty() ? nick.c_str() : u->nick.c_str());
- else if (na->nc != u->Account())
- source.Reply(_("Nick %s is not in your group."), na->nick.c_str());
- else
- {
- NickCore *oldcore = na->nc;
-
- std::vector<NickAlias *>::iterator it = std::find(oldcore->aliases->begin(), oldcore->aliases->end(), na);
- if (it != oldcore->aliases->end())
- oldcore->aliases->erase(it);
-
- if (na->nick.equals_ci(oldcore->display))
- oldcore->SetDisplay(oldcore->aliases->front());
-
- NickCore *nc = new NickCore(na->nick);
- na->nc = nc;
- nc->aliases->push_back(na);
-
- nc->pass = oldcore->pass;
- if (!oldcore->email.empty())
- nc->email = oldcore->email;
- nc->language = oldcore->language;
-
- source.Reply(_("Nick %s has been ungrouped from %s."), na->nick.c_str(), oldcore->display.c_str());
-
- User *user = User::Find(na->nick, true);
- if (user)
- /* The user on the nick who was ungrouped may be identified to the old group, set -r */
- user->RemoveMode(source.service, "REGISTERED");
- }
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("This command ungroups your nick, or if given, the specificed nick,\n"
- "from the group it is in. The ungrouped nick keeps its registration\n"
- "time, password, email, greet, language, and url. Everything else\n"
- "is reset. You may not ungroup yourself if there is only one nick in\n"
- "your group."));
- return true;
- }
-};
-
-class CommandNSGList : public Command
-{
- public:
- CommandNSGList(Module *creator) : Command(creator, "nickserv/glist", 0, 1)
- {
- this->SetDesc(_("Lists all nicknames in your group"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &nick = !params.empty() ? params[0] : "";
- const NickCore *nc;
-
- if (!nick.empty())
- {
- const NickAlias *na = NickAlias::Find(nick);
- if (!na)
- {
- source.Reply(NICK_X_NOT_REGISTERED, nick.c_str());
- return;
- }
- else if (na->nc != source.GetAccount() && !source.IsServicesOper())
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- nc = na->nc;
- }
- else
- nc = source.GetAccount();
-
- ListFormatter list(source.GetAccount());
- list.AddColumn(_("Nick")).AddColumn(_("Expires"));
- time_t nickserv_expire = Config->GetModule("nickserv")->Get<time_t>("expire", "21d"),
- unconfirmed_expire = Config->GetModule("nickserv")->Get<time_t>("unconfirmedexpire", "1d");
- for (unsigned i = 0; i < nc->aliases->size(); ++i)
- {
- const NickAlias *na2 = nc->aliases->at(i);
-
- Anope::string expires;
- if (na2->HasExt("NS_NO_EXPIRE"))
- expires = NO_EXPIRE;
- else if (!nickserv_expire || Anope::NoExpire)
- ;
- else if (na2->nc->HasExt("UNCONFIRMED") && unconfirmed_expire)
- expires = Anope::strftime(na2->time_registered + unconfirmed_expire, source.GetAccount());
- else
- expires = Anope::strftime(na2->last_seen + nickserv_expire, source.GetAccount());
-
- ListFormatter::ListEntry entry;
- entry["Nick"] = na2->nick;
- entry["Expires"] = expires;
- list.AddEntry(entry);
- }
-
- source.Reply(!nick.empty() ? _("List of nicknames in the group of \002%s\002:") : _("List of nicknames in your group:"), nc->display.c_str());
- std::vector<Anope::string> replies;
- list.Process(replies);
-
- for (unsigned i = 0; i < replies.size(); ++i)
- source.Reply(replies[i]);
-
- source.Reply(_("%d nickname(s) in the group."), nc->aliases->size());
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- if (source.IsServicesOper())
- source.Reply(_("Syntax: \002%s [\037nickname\037]\002\n"
- " \n"
- "Without a parameter, lists all nicknames that are in\n"
- "your group.\n"
- " \n"
- "With a parameter, lists all nicknames that are in the\n"
- "group of the given nick.\n"
- "Specifying a nick is limited to \002Services Operators\002."),
- source.command.c_str());
- else
- source.Reply(_("Syntax: \002%s\002\n"
- " \n"
- "Lists all nicks in your group."), source.command.c_str());
-
- return true;
- }
-};
-
-class NSGroup : public Module
-{
- CommandNSGroup commandnsgroup;
- CommandNSUngroup commandnsungroup;
- CommandNSGList commandnsglist;
-
- public:
- NSGroup(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandnsgroup(this), commandnsungroup(this), commandnsglist(this)
- {
- if (Config->GetModule("nickserv")->Get<bool>("nonicknameownership"))
- throw ModuleException(modname + " can not be used with options:nonicknameownership enabled");
- }
-};
-
-MODULE_INIT(NSGroup)
diff --git a/modules/commands/ns_identify.cpp b/modules/commands/ns_identify.cpp
deleted file mode 100644
index a188b2ee8..000000000
--- a/modules/commands/ns_identify.cpp
+++ /dev/null
@@ -1,128 +0,0 @@
-/* NickServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class NSIdentifyRequest : public IdentifyRequest
-{
- CommandSource source;
- Command *cmd;
-
- public:
- NSIdentifyRequest(Module *o, CommandSource &s, Command *c, const Anope::string &acc, const Anope::string &pass) : IdentifyRequest(o, acc, pass), source(s), cmd(c) { }
-
- void OnSuccess() anope_override
- {
- if (!source.GetUser())
- return;
-
- User *u = source.GetUser();
- NickAlias *na = NickAlias::Find(GetAccount());
-
- if (!na)
- source.Reply(NICK_X_NOT_REGISTERED, GetAccount().c_str());
- else
- {
- if (u->IsIdentified())
- Log(LOG_COMMAND, source, cmd) << "to log out of account " << u->Account()->display;
-
- Log(LOG_COMMAND, source, cmd) << "and identified for account " << na->nc->display;
- source.Reply(_("Password accepted - you are now recognized."));
- u->Identify(na);
- }
- }
-
- void OnFail() anope_override
- {
- if (source.GetUser())
- {
- bool accountexists = NickAlias::Find(GetAccount()) != NULL;
- Log(LOG_COMMAND, source, cmd) << "and failed to identify to" << (accountexists ? " " : " nonexistent ") << "account " << GetAccount();
- if (accountexists)
- {
- source.Reply(PASSWORD_INCORRECT);
- source.GetUser()->BadPassword();
- }
- else
- source.Reply(NICK_X_NOT_REGISTERED, GetAccount().c_str());
- }
- }
-};
-
-class CommandNSIdentify : public Command
-{
- public:
- CommandNSIdentify(Module *creator) : Command(creator, "nickserv/identify", 1, 2)
- {
- this->SetDesc(_("Identify yourself with your password"));
- this->SetSyntax(_("[\037account\037] \037password\037"));
- this->AllowUnregistered(true);
- this->RequireUser(true);
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- User *u = source.GetUser();
-
- const Anope::string &nick = params.size() == 2 ? params[0] : u->nick;
- Anope::string pass = params[params.size() - 1];
-
- NickAlias *na = NickAlias::Find(nick);
- if (na && na->nc->HasExt("NS_SUSPENDED"))
- {
- source.Reply(NICK_X_SUSPENDED, na->nick.c_str());
- return;
- }
-
- if (u->Account() && na && u->Account() == na->nc)
- {
- source.Reply(_("You are already identified."));
- return;
- }
-
- unsigned int maxlogins = Config->GetModule(this->owner)->Get<unsigned int>("maxlogins");
- if (na && maxlogins && na->nc->users.size() >= maxlogins)
- {
- source.Reply(_("Account \002%s\002 has already reached the maximum number of simultaneous logins (%u)."), na->nc->display.c_str(), maxlogins);
- return;
- }
-
- NSIdentifyRequest *req = new NSIdentifyRequest(owner, source, this, na ? na->nc->display : nick, pass);
- FOREACH_MOD(OnCheckAuthentication, (u, req));
- req->Dispatch();
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Tells %s that you are really the owner of this\n"
- "nick. Many commands require you to authenticate yourself\n"
- "with this command before you use them. The password\n"
- "should be the same one you sent with the \002REGISTER\002\n"
- "command."), source.service->nick.c_str());
- return true;
- }
-};
-
-class NSIdentify : public Module
-{
- CommandNSIdentify commandnsidentify;
-
- public:
- NSIdentify(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandnsidentify(this)
- {
-
- }
-};
-
-MODULE_INIT(NSIdentify)
diff --git a/modules/commands/ns_info.cpp b/modules/commands/ns_info.cpp
deleted file mode 100644
index a1cdfc897..000000000
--- a/modules/commands/ns_info.cpp
+++ /dev/null
@@ -1,282 +0,0 @@
-/* NickServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class CommandNSInfo : public Command
-{
- public:
- CommandNSInfo(Module *creator) : Command(creator, "nickserv/info", 0, 2)
- {
- this->SetDesc(_("Displays information about a given nickname"));
- this->SetSyntax(_("[\037nickname\037]"));
- this->AllowUnregistered(true);
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
-
- const Anope::string &nick = params.size() ? params[0] : (source.nc ? source.nc->display : source.GetNick());
- NickAlias *na = NickAlias::Find(nick);
- bool has_auspex = source.HasPriv("nickserv/auspex");
-
- if (!na)
- {
- if (BotInfo::Find(nick, true))
- source.Reply(_("Nick \002%s\002 is part of this Network's Services."), nick.c_str());
- else
- source.Reply(NICK_X_NOT_REGISTERED, nick.c_str());
- }
- else
- {
- bool nick_online = false, show_hidden = false;
-
- /* Is the real owner of the nick we're looking up online? -TheShadow */
- User *u2 = User::Find(na->nick, true);
- if (u2 && u2->Account() == na->nc)
- {
- nick_online = true;
- na->last_seen = Anope::CurTime;
- }
-
- if (has_auspex || na->nc == source.GetAccount())
- show_hidden = true;
-
- source.Reply(_("%s is %s"), na->nick.c_str(), na->last_realname.c_str());
-
- if (na->nc->HasExt("UNCONFIRMED"))
- source.Reply(_("%s is an unconfirmed nickname."), na->nick.c_str());
-
- if (na->nc->IsServicesOper() && (show_hidden || !na->nc->HasExt("HIDE_STATUS")))
- source.Reply(_("%s is a Services Operator of type %s."), na->nick.c_str(), na->nc->o->ot->GetName().c_str());
-
- InfoFormatter info(source.nc);
-
- if (nick_online)
- {
- bool shown = false;
- if (show_hidden && !na->last_realhost.empty())
- {
- info[_("Online from")] = na->last_realhost;
- shown = true;
- }
- if ((show_hidden || !na->nc->HasExt("HIDE_MASK")) && (!shown || na->last_usermask != na->last_realhost))
- info[_("Online from")] = na->last_usermask;
- else
- source.Reply(_("%s is currently online."), na->nick.c_str());
- }
- else
- {
- Anope::string shown;
- if (show_hidden || !na->nc->HasExt("HIDE_MASK"))
- {
- info[_("Last seen address")] = na->last_usermask;
- shown = na->last_usermask;
- }
-
- if (show_hidden && !na->last_realhost.empty() && na->last_realhost != shown)
- info[_("Last seen address")] = na->last_realhost;
- }
-
- info[_("Registered")] = Anope::strftime(na->time_registered, source.GetAccount());
-
- if (!nick_online)
- info[_("Last seen")] = Anope::strftime(na->last_seen, source.GetAccount());
-
- if (!na->last_quit.empty() && (show_hidden || !na->nc->HasExt("HIDE_QUIT")))
- info[_("Last quit message")] = na->last_quit;
-
- if (!na->nc->email.empty() && (show_hidden || !na->nc->HasExt("HIDE_EMAIL")))
- info[_("Email address")] = na->nc->email;
-
- if (show_hidden)
- {
- if (na->HasVhost())
- {
- if (IRCD->CanSetVIdent && !na->GetVhostIdent().empty())
- info[_("VHost")] = na->GetVhostIdent() + "@" + na->GetVhostHost();
- else
- info[_("VHost")] = na->GetVhostHost();
- }
- }
-
- FOREACH_MOD(OnNickInfo, (source, na, info, show_hidden));
-
- std::vector<Anope::string> replies;
- info.Process(replies);
-
- for (unsigned i = 0; i < replies.size(); ++i)
- source.Reply(replies[i]);
- }
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Displays information about the given nickname, such as\n"
- "the nick's owner, last seen address and time, and nick\n"
- "options. If no nick is given, and you are identified,\n"
- "your account name is used, else your current nickname is\n"
- "used."));
-
- return true;
- }
-};
-
-
-class CommandNSSetHide : public Command
-{
- public:
- CommandNSSetHide(Module *creator, const Anope::string &sname = "nickserv/set/hide", size_t min = 2) : Command(creator, sname, min, min + 1)
- {
- this->SetDesc(_("Hide certain pieces of nickname information"));
- this->SetSyntax("{EMAIL | STATUS | USERMASK | QUIT} {ON | OFF}");
- }
-
- void Run(CommandSource &source, const Anope::string &user, const Anope::string &param, const Anope::string &arg)
- {
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- const NickAlias *na = NickAlias::Find(user);
- if (!na)
- {
- source.Reply(NICK_X_NOT_REGISTERED, user.c_str());
- return;
- }
- NickCore *nc = na->nc;
-
- EventReturn MOD_RESULT;
- FOREACH_RESULT(OnSetNickOption, MOD_RESULT, (source, this, nc, param));
- if (MOD_RESULT == EVENT_STOP)
- return;
-
- Anope::string onmsg, offmsg, flag;
-
- if (param.equals_ci("EMAIL"))
- {
- flag = "HIDE_EMAIL";
- onmsg = _("The E-mail address of \002%s\002 will now be hidden from %s INFO displays.");
- offmsg = _("The E-mail address of \002%s\002 will now be shown in %s INFO displays.");
- }
- else if (param.equals_ci("USERMASK"))
- {
- flag = "HIDE_MASK";
- onmsg = _("The last seen user@host mask of \002%s\002 will now be hidden from %s INFO displays.");
- offmsg = _("The last seen user@host mask of \002%s\002 will now be shown in %s INFO displays.");
- }
- else if (param.equals_ci("STATUS"))
- {
- flag = "HIDE_STATUS";
- onmsg = _("The services access status of \002%s\002 will now be hidden from %s INFO displays.");
- offmsg = _("The services access status of \002%s\002 will now be shown in %s INFO displays.");
- }
- else if (param.equals_ci("QUIT"))
- {
- flag = "HIDE_QUIT";
- onmsg = _("The last quit message of \002%s\002 will now be hidden from %s INFO displays.");
- offmsg = _("The last quit message of \002%s\002 will now be shown in %s INFO displays.");
- }
- else
- {
- this->OnSyntaxError(source, "HIDE");
- return;
- }
-
- if (arg.equals_ci("ON"))
- {
- Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to change hide " << param.upper() << " to " << arg.upper() << " for " << nc->display;
- nc->Extend<bool>(flag);
- source.Reply(onmsg.c_str(), nc->display.c_str(), source.service->nick.c_str());
- }
- else if (arg.equals_ci("OFF"))
- {
- Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to change hide " << param.upper() << " to " << arg.upper() << " for " << nc->display;
- nc->Shrink<bool>(flag);
- source.Reply(offmsg.c_str(), nc->display.c_str(), source.service->nick.c_str());
- }
- else
- this->OnSyntaxError(source, "HIDE");
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- this->Run(source, source.nc->display, params[0], params[1]);
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Allows you to prevent certain pieces of information from\n"
- "being displayed when someone does a %s \002INFO\002 on your\n"
- "nick. You can hide your E-mail address (\002EMAIL\002), last seen\n"
- "user@host mask (\002USERMASK\002), your services access status\n"
- "(\002STATUS\002) and last quit message (\002QUIT\002).\n"
- "The second parameter specifies whether the information should\n"
- "be displayed (\002OFF\002) or hidden (\002ON\002)."), source.service->nick.c_str());
- return true;
- }
-};
-
-class CommandNSSASetHide : public CommandNSSetHide
-{
- public:
- CommandNSSASetHide(Module *creator) : CommandNSSetHide(creator, "nickserv/saset/hide", 3)
- {
- this->SetSyntax(_("\037nickname\037 {EMAIL | STATUS | USERMASK | QUIT} {ON | OFF}"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- this->ClearSyntax();
- this->Run(source, params[0], params[1], params[2]);
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Allows you to prevent certain pieces of information from\n"
- "being displayed when someone does a %s \002INFO\002 on the\n"
- "nick. You can hide the E-mail address (\002EMAIL\002), last seen\n"
- "user@host mask (\002USERMASK\002), the services access status\n"
- "(\002STATUS\002) and last quit message (\002QUIT\002).\n"
- "The second parameter specifies whether the information should\n"
- "be displayed (\002OFF\002) or hidden (\002ON\002)."), source.service->nick.c_str());
- return true;
- }
-};
-
-class NSInfo : public Module
-{
- CommandNSInfo commandnsinfo;
-
- CommandNSSetHide commandnssethide;
- CommandNSSASetHide commandnssasethide;
-
- SerializableExtensibleItem<bool> hide_email, hide_usermask, hide_status, hide_quit;
-
- public:
- NSInfo(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandnsinfo(this), commandnssethide(this), commandnssasethide(this),
- hide_email(this, "HIDE_EMAIL"), hide_usermask(this, "HIDE_MASK"), hide_status(this, "HIDE_STATUS"),
- hide_quit(this, "HIDE_QUIT")
- {
-
- }
-};
-
-MODULE_INIT(NSInfo)
diff --git a/modules/commands/ns_list.cpp b/modules/commands/ns_list.cpp
deleted file mode 100644
index 43e8d3f13..000000000
--- a/modules/commands/ns_list.cpp
+++ /dev/null
@@ -1,301 +0,0 @@
-/* NickServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class CommandNSList : public Command
-{
- public:
- CommandNSList(Module *creator) : Command(creator, "nickserv/list", 1, 2)
- {
- this->SetDesc(_("List all registered nicknames that match a given pattern"));
- this->SetSyntax(_("\037pattern\037 [SUSPENDED] [NOEXPIRE] [UNCONFIRMED]"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
-
- Anope::string pattern = params[0];
- const NickCore *mync;
- unsigned nnicks;
- bool is_servadmin = source.HasCommand("nickserv/list");
- int count = 0, from = 0, to = 0;
- bool suspended, nsnoexpire, unconfirmed;
- unsigned listmax = Config->GetModule(this->owner)->Get<unsigned>("listmax", "50");
-
- suspended = nsnoexpire = unconfirmed = false;
-
- if (pattern[0] == '#')
- {
- Anope::string n1, n2;
- sepstream(pattern.substr(1), '-').GetToken(n1, 0);
- sepstream(pattern, '-').GetToken(n2, 1);
- try
- {
- from = convertTo<int>(n1);
- to = convertTo<int>(n2);
- }
- catch (const ConvertException &)
- {
- source.Reply(LIST_INCORRECT_RANGE);
- return;
- }
-
- pattern = "*";
- }
-
- nnicks = 0;
-
- if (is_servadmin && params.size() > 1)
- {
- Anope::string keyword;
- spacesepstream keywords(params[1]);
- while (keywords.GetToken(keyword))
- {
- if (keyword.equals_ci("NOEXPIRE"))
- nsnoexpire = true;
- if (keyword.equals_ci("SUSPENDED"))
- suspended = true;
- if (keyword.equals_ci("UNCONFIRMED"))
- unconfirmed = true;
- }
- }
-
- mync = source.nc;
- ListFormatter list(source.GetAccount());
-
- list.AddColumn(_("Nick")).AddColumn(_("Last usermask"));
-
- Anope::map<NickAlias *> ordered_map;
- for (nickalias_map::const_iterator it = NickAliasList->begin(), it_end = NickAliasList->end(); it != it_end; ++it)
- ordered_map[it->first] = it->second;
-
- for (Anope::map<NickAlias *>::const_iterator it = ordered_map.begin(), it_end = ordered_map.end(); it != it_end; ++it)
- {
- const NickAlias *na = it->second;
-
- /* Don't show private nicks to non-services admins. */
- if (na->nc->HasExt("NS_PRIVATE") && !is_servadmin && na->nc != mync)
- continue;
- else if (nsnoexpire && !na->HasExt("NS_NO_EXPIRE"))
- continue;
- else if (suspended && !na->nc->HasExt("NS_SUSPENDED"))
- continue;
- else if (unconfirmed && !na->nc->HasExt("UNCONFIRMED"))
- continue;
-
- /* We no longer compare the pattern against the output buffer.
- * Instead we build a nice nick!user@host buffer to compare.
- * The output is then generated separately. -TheShadow */
- Anope::string buf = Anope::printf("%s!%s", na->nick.c_str(), !na->last_usermask.empty() ? na->last_usermask.c_str() : "*@*");
- if (na->nick.equals_ci(pattern) || Anope::Match(buf, pattern, false, true))
- {
- if (((count + 1 >= from && count + 1 <= to) || (!from && !to)) && ++nnicks <= listmax)
- {
- bool isnoexpire = false;
- if (is_servadmin && na->HasExt("NS_NO_EXPIRE"))
- isnoexpire = true;
-
- ListFormatter::ListEntry entry;
- entry["Nick"] = (isnoexpire ? "!" : "") + na->nick;
- if (na->nc->HasExt("HIDE_MASK") && !is_servadmin && na->nc != mync)
- entry["Last usermask"] = Language::Translate(source.GetAccount(), _("[Hostname hidden]"));
- else if (na->nc->HasExt("NS_SUSPENDED"))
- entry["Last usermask"] = Language::Translate(source.GetAccount(), _("[Suspended]"));
- else if (na->nc->HasExt("UNCONFIRMED"))
- entry["Last usermask"] = Language::Translate(source.GetAccount(), _("[Unconfirmed]"));
- else
- entry["Last usermask"] = na->last_usermask;
- list.AddEntry(entry);
- }
- ++count;
- }
- }
-
- source.Reply(_("List of entries matching \002%s\002:"), pattern.c_str());
-
- std::vector<Anope::string> replies;
- list.Process(replies);
-
- for (unsigned i = 0; i < replies.size(); ++i)
- source.Reply(replies[i]);
-
- source.Reply(_("End of list - %d/%d matches shown."), nnicks > listmax ? listmax : nnicks, nnicks);
- return;
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Lists all registered nicknames which match the given\n"
- "pattern, in \037nick!user@host\037 format. Nicks with the \002PRIVATE\002\n"
- "option set will only be displayed to Services Operators with the\n"
- "proper access. Nicks with the \002NOEXPIRE\002 option set will have\n"
- "a \002!\002 prefixed to the nickname for Services Operators to see.\n"
- " \n"
- "Note that a preceding '#' specifies a range.\n"
- " \n"
- "If the SUSPENDED, UNCONFIRMED or NOEXPIRE options are given, only\n"
- "nicks which, respectively, are SUSPENDED, UNCONFIRMED or have the\n"
- "NOEXPIRE flag set will be displayed. If multiple options are\n"
- "given, all nicks matching at least one option will be displayed.\n"
- "Note that these options are limited to \037Services Operators\037.\n"
- " \n"
- "Examples:\n"
- " \n"
- " \002LIST *!joeuser@foo.com\002\n"
- " Lists all registered nicks owned by joeuser@foo.com.\n"
- " \n"
- " \002LIST *Bot*!*@*\002\n"
- " Lists all registered nicks with \002Bot\002 in their\n"
- " names (case insensitive).\n"
- " \n"
- " \002LIST * NOEXPIRE\002\n"
- " Lists all registered nicks which have been set to not expire.\n"
- " \n"
- " \002LIST #51-100\002\n"
- " Lists all registered nicks within the given range (51-100)."));
-
- const Anope::string &regexengine = Config->GetBlock("options")->Get<const Anope::string>("regexengine");
- if (!regexengine.empty())
- {
- source.Reply(" ");
- source.Reply(_("Regex matches are also supported using the %s engine.\n"
- "Enclose your pattern in // if this is desired."), regexengine.c_str());
- }
-
- return true;
- }
-};
-
-
-class CommandNSSetPrivate : public Command
-{
- public:
- CommandNSSetPrivate(Module *creator, const Anope::string &sname = "nickserv/set/private", size_t min = 1) : Command(creator, sname, min, min + 1)
- {
- this->SetDesc(_("Prevent the nickname from appearing in the LIST command"));
- this->SetSyntax("{ON | OFF}");
- }
-
- void Run(CommandSource &source, const Anope::string &user, const Anope::string &param)
- {
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- const NickAlias *na = NickAlias::Find(user);
- if (!na)
- {
- source.Reply(NICK_X_NOT_REGISTERED, user.c_str());
- return;
- }
- NickCore *nc = na->nc;
-
- EventReturn MOD_RESULT;
- FOREACH_RESULT(OnSetNickOption, MOD_RESULT, (source, this, nc, param));
- if (MOD_RESULT == EVENT_STOP)
- return;
-
- if (param.equals_ci("ON"))
- {
- Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to enable private for " << nc->display;
- nc->Extend<bool>("NS_PRIVATE");
- source.Reply(_("Private option is now \002on\002 for \002%s\002."), nc->display.c_str());
- }
- else if (param.equals_ci("OFF"))
- {
- Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to disable private for " << nc->display;
- nc->Shrink<bool>("NS_PRIVATE");
- source.Reply(_("Private option is now \002off\002 for \002%s\002."), nc->display.c_str());
- }
- else
- this->OnSyntaxError(source, "PRIVATE");
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- this->Run(source, source.nc->display, params[0]);
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Turns %s's privacy option on or off for your nick.\n"
- "With \002PRIVATE\002 set, your nickname will not appear in\n"
- "nickname lists generated with %s's \002LIST\002 command.\n"
- "(However, anyone who knows your nickname can still get\n"
- "information on it using the \002INFO\002 command.)"),
- source.service->nick.c_str(), source.service->nick.c_str());
- return true;
- }
-};
-
-class CommandNSSASetPrivate : public CommandNSSetPrivate
-{
- public:
- CommandNSSASetPrivate(Module *creator) : CommandNSSetPrivate(creator, "nickserv/saset/private", 2)
- {
- this->ClearSyntax();
- this->SetSyntax(_("\037nickname\037 {ON | OFF}"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- this->Run(source, params[0], params[1]);
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Turns %s's privacy option on or off for the nick.\n"
- "With \002PRIVATE\002 set, the nickname will not appear in\n"
- "nickname lists generated with %s's \002LIST\002 command.\n"
- "(However, anyone who knows the nickname can still get\n"
- "information on it using the \002INFO\002 command.)"),
- source.service->nick.c_str(), source.service->nick.c_str());
- return true;
- }
-};
-
-
-class NSList : public Module
-{
- CommandNSList commandnslist;
-
- CommandNSSetPrivate commandnssetprivate;
- CommandNSSASetPrivate commandnssasetprivate;
-
- SerializableExtensibleItem<bool> priv;
-
- public:
- NSList(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandnslist(this), commandnssetprivate(this), commandnssasetprivate(this),
- priv(this, "NS_PRIVATE")
- {
- }
-
- void OnNickInfo(CommandSource &source, NickAlias *na, InfoFormatter &info, bool show_all) anope_override
- {
- if (!show_all)
- return;
-
- if (priv.HasExt(na->nc))
- info.AddOption(_("Private"));
- }
-};
-
-MODULE_INIT(NSList)
diff --git a/modules/commands/ns_logout.cpp b/modules/commands/ns_logout.cpp
deleted file mode 100644
index 7567626e0..000000000
--- a/modules/commands/ns_logout.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-/* NickServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-static ServiceReference<NickServService> NickServService("NickServService", "NickServ");
-
-class CommandNSLogout : public Command
-{
- public:
- CommandNSLogout(Module *creator) : Command(creator, "nickserv/logout", 0, 2)
- {
- this->SetDesc(_("Reverses the effect of the IDENTIFY command"));
- this->SetSyntax(_("[\037nickname\037 [REVALIDATE]]"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
-
- const Anope::string &nick = !params.empty() ? params[0] : "";
- const Anope::string &param = params.size() > 1 ? params[1] : "";
-
- User *u2;
- if (!source.IsServicesOper() && !nick.empty())
- this->OnSyntaxError(source, "");
- else if (!(u2 = (!nick.empty() ? User::Find(nick, true) : source.GetUser())))
- source.Reply(NICK_X_NOT_IN_USE, !nick.empty() ? nick.c_str() : source.GetNick().c_str());
- else if (!nick.empty() && u2->IsServicesOper())
- source.Reply(_("You can't logout %s, they are a Services Operator."), nick.c_str());
- else
- {
- if (!nick.empty() && !param.empty() && param.equals_ci("REVALIDATE") && NickServService)
- NickServService->Validate(u2);
-
- u2->super_admin = false; /* Don't let people logout and remain a SuperAdmin */
- Log(LOG_COMMAND, source, this) << "to logout " << u2->nick;
-
- /* Remove founder status from this user in all channels */
- if (!nick.empty())
- source.Reply(_("Nick %s has been logged out."), nick.c_str());
- else
- source.Reply(_("Your nick has been logged out."));
-
- IRCD->SendLogout(u2);
- u2->RemoveMode(source.service, "REGISTERED");
- u2->Logout();
-
- /* Send out an event */
- FOREACH_MOD(OnNickLogout, (u2));
- }
- return;
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Without a parameter, reverses the effect of the \002IDENTIFY\002\n"
- "command, i.e. make you not recognized as the real owner of the nick\n"
- "anymore. Note, however, that you won't be asked to reidentify\n"
- "yourself.\n"
- " \n"
- "With a parameter, does the same for the given nick. If you\n"
- "specify \002REVALIDATE\002 as well, Services will ask the given nick\n"
- "to re-identify. This is limited to \002Services Operators\002."));
-
- return true;
- }
-};
-
-class NSLogout : public Module
-{
- CommandNSLogout commandnslogout;
-
- public:
- NSLogout(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandnslogout(this)
- {
-
- }
-};
-
-MODULE_INIT(NSLogout)
diff --git a/modules/commands/ns_recover.cpp b/modules/commands/ns_recover.cpp
deleted file mode 100644
index f41a8d782..000000000
--- a/modules/commands/ns_recover.cpp
+++ /dev/null
@@ -1,295 +0,0 @@
-/* NickServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-#include "modules/ns_cert.h"
-
-static ServiceReference<NickServService> nickserv("NickServService", "NickServ");
-
-typedef std::map<Anope::string, ChannelStatus> NSRecoverInfo;
-
-class NSRecoverSvsnick
-{
- public:
- Reference<User> from;
- Anope::string to;
-};
-
-class NSRecoverRequest : public IdentifyRequest
-{
- CommandSource source;
- Command *cmd;
- Anope::string user;
-
- public:
- NSRecoverRequest(Module *o, CommandSource &src, Command *c, const Anope::string &nick, const Anope::string &pass) : IdentifyRequest(o, nick, pass), source(src), cmd(c), user(nick) { }
-
- void OnSuccess() anope_override
- {
- User *u = User::Find(user, true);
- if (!source.GetUser() || !source.service)
- return;
-
- NickAlias *na = NickAlias::Find(user);
- if (!na)
- return;
-
- Log(LOG_COMMAND, source, cmd) << "for " << na->nick;
-
- /* Nick is being held by us, release it */
- if (na->HasExt("HELD"))
- {
- nickserv->Release(na);
- source.Reply(_("Service's hold on \002%s\002 has been released."), na->nick.c_str());
- }
- else if (!u)
- {
- source.Reply(_("No one is using your nick, and services are not holding it."));
- }
- // If the user being recovered is identified for the account of the nick then the user is the
- // same person that is executing the command, so kill them off (old GHOST command).
- else if (u->Account() == na->nc)
- {
- if (!source.GetAccount() && na->nc->HasExt("NS_SECURE"))
- {
- source.GetUser()->Login(u->Account());
- Log(LOG_COMMAND, source, cmd) << "and was automatically identified to " << u->Account()->display;
- }
-
- if (Config->GetModule("ns_recover")->Get<bool>("restoreonrecover"))
- {
- if (!u->chans.empty())
- {
- NSRecoverInfo *ei = source.GetUser()->Extend<NSRecoverInfo>("recover");
- for (User::ChanUserList::iterator it = u->chans.begin(), it_end = u->chans.end(); it != it_end; ++it)
- (*ei)[it->first->name] = it->second->status;
- }
- }
-
- u->SendMessage(source.service, _("This nickname has been recovered by %s. If you did not do\n"
- "this then %s may have your password, and you should change it."),
- source.GetNick().c_str(), source.GetNick().c_str());
-
- Anope::string buf = source.command.upper() + " command used by " + source.GetNick();
- u->Kill(*source.service, buf);
-
- source.Reply(_("Ghost with your nick has been killed."));
-
- if (IRCD->CanSVSNick)
- IRCD->SendForceNickChange(source.GetUser(), GetAccount(), Anope::CurTime);
- }
- /* User is not identified or not identified to the same account as the person using this command */
- else
- {
- if (!source.GetAccount() && na->nc->HasExt("NS_SECURE"))
- {
- source.GetUser()->Login(na->nc); // Identify the user using the command if they arent identified
- Log(LOG_COMMAND, source, cmd) << "and was automatically identified to " << na->nick << " (" << na->nc->display << ")";
- source.Reply(_("You have been logged in as \002%s\002."), na->nc->display.c_str());
- }
-
- u->SendMessage(source.service, _("This nickname has been recovered by %s."), source.GetNick().c_str());
-
- if (IRCD->CanSVSNick)
- {
- NSRecoverSvsnick *svs = u->Extend<NSRecoverSvsnick>("svsnick");
- svs->from = source.GetUser();
- svs->to = u->nick;
- }
-
- if (nickserv)
- nickserv->Collide(u, na);
-
- if (IRCD->CanSVSNick)
- {
- /* If we can svsnick then release our hold and svsnick the user using the command */
- if (nickserv)
- nickserv->Release(na);
-
- source.Reply(_("You have regained control of \002%s\002."), u->nick.c_str());
- }
- else
- {
- source.Reply(_("The user with your nick has been removed. Use this command again\n"
- "to release services's hold on your nick."));
- }
- }
- }
-
- void OnFail() anope_override
- {
- if (NickAlias::Find(GetAccount()) != NULL)
- {
- source.Reply(ACCESS_DENIED);
- if (!GetPassword().empty())
- {
- Log(LOG_COMMAND, source, cmd) << "with an invalid password for " << GetAccount();
- if (source.GetUser())
- source.GetUser()->BadPassword();
- }
- }
- else
- source.Reply(NICK_X_NOT_REGISTERED, GetAccount().c_str());
- }
-};
-
-class CommandNSRecover : public Command
-{
- public:
- CommandNSRecover(Module *creator) : Command(creator, "nickserv/recover", 1, 2)
- {
- this->SetDesc(_("Regains control of your nick"));
- this->SetSyntax(_("\037nickname\037 [\037password\037]"));
- this->AllowUnregistered(true);
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &nick = params[0];
- const Anope::string &pass = params.size() > 1 ? params[1] : "";
-
- User *user = User::Find(nick, true);
-
- if (user && source.GetUser() == user)
- {
- source.Reply(_("You can't %s yourself!"), source.command.lower().c_str());
- return;
- }
-
- const NickAlias *na = NickAlias::Find(nick);
-
- if (!na)
- {
- source.Reply(NICK_X_NOT_REGISTERED, nick.c_str());
- return;
- }
- else if (na->nc->HasExt("NS_SUSPENDED"))
- {
- source.Reply(NICK_X_SUSPENDED, na->nick.c_str());
- return;
- }
-
- bool ok = false;
- if (source.GetAccount() == na->nc)
- ok = true;
- else if (!na->nc->HasExt("NS_SECURE") && source.GetUser() && na->nc->IsOnAccess(source.GetUser()))
- ok = true;
-
- NSCertList *cl = na->nc->GetExt<NSCertList>("certificates");
- if (source.GetUser() && !source.GetUser()->fingerprint.empty() && cl && cl->FindCert(source.GetUser()->fingerprint))
- ok = true;
-
- if (ok == false && !pass.empty())
- {
- NSRecoverRequest *req = new NSRecoverRequest(owner, source, this, na->nick, pass);
- FOREACH_MOD(OnCheckAuthentication, (source.GetUser(), req));
- req->Dispatch();
- }
- else
- {
- NSRecoverRequest req(owner, source, this, na->nick, pass);
-
- if (ok)
- req.OnSuccess();
- else
- req.OnFail();
- }
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Recovers your nick from another user or from services.\n"
- "If services are currently holding your nick, the hold\n"
- "will be released. If another user is holding your nick\n"
- "and is identified they will be killed (similar to the old\n"
- "GHOST command). If they are not identified they will be\n"
- "forced off of the nick."));
- return true;
- }
-};
-
-class NSRecover : public Module
-{
- CommandNSRecover commandnsrecover;
- PrimitiveExtensibleItem<NSRecoverInfo> recover;
- PrimitiveExtensibleItem<NSRecoverSvsnick> svsnick;
-
- public:
- NSRecover(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandnsrecover(this), recover(this, "recover"), svsnick(this, "svsnick")
- {
-
- if (Config->GetModule("nickserv")->Get<bool>("nonicknameownership"))
- throw ModuleException(modname + " can not be used with options:nonicknameownership enabled");
-
- }
-
- void OnUserNickChange(User *u, const Anope::string &oldnick) anope_override
- {
- if (Config->GetModule(this)->Get<bool>("restoreonrecover"))
- {
- NSRecoverInfo *ei = recover.Get(u);
- BotInfo *NickServ = Config->GetClient("NickServ");
-
- if (ei != NULL && NickServ != NULL)
- for (NSRecoverInfo::iterator it = ei->begin(), it_end = ei->end(); it != it_end;)
- {
- Channel *c = Channel::Find(it->first);
- const Anope::string &cname = it->first;
- ++it;
-
- /* User might already be on the channel */
- if (u->FindChannel(c))
- this->OnJoinChannel(u, c);
- else if (IRCD->CanSVSJoin)
- IRCD->SendSVSJoin(NickServ, u, cname, "");
- }
- }
-
- NSRecoverSvsnick *svs = svsnick.Get(u);
- if (svs)
- {
- if (svs->from)
- {
- // svsnick from to to
- IRCD->SendForceNickChange(svs->from, svs->to, Anope::CurTime);
- }
-
- svsnick.Unset(u);
- }
- }
-
- void OnJoinChannel(User *u, Channel *c) anope_override
- {
- if (Config->GetModule(this)->Get<bool>("restoreonrecover"))
- {
- NSRecoverInfo *ei = recover.Get(u);
-
- if (ei != NULL)
- {
- NSRecoverInfo::iterator it = ei->find(c->name);
- if (it != ei->end())
- {
- for (size_t i = 0; i < it->second.Modes().length(); ++i)
- c->SetMode(c->ci->WhoSends(), ModeManager::FindChannelModeByChar(it->second.Modes()[i]), u->GetUID());
-
- ei->erase(it);
- if (ei->empty())
- recover.Unset(u);
- }
- }
- }
- }
-};
-
-MODULE_INIT(NSRecover)
diff --git a/modules/commands/ns_register.cpp b/modules/commands/ns_register.cpp
deleted file mode 100644
index ce5dbc997..000000000
--- a/modules/commands/ns_register.cpp
+++ /dev/null
@@ -1,429 +0,0 @@
-/* NickServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-static bool SendRegmail(User *u, const NickAlias *na, BotInfo *bi);
-
-class CommandNSConfirm : public Command
-{
- public:
- CommandNSConfirm(Module *creator) : Command(creator, "nickserv/confirm", 1, 2)
- {
- this->SetDesc(_("Confirm a passcode"));
- this->SetSyntax(_("\037passcode\037"));
- this->AllowUnregistered(true);
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &passcode = params[0];
-
- if (source.nc && !source.nc->HasExt("UNCONFIRMED") && source.HasPriv("nickserv/confirm"))
- {
- NickAlias *na = NickAlias::Find(passcode);
- if (na == NULL)
- source.Reply(NICK_X_NOT_REGISTERED, passcode.c_str());
- else if (na->nc->HasExt("UNCONFIRMED") == false)
- source.Reply(_("Nick \002%s\002 is already confirmed."), na->nick.c_str());
- else
- {
- na->nc->Shrink<bool>("UNCONFIRMED");
- FOREACH_MOD(OnNickConfirm, (source.GetUser(), na->nc));
- Log(LOG_ADMIN, source, this) << "to confirm nick " << na->nick << " (" << na->nc->display << ")";
- source.Reply(_("Nick \002%s\002 has been confirmed."), na->nick.c_str());
-
- /* Login the users online already */
- for (std::list<User *>::iterator it = na->nc->users.begin(); it != na->nc->users.end(); ++it)
- {
- User *u = *it;
-
- IRCD->SendLogin(u, na);
-
- NickAlias *u_na = NickAlias::Find(u->nick);
-
- /* Set +r if they're on a nick in the group */
- if (!Config->GetModule("nickserv")->Get<bool>("nonicknameownership") && u_na && *u_na->nc == *na->nc)
- u->SetMode(source.service, "REGISTERED");
- }
- }
- }
- else if (source.nc)
- {
- Anope::string *code = source.nc->GetExt<Anope::string>("passcode");
- if (code != NULL && *code == passcode)
- {
- NickCore *nc = source.nc;
- nc->Shrink<Anope::string>("passcode");
- Log(LOG_COMMAND, source, this) << "to confirm their email";
- source.Reply(_("Your email address of \002%s\002 has been confirmed."), source.nc->email.c_str());
- nc->Shrink<bool>("UNCONFIRMED");
- FOREACH_MOD(OnNickConfirm, (source.GetUser(), nc));
-
- if (source.GetUser())
- {
- NickAlias *na = NickAlias::Find(source.GetNick());
- if (na)
- {
- IRCD->SendLogin(source.GetUser(), na);
- if (!Config->GetModule("nickserv")->Get<bool>("nonicknameownership") && na->nc == source.GetAccount() && !na->nc->HasExt("UNCONFIRMED"))
- source.GetUser()->SetMode(source.service, "REGISTERED");
- }
- }
- }
- else
- source.Reply(_("Invalid passcode."));
- }
- else
- source.Reply(_("Invalid passcode."));
-
- return;
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("This command is used by several commands as a way to confirm\n"
- "changes made to your account.\n"
- " \n"
- "This is most commonly used to confirm your email address once\n"
- "you register or change it.\n"
- " \n"
- "This is also used after the RESETPASS command has been used to\n"
- "force identify you to your nick so you may change your password."));
- if (source.HasPriv("nickserv/confirm"))
- source.Reply(_("Additionally, Services Operators with the \037nickserv/confirm\037 permission can\n"
- "replace \037passcode\037 with a users nick to force validate them."));
- return true;
- }
-
- void OnSyntaxError(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- source.Reply(NICK_CONFIRM_INVALID);
- }
-};
-
-class CommandNSRegister : public Command
-{
- public:
- CommandNSRegister(Module *creator) : Command(creator, "nickserv/register", 1, 2)
- {
- this->SetDesc(_("Register a nickname"));
- if (Config->GetModule("nickserv")->Get<bool>("forceemail", "yes"))
- this->SetSyntax(_("\037password\037 \037email\037"));
- else
- this->SetSyntax(_("\037password\037 \037[email]\037"));
- this->AllowUnregistered(true);
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- User *u = source.GetUser();
- Anope::string u_nick = source.GetNick();
- size_t nicklen = u_nick.length();
- Anope::string pass = params[0];
- Anope::string email = params.size() > 1 ? params[1] : "";
- const Anope::string &nsregister = Config->GetModule(this->owner)->Get<const Anope::string>("registration");
-
- if (Anope::ReadOnly)
- {
- source.Reply(_("Sorry, nickname registration is temporarily disabled."));
- return;
- }
-
- if (nsregister.equals_ci("disable"))
- {
- source.Reply(_("Registration is currently disabled."));
- return;
- }
-
- time_t nickregdelay = Config->GetModule(this->owner)->Get<time_t>("nickregdelay");
- time_t reg_delay = Config->GetModule("nickserv")->Get<time_t>("regdelay");
- if (u && !u->HasMode("OPER") && nickregdelay && Anope::CurTime - u->timestamp < nickregdelay)
- {
- source.Reply(_("You must have been using this nick for at least %d seconds to register."), nickregdelay);
- return;
- }
-
- /* Prevent "Guest" nicks from being registered. -TheShadow */
-
- /* Guest nick can now have a series of between 1 and 7 digits.
- * --lara
- */
- const Anope::string &guestnick = Config->GetModule("nickserv")->Get<const Anope::string>("guestnickprefix", "Guest");
- if (nicklen <= guestnick.length() + 7 && nicklen >= guestnick.length() + 1 && !u_nick.find_ci(guestnick) && u_nick.substr(guestnick.length()).find_first_not_of("1234567890") == Anope::string::npos)
- {
- source.Reply(NICK_CANNOT_BE_REGISTERED, u_nick.c_str());
- return;
- }
-
- if (!IRCD->IsNickValid(u_nick))
- {
- source.Reply(NICK_CANNOT_BE_REGISTERED, u_nick.c_str());
- return;
- }
-
- if (BotInfo::Find(u_nick, true))
- {
- source.Reply(NICK_CANNOT_BE_REGISTERED, u_nick.c_str());
- return;
- }
-
- if (Config->GetModule("nickserv")->Get<bool>("restrictopernicks"))
- for (unsigned i = 0; i < Oper::opers.size(); ++i)
- {
- Oper *o = Oper::opers[i];
-
- if (!source.IsOper() && u_nick.find_ci(o->name) != Anope::string::npos)
- {
- source.Reply(NICK_CANNOT_BE_REGISTERED, u_nick.c_str());
- return;
- }
- }
-
- unsigned int passlen = Config->GetModule("nickserv")->Get<unsigned>("passlen", "32");
-
- if (Config->GetModule("nickserv")->Get<bool>("forceemail", "yes") && email.empty())
- this->OnSyntaxError(source, "");
- else if (u && Anope::CurTime < u->lastnickreg + reg_delay)
- source.Reply(_("Please wait %d seconds before using the REGISTER command again."), (u->lastnickreg + reg_delay) - Anope::CurTime);
- else if (NickAlias::Find(u_nick) != NULL)
- source.Reply(NICK_ALREADY_REGISTERED, u_nick.c_str());
- else if (pass.equals_ci(u_nick) || (Config->GetBlock("options")->Get<bool>("strictpasswords") && pass.length() < 5))
- source.Reply(MORE_OBSCURE_PASSWORD);
- else if (pass.length() > passlen)
- source.Reply(PASSWORD_TOO_LONG, passlen);
- else if (!email.empty() && !Mail::Validate(email))
- source.Reply(MAIL_X_INVALID, email.c_str());
- else
- {
- NickCore *nc = new NickCore(u_nick);
- NickAlias *na = new NickAlias(u_nick, nc);
- Anope::Encrypt(pass, nc->pass);
- if (!email.empty())
- nc->email = email;
-
- if (u)
- {
- na->last_usermask = u->GetIdent() + "@" + u->GetDisplayedHost();
- na->last_realname = u->realname;
- }
- else
- na->last_realname = source.GetNick();
-
- Log(LOG_COMMAND, source, this) << "to register " << na->nick << " (email: " << (!na->nc->email.empty() ? na->nc->email : "none") << ")";
-
- if (na->nc->GetAccessCount())
- source.Reply(_("Nickname \002%s\002 registered under your user@host-mask: %s"), u_nick.c_str(), na->nc->GetAccess(0).c_str());
- else
- source.Reply(_("Nickname \002%s\002 registered."), u_nick.c_str());
-
- Anope::string tmp_pass;
- if (Anope::Decrypt(na->nc->pass, tmp_pass) == 1)
- source.Reply(_("Your password is \002%s\002 - remember this for later use."), tmp_pass.c_str());
-
- if (nsregister.equals_ci("admin"))
- {
- nc->Extend<bool>("UNCONFIRMED");
- // User::Identify() called below will notify the user that their registration is pending
- }
- else if (nsregister.equals_ci("mail"))
- {
- if (!email.empty())
- {
- nc->Extend<bool>("UNCONFIRMED");
- SendRegmail(NULL, na, source.service);
- }
- }
-
- FOREACH_MOD(OnNickRegister, (source.GetUser(), na, pass));
-
- if (u)
- {
- u->Identify(na);
- u->lastnickreg = Anope::CurTime;
- }
- }
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Registers your nickname in the %s database. Once\n"
- "your nick is registered, you can use the \002SET\002 and \002ACCESS\002\n"
- "commands to configure your nick's settings as you like\n"
- "them. Make sure you remember the password you use when\n"
- "registering - you'll need it to make changes to your nick\n"
- "later. (Note that \002case matters!\002 \037ANOPE\037, \037Anope\037, and\n"
- "\037anope\037 are all different passwords!)\n"
- " \n"
- "Guidelines on choosing passwords:\n"
- " \n"
- "Passwords should not be easily guessable. For example,\n"
- "using your real name as a password is a bad idea. Using\n"
- "your nickname as a password is a much worse idea ;) and,\n"
- "in fact, %s will not allow it. Also, short\n"
- "passwords are vulnerable to trial-and-error searches, so\n"
- "you should choose a password at least 5 characters long.\n"
- "Finally, the space character cannot be used in passwords."),
- source.service->nick.c_str(), source.service->nick.c_str());
-
- if (!Config->GetModule("nickserv")->Get<bool>("forceemail", "yes"))
- {
- source.Reply(" ");
- source.Reply(_("The \037email\037 parameter is optional and will set the email\n"
- "for your nick immediately.\n"
- "Your privacy is respected; this e-mail won't be given to\n"
- "any third-party person. You may also wish to \002SET HIDE\002 it\n"
- "after registering if it isn't the default setting already."));
- }
-
- source.Reply(" ");
- source.Reply(_("This command also creates a new group for your nickname,\n"
- "that will allow you to register other nicks later sharing\n"
- "the same configuration, the same set of memos and the\n"
- "same channel privileges."));
- return true;
- }
-};
-
-class CommandNSResend : public Command
-{
- public:
- CommandNSResend(Module *creator) : Command(creator, "nickserv/resend", 0, 0)
- {
- this->SetDesc(_("Resend registration confirmation email"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (!Config->GetModule(this->owner)->Get<const Anope::string>("registration").equals_ci("mail"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- const NickAlias *na = NickAlias::Find(source.GetNick());
-
- if (na == NULL)
- source.Reply(NICK_NOT_REGISTERED);
- else if (na->nc != source.GetAccount() || !source.nc->HasExt("UNCONFIRMED"))
- source.Reply(_("Your account is already confirmed."));
- else
- {
- if (Anope::CurTime < source.nc->lastmail + Config->GetModule(this->owner)->Get<time_t>("resenddelay"))
- source.Reply(_("Cannot send mail now; please retry a little later."));
- else if (SendRegmail(source.GetUser(), na, source.service))
- {
- na->nc->lastmail = Anope::CurTime;
- source.Reply(_("Your passcode has been re-sent to %s."), na->nc->email.c_str());
- Log(LOG_COMMAND, source, this) << "to resend registration verification code";
- }
- else
- Log(this->owner) << "Unable to resend registration verification code for " << source.GetNick();
- }
-
- return;
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- if (!Config->GetModule(this->owner)->Get<const Anope::string>("registration").equals_ci("mail"))
- return false;
-
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("This command will resend you the registration confirmation email."));
- return true;
- }
-
- void OnServHelp(CommandSource &source) anope_override
- {
- if (Config->GetModule(this->owner)->Get<const Anope::string>("registration").equals_ci("mail"))
- Command::OnServHelp(source);
- }
-};
-
-class NSRegister : public Module
-{
- CommandNSRegister commandnsregister;
- CommandNSConfirm commandnsconfirm;
- CommandNSResend commandnsrsend;
-
- SerializableExtensibleItem<bool> unconfirmed;
- SerializableExtensibleItem<Anope::string> passcode;
-
- public:
- NSRegister(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandnsregister(this), commandnsconfirm(this), commandnsrsend(this), unconfirmed(this, "UNCONFIRMED"),
- passcode(this, "passcode")
- {
- if (Config->GetModule(this)->Get<const Anope::string>("registration").equals_ci("disable"))
- throw ModuleException("Module " + this->name + " will not load with registration disabled.");
- }
-
- void OnNickIdentify(User *u) anope_override
- {
- BotInfo *NickServ;
- if (unconfirmed.HasExt(u->Account()) && (NickServ = Config->GetClient("NickServ")))
- {
- const Anope::string &nsregister = Config->GetModule(this)->Get<const Anope::string>("registration");
- if (nsregister.equals_ci("admin"))
- u->SendMessage(NickServ, _("All new accounts must be validated by an administrator. Please wait for your registration to be confirmed."));
- else
- u->SendMessage(NickServ, _("Your email address is not confirmed. To confirm it, follow the instructions that were emailed to you."));
- const NickAlias *this_na = NickAlias::Find(u->Account()->display);
- time_t time_registered = Anope::CurTime - this_na->time_registered;
- time_t unconfirmed_expire = Config->GetModule(this)->Get<time_t>("unconfirmedexpire", "1d");
- if (unconfirmed_expire > time_registered)
- u->SendMessage(NickServ, _("Your account will expire, if not confirmed, in %s."), Anope::Duration(unconfirmed_expire - time_registered, u->Account()).c_str());
- }
- }
-
- void OnPreNickExpire(NickAlias *na, bool &expire) anope_override
- {
- if (unconfirmed.HasExt(na->nc))
- {
- time_t unconfirmed_expire = Config->GetModule(this)->Get<time_t>("unconfirmedexpire", "1d");
- if (unconfirmed_expire && Anope::CurTime - na->time_registered >= unconfirmed_expire)
- expire = true;
- }
- }
-};
-
-static bool SendRegmail(User *u, const NickAlias *na, BotInfo *bi)
-{
- NickCore *nc = na->nc;
-
- Anope::string *code = na->nc->GetExt<Anope::string>("passcode");
- if (code == NULL)
- {
- code = na->nc->Extend<Anope::string>("passcode");
- *code = Anope::Random(9);
- }
-
- Anope::string subject = Language::Translate(na->nc, Config->GetBlock("mail")->Get<const Anope::string>("registration_subject").c_str()),
- message = Language::Translate(na->nc, Config->GetBlock("mail")->Get<const Anope::string>("registration_message").c_str());
-
- subject = subject.replace_all_cs("%n", na->nick);
- subject = subject.replace_all_cs("%N", Config->GetBlock("networkinfo")->Get<const Anope::string>("networkname"));
- subject = subject.replace_all_cs("%c", *code);
-
- message = message.replace_all_cs("%n", na->nick);
- message = message.replace_all_cs("%N", Config->GetBlock("networkinfo")->Get<const Anope::string>("networkname"));
- message = message.replace_all_cs("%c", *code);
-
- return Mail::Send(u, nc, bi, subject, message);
-}
-
-MODULE_INIT(NSRegister)
diff --git a/modules/commands/ns_resetpass.cpp b/modules/commands/ns_resetpass.cpp
deleted file mode 100644
index b1f835a01..000000000
--- a/modules/commands/ns_resetpass.cpp
+++ /dev/null
@@ -1,143 +0,0 @@
-/* NickServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-static bool SendResetEmail(User *u, const NickAlias *na, BotInfo *bi);
-
-class CommandNSResetPass : public Command
-{
- public:
- CommandNSResetPass(Module *creator) : Command(creator, "nickserv/resetpass", 2, 2)
- {
- this->SetDesc(_("Helps you reset lost passwords"));
- this->SetSyntax(_("\037nickname\037 \037email\037"));
- this->AllowUnregistered(true);
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const NickAlias *na;
-
- if (!(na = NickAlias::Find(params[0])))
- source.Reply(NICK_X_NOT_REGISTERED, params[0].c_str());
- else if (!na->nc->email.equals_ci(params[1]))
- source.Reply(_("Incorrect email address."));
- else
- {
- if (SendResetEmail(source.GetUser(), na, source.service))
- {
- Log(LOG_COMMAND, source, this) << "for " << na->nick << " (group: " << na->nc->display << ")";
- source.Reply(_("Password reset email for \002%s\002 has been sent."), na->nick.c_str());
- }
- }
-
- return;
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Sends a passcode to the nickname with instructions on how to\n"
- "reset their password. Email must be the email address associated\n"
- "to the nickname."));
- return true;
- }
-};
-
-struct ResetInfo
-{
- Anope::string code;
- time_t time;
-};
-
-class NSResetPass : public Module
-{
- CommandNSResetPass commandnsresetpass;
- PrimitiveExtensibleItem<ResetInfo> reset;
-
- public:
- NSResetPass(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandnsresetpass(this), reset(this, "reset")
- {
- if (!Config->GetBlock("mail")->Get<bool>("usemail"))
- throw ModuleException("Not using mail.");
- }
-
- EventReturn OnPreCommand(CommandSource &source, Command *command, std::vector<Anope::string> &params) anope_override
- {
- if (command->name == "nickserv/confirm" && params.size() > 1)
- {
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return EVENT_STOP;
- }
-
- NickAlias *na = NickAlias::Find(params[0]);
-
- ResetInfo *ri = na ? reset.Get(na->nc) : NULL;
- if (na && ri)
- {
- NickCore *nc = na->nc;
- const Anope::string &passcode = params[1];
- if (ri->time < Anope::CurTime - 3600)
- {
- reset.Unset(nc);
- source.Reply(_("Your password reset request has expired."));
- }
- else if (passcode.equals_cs(ri->code))
- {
- reset.Unset(nc);
- nc->Shrink<bool>("UNCONFIRMED");
-
- Log(LOG_COMMAND, source, &commandnsresetpass) << "confirmed RESETPASS to forcefully identify as " << na->nick;
-
- if (source.GetUser())
- {
- source.GetUser()->Identify(na);
- source.Reply(_("You are now identified for your nick. Change your password now."));
- }
- }
- else
- return EVENT_CONTINUE;
-
- return EVENT_STOP;
- }
- }
-
- return EVENT_CONTINUE;
- }
-};
-
-static bool SendResetEmail(User *u, const NickAlias *na, BotInfo *bi)
-{
- Anope::string subject = Language::Translate(na->nc, Config->GetBlock("mail")->Get<const Anope::string>("reset_subject").c_str()),
- message = Language::Translate(na->nc, Config->GetBlock("mail")->Get<const Anope::string>("reset_message").c_str()),
- passcode = Anope::Random(20);
-
- subject = subject.replace_all_cs("%n", na->nick);
- subject = subject.replace_all_cs("%N", Config->GetBlock("networkinfo")->Get<const Anope::string>("networkname"));
- subject = subject.replace_all_cs("%c", passcode);
-
- message = message.replace_all_cs("%n", na->nick);
- message = message.replace_all_cs("%N", Config->GetBlock("networkinfo")->Get<const Anope::string>("networkname"));
- message = message.replace_all_cs("%c", passcode);
-
- ResetInfo *ri = na->nc->Extend<ResetInfo>("reset");
- ri->code = passcode;
- ri->time = Anope::CurTime;
-
- return Mail::Send(u, na->nc, bi, subject, message);
-}
-
-MODULE_INIT(NSResetPass)
diff --git a/modules/commands/ns_set.cpp b/modules/commands/ns_set.cpp
deleted file mode 100644
index f9351424e..000000000
--- a/modules/commands/ns_set.cpp
+++ /dev/null
@@ -1,1341 +0,0 @@
-/* NickServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class CommandNSSet : public Command
-{
- public:
- CommandNSSet(Module *creator) : Command(creator, "nickserv/set", 1, 3)
- {
- this->SetDesc(_("Set options, including kill protection"));
- this->SetSyntax(_("\037option\037 \037parameters\037"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- this->OnSyntaxError(source, "");
- return;
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Sets various nickname options. \037option\037 can be one of:"));
-
- Anope::string this_name = source.command;
- bool hide_privileged_commands = Config->GetBlock("options")->Get<bool>("hideprivilegedcommands"),
- hide_registered_commands = Config->GetBlock("options")->Get<bool>("hideregisteredcommands");
- for (CommandInfo::map::const_iterator it = source.service->commands.begin(), it_end = source.service->commands.end(); it != it_end; ++it)
- {
- const Anope::string &c_name = it->first;
- const CommandInfo &info = it->second;
-
- if (c_name.find_ci(this_name + " ") == 0)
- {
- ServiceReference<Command> c("Command", info.name);
- // XXX dup
- if (!c)
- continue;
- else if (hide_registered_commands && !c->AllowUnregistered() && !source.GetAccount())
- continue;
- else if (hide_privileged_commands && !info.permission.empty() && !source.HasCommand(info.permission))
- continue;
-
- source.command = c_name;
- c->OnServHelp(source);
- }
- }
-
- source.Reply(_("Type \002%s%s HELP %s \037option\037\002 for more information\n"
- "on a specific option."), Config->StrictPrivmsg.c_str(), source.service->nick.c_str(), this_name.c_str());
-
- return true;
- }
-};
-
-class CommandNSSASet : public Command
-{
- public:
- CommandNSSASet(Module *creator) : Command(creator, "nickserv/saset", 2, 4)
- {
- this->SetDesc(_("Set SET-options on another nickname"));
- this->SetSyntax(_("\037option\037 \037nickname\037 \037parameters\037"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- this->OnSyntaxError(source, "");
- return;
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Sets various nickname options. \037option\037 can be one of:"));
-
- Anope::string this_name = source.command;
- for (CommandInfo::map::const_iterator it = source.service->commands.begin(), it_end = source.service->commands.end(); it != it_end; ++it)
- {
- const Anope::string &c_name = it->first;
- const CommandInfo &info = it->second;
-
- if (c_name.find_ci(this_name + " ") == 0)
- {
- ServiceReference<Command> command("Command", info.name);
- if (command)
- {
- source.command = c_name;
- command->OnServHelp(source);
- }
- }
- }
-
- source.Reply(_("Type \002%s%s HELP %s \037option\037\002 for more information\n"
- "on a specific option. The options will be set on the given\n"
- "\037nickname\037."), Config->StrictPrivmsg.c_str(), source.service->nick.c_str(), this_name.c_str());
- return true;
- }
-};
-
-class CommandNSSetPassword : public Command
-{
- public:
- CommandNSSetPassword(Module *creator) : Command(creator, "nickserv/set/password", 1)
- {
- this->SetDesc(_("Set your nickname password"));
- this->SetSyntax(_("\037new-password\037"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &param = params[0];
- unsigned len = param.length();
-
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- if (source.GetNick().equals_ci(param) || (Config->GetBlock("options")->Get<bool>("strictpasswords") && len < 5))
- {
- source.Reply(MORE_OBSCURE_PASSWORD);
- return;
- }
-
- unsigned int passlen = Config->GetModule("nickserv")->Get<unsigned>("passlen", "32");
- if (len > passlen)
- {
- source.Reply(PASSWORD_TOO_LONG, passlen);
- return;
- }
-
- Log(LOG_COMMAND, source, this) << "to change their password";
-
- Anope::Encrypt(param, source.nc->pass);
- Anope::string tmp_pass;
- if (Anope::Decrypt(source.nc->pass, tmp_pass) == 1)
- source.Reply(_("Password for \002%s\002 changed to \002%s\002."), source.nc->display.c_str(), tmp_pass.c_str());
- else
- source.Reply(_("Password for \002%s\002 changed."), source.nc->display.c_str());
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Changes the password used to identify you as the nick's\n"
- "owner."));
- return true;
- }
-};
-
-class CommandNSSASetPassword : public Command
-{
- public:
- CommandNSSASetPassword(Module *creator) : Command(creator, "nickserv/saset/password", 2, 2)
- {
- this->SetDesc(_("Set the nickname password"));
- this->SetSyntax(_("\037nickname\037 \037new-password\037"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- const NickAlias *setter_na = NickAlias::Find(params[0]);
- if (setter_na == NULL)
- {
- source.Reply(NICK_X_NOT_REGISTERED, params[0].c_str());
- return;
- }
- NickCore *nc = setter_na->nc;
-
- size_t len = params[1].length();
-
- if (Config->GetModule("nickserv")->Get<bool>("secureadmins", "yes") && source.nc != nc && nc->IsServicesOper())
- {
- source.Reply(_("You may not change the password of other Services Operators."));
- return;
- }
-
- if (nc->display.equals_ci(params[1]) || (Config->GetBlock("options")->Get<bool>("strictpasswords") && len < 5))
- {
- source.Reply(MORE_OBSCURE_PASSWORD);
- return;
- }
-
- unsigned int passlen = Config->GetModule("nickserv")->Get<unsigned>("passlen", "32");
- if (len > passlen)
- {
- source.Reply(PASSWORD_TOO_LONG, passlen);
- return;
- }
-
- Log(LOG_ADMIN, source, this) << "to change the password of " << nc->display;
-
- Anope::Encrypt(params[1], nc->pass);
- Anope::string tmp_pass;
- if (Anope::Decrypt(nc->pass, tmp_pass) == 1)
- source.Reply(_("Password for \002%s\002 changed to \002%s\002."), nc->display.c_str(), tmp_pass.c_str());
- else
- source.Reply(_("Password for \002%s\002 changed."), nc->display.c_str());
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Changes the password used to identify as the nick's owner."));
- return true;
- }
-};
-
-class CommandNSSetAutoOp : public Command
-{
- public:
- CommandNSSetAutoOp(Module *creator, const Anope::string &sname = "nickserv/set/autoop", size_t min = 1) : Command(creator, sname, min, min + 1)
- {
- this->SetDesc(_("Sets whether services should set channel status modes on you automatically."));
- this->SetSyntax("{ON | OFF}");
- }
-
- void Run(CommandSource &source, const Anope::string &user, const Anope::string &param)
- {
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- const NickAlias *na = NickAlias::Find(user);
- if (na == NULL)
- {
- source.Reply(NICK_X_NOT_REGISTERED, user.c_str());
- return;
- }
- NickCore *nc = na->nc;
-
- EventReturn MOD_RESULT;
- FOREACH_RESULT(OnSetNickOption, MOD_RESULT, (source, this, nc, param));
- if (MOD_RESULT == EVENT_STOP)
- return;
-
- if (param.equals_ci("ON"))
- {
- Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to enable autoop for " << na->nc->display;
- nc->Extend<bool>("AUTOOP");
- source.Reply(_("Services will from now on set status modes on %s in channels."), nc->display.c_str());
- }
- else if (param.equals_ci("OFF"))
- {
- Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to disable autoop for " << na->nc->display;
- nc->Shrink<bool>("AUTOOP");
- source.Reply(_("Services will no longer set status modes on %s in channels."), nc->display.c_str());
- }
- else
- this->OnSyntaxError(source, "AUTOOP");
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- this->Run(source, source.nc->display, params[0]);
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
- {
- BotInfo *bi = Config->GetClient("ChanServ");
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Sets whether you will be given your channel status modes automatically.\n"
- "Set to \002ON\002 to allow %s to set status modes on you automatically\n"
- "when entering channels. Note that depending on channel settings some modes\n"
- "may not get set automatically."), bi ? bi->nick.c_str() : "ChanServ");
- return true;
- }
-};
-
-class CommandNSSASetAutoOp : public CommandNSSetAutoOp
-{
- public:
- CommandNSSASetAutoOp(Module *creator) : CommandNSSetAutoOp(creator, "nickserv/saset/autoop", 2)
- {
- this->ClearSyntax();
- this->SetSyntax(_("\037nickname\037 {ON | OFF}"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- this->Run(source, params[0], params[1]);
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
- {
- BotInfo *bi = Config->GetClient("ChanServ");
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Sets whether the given nickname will be given its status modes\n"
- "in channels automatically. Set to \002ON\002 to allow %s\n"
- "to set status modes on the given nickname automatically when it\n"
- "is entering channels. Note that depending on channel settings\n"
- "some modes may not get set automatically."), bi ? bi->nick.c_str() : "ChanServ");
- return true;
- }
-};
-
-class CommandNSSetDisplay : public Command
-{
- public:
- CommandNSSetDisplay(Module *creator, const Anope::string &sname = "nickserv/set/display", size_t min = 1) : Command(creator, sname, min, min + 1)
- {
- this->SetDesc(_("Set the display of your group in Services"));
- this->SetSyntax(_("\037new-display\037"));
- }
-
- void Run(CommandSource &source, const Anope::string &user, const Anope::string &param)
- {
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- NickAlias *user_na = NickAlias::Find(user), *na = NickAlias::Find(param);
-
- if (Config->GetModule("nickserv")->Get<bool>("nonicknameownership"))
- {
- source.Reply(_("This command may not be used on this network because nickname ownership is disabled."));
- return;
- }
- if (user_na == NULL)
- {
- source.Reply(NICK_X_NOT_REGISTERED, user.c_str());
- return;
- }
- else if (!na || *na->nc != *user_na->nc)
- {
- source.Reply(_("The new display MUST be a nickname of the nickname group %s."), user_na->nc->display.c_str());
- return;
- }
-
- EventReturn MOD_RESULT;
- FOREACH_RESULT(OnSetNickOption, MOD_RESULT, (source, this, user_na->nc, param));
- if (MOD_RESULT == EVENT_STOP)
- return;
-
- Log(user_na->nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to change the display of " << user_na->nc->display << " to " << na->nick;
-
- user_na->nc->SetDisplay(na);
-
- /* Send updated account name */
- for (std::list<User *>::iterator it = user_na->nc->users.begin(); it != user_na->nc->users.end(); ++it)
- {
- User *u = *it;
- IRCD->SendLogin(u, user_na);
- }
-
- source.Reply(NICK_SET_DISPLAY_CHANGED, user_na->nc->display.c_str());
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- this->Run(source, source.nc->display, params[0]);
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Changes the display used to refer to your nickname group in\n"
- "Services. The new display MUST be a nick of your group."));
- return true;
- }
-};
-
-class CommandNSSASetDisplay : public CommandNSSetDisplay
-{
- public:
- CommandNSSASetDisplay(Module *creator) : CommandNSSetDisplay(creator, "nickserv/saset/display", 2)
- {
- this->ClearSyntax();
- this->SetSyntax(_("\037nickname\037 \037new-display\037"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- this->Run(source, params[0], params[1]);
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Changes the display used to refer to the nickname group in\n"
- "Services. The new display MUST be a nick of the group."));
- return true;
- }
-};
-
-class CommandNSSetEmail : public Command
-{
- static bool SendConfirmMail(User *u, BotInfo *bi, const Anope::string &new_email)
- {
- Anope::string code = Anope::Random(9);
-
- std::pair<Anope::string, Anope::string> *n = u->Account()->Extend<std::pair<Anope::string, Anope::string> >("ns_set_email");
- n->first = new_email;
- n->second = code;
-
- Anope::string subject = Config->GetBlock("mail")->Get<const Anope::string>("emailchange_subject"),
- message = Config->GetBlock("mail")->Get<const Anope::string>("emailchange_message");
-
- subject = subject.replace_all_cs("%e", u->Account()->email);
- subject = subject.replace_all_cs("%E", new_email);
- subject = subject.replace_all_cs("%N", Config->GetBlock("networkinfo")->Get<const Anope::string>("networkname"));
- subject = subject.replace_all_cs("%c", code);
-
- message = message.replace_all_cs("%e", u->Account()->email);
- message = message.replace_all_cs("%E", new_email);
- message = message.replace_all_cs("%N", Config->GetBlock("networkinfo")->Get<const Anope::string>("networkname"));
- message = message.replace_all_cs("%c", code);
-
- Anope::string old = u->Account()->email;
- u->Account()->email = new_email;
- bool b = Mail::Send(u, u->Account(), bi, subject, message);
- u->Account()->email = old;
- return b;
- }
-
- public:
- CommandNSSetEmail(Module *creator, const Anope::string &cname = "nickserv/set/email", size_t min = 0) : Command(creator, cname, min, min + 1)
- {
- this->SetDesc(_("Associate an E-mail address with your nickname"));
- this->SetSyntax(_("\037address\037"));
- }
-
- void Run(CommandSource &source, const Anope::string &user, const Anope::string &param)
- {
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- const NickAlias *na = NickAlias::Find(user);
- if (!na)
- {
- source.Reply(NICK_X_NOT_REGISTERED, user.c_str());
- return;
- }
- NickCore *nc = na->nc;
-
- if (nc->HasExt("UNCONFIRMED"))
- {
- source.Reply(_("You may not change the email of an unconfirmed account."));
- return;
- }
-
- if (param.empty() && Config->GetModule("nickserv")->Get<bool>("forceemail", "yes"))
- {
- source.Reply(_("You cannot unset the e-mail on this network."));
- return;
- }
- else if (Config->GetModule("nickserv")->Get<bool>("secureadmins", "yes") && source.nc != nc && nc->IsServicesOper())
- {
- source.Reply(_("You may not change the e-mail of other Services Operators."));
- return;
- }
- else if (!param.empty() && !Mail::Validate(param))
- {
- source.Reply(MAIL_X_INVALID, param.c_str());
- return;
- }
-
- EventReturn MOD_RESULT;
- FOREACH_RESULT(OnSetNickOption, MOD_RESULT, (source, this, nc, param));
- if (MOD_RESULT == EVENT_STOP)
- return;
-
- if (!param.empty() && Config->GetModule("nickserv")->Get<bool>("confirmemailchanges") && !source.IsServicesOper())
- {
- if (SendConfirmMail(source.GetUser(), source.service, param))
- source.Reply(_("A confirmation e-mail has been sent to \002%s\002. Follow the instructions in it to change your e-mail address."), param.c_str());
- }
- else
- {
- if (!param.empty())
- {
- Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to change the email of " << nc->display << " to " << param;
- nc->email = param;
- source.Reply(_("E-mail address for \002%s\002 changed to \002%s\002."), nc->display.c_str(), param.c_str());
- }
- else
- {
- Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to unset the email of " << nc->display;
- nc->email.clear();
- source.Reply(_("E-mail address for \002%s\002 unset."), nc->display.c_str());
- }
- }
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- this->Run(source, source.nc->display, params.size() ? params[0] : "");
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Associates the given E-mail address with your nickname.\n"
- "This address will be displayed whenever someone requests\n"
- "information on the nickname with the \002INFO\002 command."));
- return true;
- }
-};
-
-class CommandNSSASetEmail : public CommandNSSetEmail
-{
- public:
- CommandNSSASetEmail(Module *creator) : CommandNSSetEmail(creator, "nickserv/saset/email", 2)
- {
- this->ClearSyntax();
- this->SetSyntax(_("\037nickname\037 \037address\037"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- this->Run(source, params[0], params.size() > 1 ? params[1] : "");
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Associates the given E-mail address with the nickname."));
- return true;
- }
-};
-
-class CommandNSSetKeepModes : public Command
-{
- public:
- CommandNSSetKeepModes(Module *creator, const Anope::string &sname = "nickserv/set/keepmodes", size_t min = 1) : Command(creator, sname, min, min + 1)
- {
- this->SetDesc(_("Enable or disable keep modes"));
- this->SetSyntax("{ON | OFF}");
- }
-
- void Run(CommandSource &source, const Anope::string &user, const Anope::string &param)
- {
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- const NickAlias *na = NickAlias::Find(user);
- if (!na)
- {
- source.Reply(NICK_X_NOT_REGISTERED, user.c_str());
- return;
- }
- NickCore *nc = na->nc;
-
- EventReturn MOD_RESULT;
- FOREACH_RESULT(OnSetNickOption, MOD_RESULT, (source, this, nc, param));
- if (MOD_RESULT == EVENT_STOP)
- return;
-
- if (param.equals_ci("ON"))
- {
- Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to enable keepmodes for " << nc->display;
- nc->Extend<bool>("NS_KEEP_MODES");
- source.Reply(_("Keep modes for %s is now \002on\002."), nc->display.c_str());
- }
- else if (param.equals_ci("OFF"))
- {
- Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to disable keepmodes for " << nc->display;
- nc->Shrink<bool>("NS_KEEP_MODES");
- source.Reply(_("Keep modes for %s is now \002off\002."), nc->display.c_str());
- }
- else
- this->OnSyntaxError(source, "");
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- this->Run(source, source.nc->display, params[0]);
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Enables or disables keepmodes for your nick. If keep\n"
- "modes is enabled, services will remember your usermodes\n"
- "and attempt to re-set them the next time you authenticate."));
- return true;
- }
-};
-
-class CommandNSSASetKeepModes : public CommandNSSetKeepModes
-{
- public:
- CommandNSSASetKeepModes(Module *creator) : CommandNSSetKeepModes(creator, "nickserv/saset/keepmodes", 2)
- {
- this->ClearSyntax();
- this->SetSyntax(_("\037nickname\037 {ON | OFF}"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- this->Run(source, params[0], params[1]);
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Enables or disables keepmodes for the given nick. If keep\n"
- "modes is enabled, services will remember users' usermodes\n"
- "and attempt to re-set them the next time they authenticate."));
- return true;
- }
-};
-
-class CommandNSSetKill : public Command
-{
- public:
- CommandNSSetKill(Module *creator, const Anope::string &sname = "nickserv/set/kill", size_t min = 1) : Command(creator, sname, min, min + 1)
- {
- this->SetDesc(_("Turn protection on or off"));
- this->SetSyntax("{ON | QUICK | IMMED | OFF}");
- }
-
- void Run(CommandSource &source, const Anope::string &user, const Anope::string &param)
- {
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- if (Config->GetModule("nickserv")->Get<bool>("nonicknameownership"))
- {
- source.Reply(_("This command may not be used on this network because nickname ownership is disabled."));
- return;
- }
-
- const NickAlias *na = NickAlias::Find(user);
- if (!na)
- {
- source.Reply(NICK_X_NOT_REGISTERED, user.c_str());
- return;
- }
- NickCore *nc = na->nc;
-
- EventReturn MOD_RESULT;
- FOREACH_RESULT(OnSetNickOption, MOD_RESULT, (source, this, nc, param));
- if (MOD_RESULT == EVENT_STOP)
- return;
-
- if (param.equals_ci("ON"))
- {
- nc->Extend<bool>("KILLPROTECT");
- nc->Shrink<bool>("KILL_QUICK");
- nc->Shrink<bool>("KILL_IMMED");
- Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to set kill on for " << nc->display;
- source.Reply(_("Protection is now \002on\002 for \002%s\002."), nc->display.c_str());
- }
- else if (param.equals_ci("QUICK"))
- {
- nc->Extend<bool>("KILLPROTECT");
- nc->Extend<bool>("KILL_QUICK");
- nc->Shrink<bool>("KILL_IMMED");
- Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to set kill quick for " << nc->display;
- source.Reply(_("Protection is now \002on\002 for \002%s\002, with a reduced delay."), nc->display.c_str());
- }
- else if (param.equals_ci("IMMED"))
- {
- if (Config->GetModule(this->owner)->Get<bool>("allowkillimmed"))
- {
- nc->Extend<bool>("KILLPROTECT");
- nc->Shrink<bool>("KILL_QUICK");
- nc->Extend<bool>("KILL_IMMED");
- Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to set kill immed for " << nc->display;
- source.Reply(_("Protection is now \002on\002 for \002%s\002, with no delay."), nc->display.c_str());
- }
- else
- source.Reply(_("The \002IMMED\002 option is not available on this network."));
- }
- else if (param.equals_ci("OFF"))
- {
- nc->Shrink<bool>("KILLPROTECT");
- nc->Shrink<bool>("KILL_QUICK");
- nc->Shrink<bool>("KILL_IMMED");
- Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to disable kill for " << nc->display;
- source.Reply(_("Protection is now \002off\002 for \002%s\002."), nc->display.c_str());
- }
- else
- this->OnSyntaxError(source, "KILL");
-
- return;
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- this->Run(source, source.nc->display, params[0]);
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Turns the automatic protection option for your nick\n"
- "on or off. With protection on, if another user\n"
- "tries to take your nick, they will be given one minute to\n"
- "change to another nick, after which %s will forcibly change\n"
- "their nick.\n"
- " \n"
- "If you select \002QUICK\002, the user will be given only 20 seconds\n"
- "to change nicks instead of the usual 60. If you select\n"
- "\002IMMED\002, the user's nick will be changed immediately \037without\037 being\n"
- "warned first or given a chance to change their nick; please\n"
- "do not use this option unless necessary. Also, your\n"
- "network's administrators may have disabled this option."), source.service->nick.c_str());
- return true;
- }
-};
-
-class CommandNSSASetKill : public CommandNSSetKill
-{
- public:
- CommandNSSASetKill(Module *creator) : CommandNSSetKill(creator, "nickserv/saset/kill", 2)
- {
- this->ClearSyntax();
- this->SetSyntax(_("\037nickname\037 {ON | QUICK | IMMED | OFF}"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- this->Run(source, params[0], params[1]);
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Turns the automatic protection option for the nick\n"
- "on or off. With protection on, if another user\n"
- "tries to take the nick, they will be given one minute to\n"
- "change to another nick, after which %s will forcibly change\n"
- "their nick.\n"
- " \n"
- "If you select \002QUICK\002, the user will be given only 20 seconds\n"
- "to change nicks instead of the usual 60. If you select\n"
- "\002IMMED\002, the user's nick will be changed immediately \037without\037 being\n"
- "warned first or given a chance to change their nick; please\n"
- "do not use this option unless necessary. Also, your\n"
- "network's administrators may have disabled this option."), source.service->nick.c_str());
- return true;
- }
-};
-
-class CommandNSSetLanguage : public Command
-{
- public:
- CommandNSSetLanguage(Module *creator, const Anope::string &sname = "nickserv/set/language", size_t min = 1) : Command(creator, sname, min, min + 1)
- {
- this->SetDesc(_("Set the language Services will use when messaging you"));
- this->SetSyntax(_("\037language\037"));
- }
-
- void Run(CommandSource &source, const Anope::string &user, const Anope::string &param)
- {
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- const NickAlias *na = NickAlias::Find(user);
- if (!na)
- {
- source.Reply(NICK_X_NOT_REGISTERED, user.c_str());
- return;
- }
- NickCore *nc = na->nc;
-
- EventReturn MOD_RESULT;
- FOREACH_RESULT(OnSetNickOption, MOD_RESULT, (source, this, nc, param));
- if (MOD_RESULT == EVENT_STOP)
- return;
-
- if (param != "en_US")
- for (unsigned j = 0; j < Language::Languages.size(); ++j)
- {
- if (Language::Languages[j] == param)
- break;
- else if (j + 1 == Language::Languages.size())
- {
- this->OnSyntaxError(source, "");
- return;
- }
- }
-
- Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to change the language of " << nc->display << " to " << param;
-
- nc->language = param;
- if (source.GetAccount() == nc)
- source.Reply(_("Language changed to \002English\002."));
- else
- source.Reply(_("Language for \002%s\002 changed to \002%s\002."), nc->display.c_str(), Language::Translate(param.c_str(), _("English")));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &param) anope_override
- {
- this->Run(source, source.nc->display, param[0]);
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Changes the language Services uses when sending messages to\n"
- "you (for example, when responding to a command you send).\n"
- "\037language\037 should be chosen from the following list of\n"
- "supported languages:"));
-
- source.Reply(" en_US (English)");
- for (unsigned j = 0; j < Language::Languages.size(); ++j)
- {
- const Anope::string &langname = Language::Translate(Language::Languages[j].c_str(), _("English"));
- if (langname == "English")
- continue;
- source.Reply(" %s (%s)", Language::Languages[j].c_str(), langname.c_str());
- }
-
- return true;
- }
-};
-
-class CommandNSSASetLanguage : public CommandNSSetLanguage
-{
- public:
- CommandNSSASetLanguage(Module *creator) : CommandNSSetLanguage(creator, "nickserv/saset/language", 2)
- {
- this->ClearSyntax();
- this->SetSyntax(_("\037nickname\037 \037language\037"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- this->Run(source, params[0], params[1]);
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Changes the language Services uses when sending messages to\n"
- "the given user (for example, when responding to a command they send).\n"
- "\037language\037 should be chosen from the following list of\n"
- "supported languages:"));
- source.Reply(" en (English)");
- for (unsigned j = 0; j < Language::Languages.size(); ++j)
- {
- const Anope::string &langname = Language::Translate(Language::Languages[j].c_str(), _("English"));
- if (langname == "English")
- continue;
- source.Reply(" %s (%s)", Language::Languages[j].c_str(), langname.c_str());
- }
- return true;
- }
-};
-
-class CommandNSSetMessage : public Command
-{
- public:
- CommandNSSetMessage(Module *creator, const Anope::string &sname = "nickserv/set/message", size_t min = 1) : Command(creator, sname, min, min + 1)
- {
- this->SetDesc(_("Change the communication method of Services"));
- this->SetSyntax("{ON | OFF}");
- }
-
- void Run(CommandSource &source, const Anope::string &user, const Anope::string &param)
- {
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- const NickAlias *na = NickAlias::Find(user);
- if (!na)
- {
- source.Reply(NICK_X_NOT_REGISTERED, user.c_str());
- return;
- }
- NickCore *nc = na->nc;
-
- if (!Config->GetBlock("options")->Get<bool>("useprivmsg"))
- {
- source.Reply(_("You cannot %s on this network."), source.command.c_str());
- return;
- }
-
- EventReturn MOD_RESULT;
- FOREACH_RESULT(OnSetNickOption, MOD_RESULT, (source, this, nc, param));
- if (MOD_RESULT == EVENT_STOP)
- return;
-
- if (param.equals_ci("ON"))
- {
- Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to enable " << source.command << " for " << nc->display;
- nc->Extend<bool>("MSG");
- source.Reply(_("Services will now reply to \002%s\002 with \002messages\002."), nc->display.c_str());
- }
- else if (param.equals_ci("OFF"))
- {
- Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to disable " << source.command << " for " << nc->display;
- nc->Shrink<bool>("MSG");
- source.Reply(_("Services will now reply to \002%s\002 with \002notices\002."), nc->display.c_str());
- }
- else
- this->OnSyntaxError(source, "MSG");
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- this->Run(source, source.nc->display, params[0]);
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
- {
- Anope::string cmd = source.command;
- size_t i = cmd.find_last_of(' ');
- if (i != Anope::string::npos)
- cmd = cmd.substr(i + 1);
-
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Allows you to choose the way Services are communicating with\n"
- "you. With \002%s\002 set, Services will use messages, else they'll\n"
- "use notices."), cmd.upper().c_str());
- return true;
- }
-
- void OnServHelp(CommandSource &source) anope_override
- {
- if (!Config->GetBlock("options")->Get<bool>("useprivmsg"))
- Command::OnServHelp(source);
- }
-};
-
-class CommandNSSASetMessage : public CommandNSSetMessage
-{
- public:
- CommandNSSASetMessage(Module *creator) : CommandNSSetMessage(creator, "nickserv/saset/message", 2)
- {
- this->ClearSyntax();
- this->SetSyntax(_("\037nickname\037 {ON | OFF}"));
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Allows you to choose the way Services are communicating with\n"
- "the given user. With \002MSG\002 set, Services will use messages,\n"
- "else they'll use notices."));
- return true;
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- this->Run(source, params[0], params[1]);
- }
-};
-
-class CommandNSSetSecure : public Command
-{
- public:
- CommandNSSetSecure(Module *creator, const Anope::string &sname = "nickserv/set/secure", size_t min = 1) : Command(creator, sname, min, min + 1)
- {
- this->SetDesc(_("Turn nickname security on or off"));
- this->SetSyntax("{ON | OFF}");
- }
-
- void Run(CommandSource &source, const Anope::string &user, const Anope::string &param)
- {
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- const NickAlias *na = NickAlias::Find(user);
- if (!na)
- {
- source.Reply(NICK_X_NOT_REGISTERED, user.c_str());
- return;
- }
- NickCore *nc = na->nc;
-
- EventReturn MOD_RESULT;
- FOREACH_RESULT(OnSetNickOption, MOD_RESULT, (source, this, nc, param));
- if (MOD_RESULT == EVENT_STOP)
- return;
-
- if (param.equals_ci("ON"))
- {
- Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to enable secure for " << nc->display;
- nc->Extend<bool>("NS_SECURE");
- source.Reply(_("Secure option is now \002on\002 for \002%s\002."), nc->display.c_str());
- }
- else if (param.equals_ci("OFF"))
- {
- Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to disable secure for " << nc->display;
- nc->Shrink<bool>("NS_SECURE");
- source.Reply(_("Secure option is now \002off\002 for \002%s\002."), nc->display.c_str());
- }
- else
- this->OnSyntaxError(source, "SECURE");
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- this->Run(source, source.nc->display, params[0]);
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Turns %s's security features on or off for your\n"
- "nick. With \002SECURE\002 set, you must enter your password\n"
- "before you will be recognized as the owner of the nick,\n"
- "regardless of whether your address is on the access\n"
- "list. However, if you are on the access list, %s\n"
- "will not auto-kill you regardless of the setting of the\n"
- "\002KILL\002 option."), source.service->nick.c_str(), source.service->nick.c_str());
- return true;
- }
-};
-
-class CommandNSSASetSecure : public CommandNSSetSecure
-{
- public:
- CommandNSSASetSecure(Module *creator) : CommandNSSetSecure(creator, "nickserv/saset/secure", 2)
- {
- this->ClearSyntax();
- this->SetSyntax(_("\037nickname\037 {ON | OFF}"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- this->Run(source, params[0], params[1]);
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Turns %s's security features on or off for your\n"
- "nick. With \002SECURE\002 set, you must enter your password\n"
- "before you will be recognized as the owner of the nick,\n"
- "regardless of whether your address is on the access\n"
- "list. However, if you are on the access list, %s\n"
- "will not auto-kill you regardless of the setting of the\n"
- "\002KILL\002 option."), source.service->nick.c_str(), source.service->nick.c_str());
- return true;
- }
-};
-
-class CommandNSSASetNoexpire : public Command
-{
- public:
- CommandNSSASetNoexpire(Module *creator) : Command(creator, "nickserv/saset/noexpire", 1, 2)
- {
- this->SetDesc(_("Prevent the nickname from expiring"));
- this->SetSyntax(_("\037nickname\037 {ON | OFF}"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- NickAlias *na = NickAlias::Find(params[0]);
- if (na == NULL)
- {
- source.Reply(NICK_X_NOT_REGISTERED, params[0].c_str());
- return;
- }
-
- Anope::string param = params.size() > 1 ? params[1] : "";
-
- if (param.equals_ci("ON"))
- {
- Log(LOG_ADMIN, source, this) << "to enable noexpire for " << na->nick << " (" << na->nc->display << ")";
- na->Extend<bool>("NS_NO_EXPIRE");
- source.Reply(_("Nick %s \002will not\002 expire."), na->nick.c_str());
- }
- else if (param.equals_ci("OFF"))
- {
- Log(LOG_ADMIN, source, this) << "to disable noexpire for " << na->nick << " (" << na->nc->display << ")";
- na->Shrink<bool>("NS_NO_EXPIRE");
- source.Reply(_("Nick %s \002will\002 expire."), na->nick.c_str());
- }
- else
- this->OnSyntaxError(source, "NOEXPIRE");
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Sets whether the given nickname will expire. Setting this\n"
- "to \002ON\002 prevents the nickname from expiring."));
- return true;
- }
-};
-
-class NSSet : public Module
-{
- CommandNSSet commandnsset;
- CommandNSSASet commandnssaset;
-
- CommandNSSetAutoOp commandnssetautoop;
- CommandNSSASetAutoOp commandnssasetautoop;
-
- CommandNSSetDisplay commandnssetdisplay;
- CommandNSSASetDisplay commandnssasetdisplay;
-
- CommandNSSetEmail commandnssetemail;
- CommandNSSASetEmail commandnssasetemail;
-
- CommandNSSetKeepModes commandnssetkeepmodes;
- CommandNSSASetKeepModes commandnssasetkeepmodes;
-
- CommandNSSetKill commandnssetkill;
- CommandNSSASetKill commandnssasetkill;
-
- CommandNSSetLanguage commandnssetlanguage;
- CommandNSSASetLanguage commandnssasetlanguage;
-
- CommandNSSetMessage commandnssetmessage;
- CommandNSSASetMessage commandnssasetmessage;
-
- CommandNSSetPassword commandnssetpassword;
- CommandNSSASetPassword commandnssasetpassword;
-
- CommandNSSetSecure commandnssetsecure;
- CommandNSSASetSecure commandnssasetsecure;
-
- CommandNSSASetNoexpire commandnssasetnoexpire;
-
- SerializableExtensibleItem<bool> autoop, killprotect, kill_quick, kill_immed,
- message, secure, noexpire;
-
- struct KeepModes : SerializableExtensibleItem<bool>
- {
- KeepModes(Module *m, const Anope::string &n) : SerializableExtensibleItem<bool>(m, n) { }
-
- void ExtensibleSerialize(const Extensible *e, const Serializable *s, Serialize::Data &data) const anope_override
- {
- SerializableExtensibleItem<bool>::ExtensibleSerialize(e, s, data);
-
- if (s->GetSerializableType()->GetName() != "NickCore")
- return;
-
- const NickCore *nc = anope_dynamic_static_cast<const NickCore *>(s);
- Anope::string modes;
- for (User::ModeList::const_iterator it = nc->last_modes.begin(); it != nc->last_modes.end(); ++it)
- {
- if (!modes.empty())
- modes += " ";
- modes += it->first;
- if (!it->second.empty())
- modes += "," + it->second;
- }
- data["last_modes"] << modes;
- }
-
- void ExtensibleUnserialize(Extensible *e, Serializable *s, Serialize::Data &data) anope_override
- {
- SerializableExtensibleItem<bool>::ExtensibleUnserialize(e, s, data);
-
- if (s->GetSerializableType()->GetName() != "NickCore")
- return;
-
- NickCore *nc = anope_dynamic_static_cast<NickCore *>(s);
- Anope::string modes;
- data["last_modes"] >> modes;
- for (spacesepstream sep(modes); sep.GetToken(modes);)
- {
- size_t c = modes.find(',');
- if (c == Anope::string::npos)
- nc->last_modes.insert(std::make_pair(modes, ""));
- else
- nc->last_modes.insert(std::make_pair(modes.substr(0, c), modes.substr(c + 1)));
- }
- }
- } keep_modes;
-
- /* email, passcode */
- PrimitiveExtensibleItem<std::pair<Anope::string, Anope::string > > ns_set_email;
-
- public:
- NSSet(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandnsset(this), commandnssaset(this),
- commandnssetautoop(this), commandnssasetautoop(this),
- commandnssetdisplay(this), commandnssasetdisplay(this),
- commandnssetemail(this), commandnssasetemail(this),
- commandnssetkeepmodes(this), commandnssasetkeepmodes(this),
- commandnssetkill(this), commandnssasetkill(this),
- commandnssetlanguage(this), commandnssasetlanguage(this),
- commandnssetmessage(this), commandnssasetmessage(this),
- commandnssetpassword(this), commandnssasetpassword(this),
- commandnssetsecure(this), commandnssasetsecure(this),
- commandnssasetnoexpire(this),
-
- autoop(this, "AUTOOP"),
- killprotect(this, "KILLPROTECT"), kill_quick(this, "KILL_QUICK"),
- kill_immed(this, "KILL_IMMED"), message(this, "MSG"),
- secure(this, "NS_SECURE"), noexpire(this, "NS_NO_EXPIRE"),
-
- keep_modes(this, "NS_KEEP_MODES"), ns_set_email(this, "ns_set_email")
- {
-
- }
-
- EventReturn OnPreCommand(CommandSource &source, Command *command, std::vector<Anope::string> &params) anope_override
- {
- NickCore *uac = source.nc;
-
- if (command->name == "nickserv/confirm" && !params.empty() && uac)
- {
- std::pair<Anope::string, Anope::string> *n = ns_set_email.Get(uac);
- if (n)
- {
- if (params[0] == n->second)
- {
- uac->email = n->first;
- Log(LOG_COMMAND, source, command) << "to confirm their email address change to " << uac->email;
- source.Reply(_("Your email address has been changed to \002%s\002."), uac->email.c_str());
- ns_set_email.Unset(uac);
- return EVENT_STOP;
- }
- }
- }
-
- return EVENT_CONTINUE;
- }
-
- void OnSetCorrectModes(User *user, Channel *chan, AccessGroup &access, bool &give_modes, bool &take_modes) anope_override
- {
- if (chan->ci)
- {
- /* Only give modes if autoop is set */
- give_modes &= !user->Account() || autoop.HasExt(user->Account());
- }
- }
-
- void OnPreNickExpire(NickAlias *na, bool &expire) anope_override
- {
- if (noexpire.HasExt(na))
- expire = false;
- }
-
- void OnNickInfo(CommandSource &source, NickAlias *na, InfoFormatter &info, bool show_hidden) anope_override
- {
- if (!show_hidden)
- return;
-
- if (kill_immed.HasExt(na->nc))
- info.AddOption(_("Immediate protection"));
- else if (kill_quick.HasExt(na->nc))
- info.AddOption(_("Quick protection"));
- else if (killprotect.HasExt(na->nc))
- info.AddOption(_("Protection"));
- if (secure.HasExt(na->nc))
- info.AddOption(_("Security"));
- if (message.HasExt(na->nc))
- info.AddOption(_("Message mode"));
- if (autoop.HasExt(na->nc))
- info.AddOption(_("Auto-op"));
- if (noexpire.HasExt(na))
- info.AddOption(_("No expire"));
- if (keep_modes.HasExt(na->nc))
- info.AddOption(_("Keep modes"));
- }
-
- void OnUserModeSet(const MessageSource &setter, User *u, const Anope::string &mname) anope_override
- {
- if (u->Account() && setter.GetUser() == u)
- u->Account()->last_modes = u->GetModeList();
- }
-
- void OnUserModeUnset(const MessageSource &setter, User *u, const Anope::string &mname) anope_override
- {
- if (u->Account() && setter.GetUser() == u)
- u->Account()->last_modes = u->GetModeList();
- }
-
- void OnUserLogin(User *u) anope_override
- {
- if (keep_modes.HasExt(u->Account()))
- {
- User::ModeList modes = u->Account()->last_modes;
- for (User::ModeList::iterator it = modes.begin(); it != modes.end(); ++it)
- {
- UserMode *um = ModeManager::FindUserModeByName(it->first);
- /* if the null user can set the mode, then it's probably safe */
- if (um && um->CanSet(NULL))
- u->SetMode(NULL, it->first, it->second);
- }
- }
- }
-};
-
-MODULE_INIT(NSSet)
diff --git a/modules/commands/ns_set_misc.cpp b/modules/commands/ns_set_misc.cpp
deleted file mode 100644
index c145e386c..000000000
--- a/modules/commands/ns_set_misc.cpp
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-#include "modules/set_misc.h"
-
-static Module *me;
-
-static Anope::map<Anope::string> descriptions;
-
-struct NSMiscData;
-static Anope::map<ExtensibleItem<NSMiscData> *> items;
-
-static ExtensibleItem<NSMiscData> *GetItem(const Anope::string &name)
-{
- ExtensibleItem<NSMiscData>* &it = items[name];
- if (!it)
- try
- {
- it = new ExtensibleItem<NSMiscData>(me, name);
- }
- catch (const ModuleException &) { }
- return it;
-}
-
-struct NSMiscData : MiscData, Serializable
-{
- NSMiscData(Extensible *) : Serializable("NSMiscData") { }
-
- NSMiscData(NickCore *ncore, const Anope::string &n, const Anope::string &d) : Serializable("NSMiscData")
- {
- object = ncore->display;
- name = n;
- data = d;
- }
-
- void Serialize(Serialize::Data &sdata) const anope_override
- {
- sdata["nc"] << this->object;
- sdata["name"] << this->name;
- sdata["data"] << this->data;
- }
-
- static Serializable* Unserialize(Serializable *obj, Serialize::Data &data)
- {
- Anope::string snc, sname, sdata;
-
- data["nc"] >> snc;
- data["name"] >> sname;
- data["data"] >> sdata;
-
- NickCore *nc = NickCore::Find(snc);
- if (nc == NULL)
- return NULL;
-
- NSMiscData *d = NULL;
- if (obj)
- {
- d = anope_dynamic_static_cast<NSMiscData *>(obj);
- d->object = nc->display;
- data["name"] >> d->name;
- data["data"] >> d->data;
- }
- else
- {
- ExtensibleItem<NSMiscData> *item = GetItem(sname);
- if (item)
- d = item->Set(nc, NSMiscData(nc, sname, sdata));
- }
-
- return d;
- }
-};
-
-static Anope::string GetAttribute(const Anope::string &command)
-{
- size_t sp = command.rfind(' ');
- if (sp != Anope::string::npos)
- return command.substr(sp + 1);
- return command;
-}
-
-class CommandNSSetMisc : public Command
-{
- public:
- CommandNSSetMisc(Module *creator, const Anope::string &cname = "nickserv/set/misc", size_t min = 0) : Command(creator, cname, min, min + 1)
- {
- this->SetSyntax(_("[\037parameter\037]"));
- }
-
- void Run(CommandSource &source, const Anope::string &user, const Anope::string &param)
- {
- if (Anope::ReadOnly)
- {
- source.Reply(READ_ONLY_MODE);
- return;
- }
-
- const NickAlias *na = NickAlias::Find(user);
- if (!na)
- {
- source.Reply(NICK_X_NOT_REGISTERED, user.c_str());
- return;
- }
- NickCore *nc = na->nc;
-
- EventReturn MOD_RESULT;
- FOREACH_RESULT(OnSetNickOption, MOD_RESULT, (source, this, nc, param));
- if (MOD_RESULT == EVENT_STOP)
- return;
-
- Anope::string scommand = GetAttribute(source.command);
- Anope::string key = "ns_set_misc:" + scommand;
- ExtensibleItem<NSMiscData> *item = GetItem(key);
- if (item == NULL)
- return;
-
- if (!param.empty())
- {
- item->Set(nc, NSMiscData(nc, key, param));
- source.Reply(CHAN_SETTING_CHANGED, scommand.c_str(), nc->display.c_str(), param.c_str());
- }
- else
- {
- item->Unset(nc);
- source.Reply(CHAN_SETTING_UNSET, scommand.c_str(), nc->display.c_str());
- }
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- this->Run(source, source.nc->display, !params.empty() ? params[0] : "");
- }
-
- void OnServHelp(CommandSource &source) anope_override
- {
- if (descriptions.count(source.command))
- {
- this->SetDesc(descriptions[source.command]);
- Command::OnServHelp(source);
- }
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- if (descriptions.count(source.command))
- {
- source.Reply("%s", Language::Translate(source.nc, descriptions[source.command].c_str()));
- return true;
- }
- return false;
- }
-};
-
-class CommandNSSASetMisc : public CommandNSSetMisc
-{
- public:
- CommandNSSASetMisc(Module *creator) : CommandNSSetMisc(creator, "nickserv/saset/misc", 1)
- {
- this->ClearSyntax();
- this->SetSyntax(_("\037nickname\037 [\037parameter\037]"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- this->Run(source, params[0], params.size() > 1 ? params[1] : "");
- }
-};
-
-class NSSetMisc : public Module
-{
- CommandNSSetMisc commandnssetmisc;
- CommandNSSASetMisc commandnssasetmisc;
- Serialize::Type nsmiscdata_type;
-
- public:
- NSSetMisc(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandnssetmisc(this), commandnssasetmisc(this), nsmiscdata_type("NSMiscData", NSMiscData::Unserialize)
- {
- me = this;
- }
-
- ~NSSetMisc()
- {
- for (Anope::map<ExtensibleItem<NSMiscData> *>::iterator it = items.begin(); it != items.end(); ++it)
- delete it->second;
- }
-
- void OnReload(Configuration::Conf *conf) anope_override
- {
- descriptions.clear();
-
- for (int i = 0; i < conf->CountBlock("command"); ++i)
- {
- Configuration::Block *block = conf->GetBlock("command", i);
-
- const Anope::string &cmd = block->Get<const Anope::string>("command");
-
- if (cmd != "nickserv/set/misc" && cmd != "nickserv/saset/misc")
- continue;
-
- Anope::string cname = block->Get<const Anope::string>("name");
- Anope::string desc = block->Get<const Anope::string>("misc_description");
-
- if (cname.empty() || desc.empty())
- continue;
-
- descriptions[cname] = desc;
- }
- }
-
- void OnNickInfo(CommandSource &source, NickAlias *na, InfoFormatter &info, bool) anope_override
- {
- for (Anope::map<ExtensibleItem<NSMiscData> *>::iterator it = items.begin(); it != items.end(); ++it)
- {
- ExtensibleItem<NSMiscData> *e = it->second;
- NSMiscData *data = e->Get(na->nc);
-
- if (data != NULL)
- info[e->name.substr(12).replace_all_cs("_", " ")] = data->data;
- }
- }
-};
-
-MODULE_INIT(NSSetMisc)
diff --git a/modules/commands/ns_suspend.cpp b/modules/commands/ns_suspend.cpp
deleted file mode 100644
index 76b79d1d1..000000000
--- a/modules/commands/ns_suspend.cpp
+++ /dev/null
@@ -1,288 +0,0 @@
-/* NickServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-#include "modules/suspend.h"
-
-static ServiceReference<NickServService> nickserv("NickServService", "NickServ");
-
-struct NSSuspendInfo : SuspendInfo, Serializable
-{
- NSSuspendInfo(Extensible *) : Serializable("NSSuspendInfo") { }
-
- void Serialize(Serialize::Data &data) const anope_override
- {
- data["nick"] << what;
- data["by"] << by;
- data["reason"] << reason;
- data["time"] << when;
- data["expires"] << expires;
- }
-
- static Serializable* Unserialize(Serializable *obj, Serialize::Data &data)
- {
- Anope::string snick;
- data["nick"] >> snick;
-
- NSSuspendInfo *si;
- if (obj)
- si = anope_dynamic_static_cast<NSSuspendInfo *>(obj);
- else
- {
- NickAlias *na = NickAlias::Find(snick);
- if (!na)
- return NULL;
- si = na->nc->Extend<NSSuspendInfo>("NS_SUSPENDED");
- data["nick"] >> si->what;
- }
-
- data["by"] >> si->by;
- data["reason"] >> si->reason;
- data["time"] >> si->when;
- data["expires"] >> si->expires;
- return si;
- }
-};
-
-class CommandNSSuspend : public Command
-{
- public:
- CommandNSSuspend(Module *creator) : Command(creator, "nickserv/suspend", 2, 3)
- {
- this->SetDesc(_("Suspend a given nick"));
- this->SetSyntax(_("\037nickname\037 [+\037expiry\037] [\037reason\037]"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
-
- const Anope::string &nick = params[0];
- Anope::string expiry = params[1];
- Anope::string reason = params.size() > 2 ? params[2] : "";
- time_t expiry_secs = Config->GetModule(this->owner)->Get<time_t>("suspendexpire");
-
- if (Anope::ReadOnly)
- source.Reply(READ_ONLY_MODE);
-
- if (expiry[0] != '+')
- {
- reason = expiry + " " + reason;
- reason.trim();
- expiry.clear();
- }
- else
- {
- expiry_secs = Anope::DoTime(expiry);
- if (expiry_secs == -1)
- {
- source.Reply(BAD_EXPIRY_TIME);
- return;
- }
- }
-
- NickAlias *na = NickAlias::Find(nick);
- if (!na)
- {
- source.Reply(NICK_X_NOT_REGISTERED, nick.c_str());
- return;
- }
-
- if (Config->GetModule("nickserv")->Get<bool>("secureadmins", "yes") && na->nc->IsServicesOper())
- {
- source.Reply(_("You may not suspend other Services Operators' nicknames."));
- return;
- }
-
- if (na->nc->HasExt("NS_SUSPENDED"))
- {
- source.Reply(_("\002%s\002 is already suspended."), na->nc->display.c_str());
- return;
- }
-
- NickCore *nc = na->nc;
-
- NSSuspendInfo *si = nc->Extend<NSSuspendInfo>("NS_SUSPENDED");
- si->what = nc->display;
- si->by = source.GetNick();
- si->reason = reason;
- si->when = Anope::CurTime;
- si->expires = expiry_secs ? expiry_secs + Anope::CurTime : 0;
-
- for (unsigned i = 0; i < nc->aliases->size(); ++i)
- {
- NickAlias *na2 = nc->aliases->at(i);
-
- if (na2 && *na2->nc == *na->nc)
- {
- na2->last_quit = reason;
-
- User *u2 = User::Find(na2->nick, true);
- if (u2)
- {
- u2->Logout();
- if (nickserv)
- nickserv->Collide(u2, na2);
- }
- }
- }
-
- Log(LOG_ADMIN, source, this) << "for " << nick << " (" << (!reason.empty() ? reason : "No reason") << "), expires on " << (expiry_secs ? Anope::strftime(Anope::CurTime + expiry_secs) : "never");
- source.Reply(_("Nick %s is now suspended."), nick.c_str());
-
- FOREACH_MOD(OnNickSuspend, (na));
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Suspends a registered nickname, which prevents it from being used\n"
- "while keeping all the data for that nick. If an expiry is given\n"
- "the nick will be unsuspended after that period of time, else the\n"
- "default expiry from the configuration is used."));
- return true;
- }
-};
-
-class CommandNSUnSuspend : public Command
-{
- public:
- CommandNSUnSuspend(Module *creator) : Command(creator, "nickserv/unsuspend", 1, 1)
- {
- this->SetDesc(_("Unsuspend a given nick"));
- this->SetSyntax(_("\037nickname\037"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &nick = params[0];
-
- if (Anope::ReadOnly)
- source.Reply(READ_ONLY_MODE);
-
- NickAlias *na = NickAlias::Find(nick);
- if (!na)
- {
- source.Reply(NICK_X_NOT_REGISTERED, nick.c_str());
- return;
- }
-
- if (!na->nc->HasExt("NS_SUSPENDED"))
- {
- source.Reply(_("Nick %s is not suspended."), na->nick.c_str());
- return;
- }
-
- NSSuspendInfo *si = na->nc->GetExt<NSSuspendInfo>("NS_SUSPENDED");
-
- Log(LOG_ADMIN, source, this) << "for " << na->nick << " which was suspended by " << (!si->by.empty() ? si->by : "(none)") << " for: " << (!si->reason.empty() ? si->reason : "No reason");
-
- na->nc->Shrink<NSSuspendInfo>("NS_SUSPENDED");
-
- source.Reply(_("Nick %s is now released."), nick.c_str());
-
- FOREACH_MOD(OnNickUnsuspended, (na));
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Unsuspends a nickname which allows it to be used again."));
- return true;
- }
-};
-
-class NSSuspend : public Module
-{
- CommandNSSuspend commandnssuspend;
- CommandNSUnSuspend commandnsunsuspend;
- ExtensibleItem<NSSuspendInfo> suspend;
- Serialize::Type suspend_type;
- std::vector<Anope::string> show;
-
- struct trim
- {
- Anope::string operator()(Anope::string s) const
- {
- return s.trim();
- }
- };
-
- bool Show(CommandSource &source, const Anope::string &what) const
- {
- return source.IsOper() || std::find(show.begin(), show.end(), what) != show.end();
- }
-
- public:
- NSSuspend(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandnssuspend(this), commandnsunsuspend(this), suspend(this, "NS_SUSPENDED"),
- suspend_type("NSSuspendInfo", NSSuspendInfo::Unserialize)
- {
- }
-
- void OnReload(Configuration::Conf *conf) anope_override
- {
- Anope::string s = conf->GetModule(this)->Get<Anope::string>("show");
- commasepstream(s).GetTokens(show);
- std::transform(show.begin(), show.end(), show.begin(), trim());
- }
-
- void OnNickInfo(CommandSource &source, NickAlias *na, InfoFormatter &info, bool show_hidden) anope_override
- {
- NSSuspendInfo *s = suspend.Get(na->nc);
- if (!s)
- return;
-
- if (show_hidden || Show(source, "suspended"))
- info[_("Suspended")] = _("This nickname is \002suspended\002.");
- if (!s->by.empty() && (show_hidden || Show(source, "by")))
- info[_("Suspended by")] = s->by;
- if (!s->reason.empty() && (show_hidden || Show(source, "reason")))
- info[_("Suspend reason")] = s->reason;
- if (s->when && (show_hidden || Show(source, "on")))
- info[_("Suspended on")] = Anope::strftime(s->when, source.GetAccount());
- if (s->expires && (show_hidden || Show(source, "expires")))
- info[_("Suspension expires")] = Anope::strftime(s->expires, source.GetAccount());
- }
-
- void OnPreNickExpire(NickAlias *na, bool &expire) anope_override
- {
- NSSuspendInfo *s = suspend.Get(na->nc);
- if (!s)
- return;
-
- expire = false;
-
- if (!s->expires)
- return;
-
- if (s->expires < Anope::CurTime)
- {
- na->last_seen = Anope::CurTime;
- suspend.Unset(na->nc);
-
- Log(LOG_NORMAL, "nickserv/expire", Config->GetClient("NickServ")) << "Expiring suspend for " << na->nick;
- }
- }
-
- EventReturn OnNickValidate(User *u, NickAlias *na) anope_override
- {
- NSSuspendInfo *s = suspend.Get(na->nc);
- if (!s)
- return EVENT_CONTINUE;
-
- u->SendMessage(Config->GetClient("NickServ"), NICK_X_SUSPENDED, u->nick.c_str());
- return EVENT_STOP;
- }
-};
-
-MODULE_INIT(NSSuspend)
diff --git a/modules/commands/ns_update.cpp b/modules/commands/ns_update.cpp
deleted file mode 100644
index fcbe02f21..000000000
--- a/modules/commands/ns_update.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-/* NickServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class CommandNSUpdate : public Command
-{
- public:
- CommandNSUpdate(Module *creator) : Command(creator, "nickserv/update", 0, 0)
- {
- this->SetDesc(_("Updates your current status, i.e. it checks for new memos"));
- this->RequireUser(true);
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- User *u = source.GetUser();
- NickAlias *na = NickAlias::Find(u->nick);
-
- if (na && na->nc == source.GetAccount())
- {
- na->last_realname = u->realname;
- na->last_seen = Anope::CurTime;
- }
-
- FOREACH_MOD(OnNickUpdate, (u));
-
- source.Reply(_("Status updated (memos, vhost, chmodes, flags)."));
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Updates your current status, i.e. it checks for new memos,\n"
- "sets needed channel modes and updates your vhost and\n"
- "your userflags (lastseentime, etc)."));
- return true;
- }
-};
-
-class NSUpdate : public Module
-{
- CommandNSUpdate commandnsupdate;
-
- public:
- NSUpdate(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandnsupdate(this)
- {
-
- }
-};
-
-MODULE_INIT(NSUpdate)
diff --git a/modules/commands/os_akill.cpp b/modules/commands/os_akill.cpp
deleted file mode 100644
index 08bbc79ac..000000000
--- a/modules/commands/os_akill.cpp
+++ /dev/null
@@ -1,483 +0,0 @@
-/* OperServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-static ServiceReference<XLineManager> akills("XLineManager", "xlinemanager/sgline");
-
-class AkillDelCallback : public NumberList
-{
- CommandSource &source;
- unsigned deleted;
- Command *cmd;
- public:
- AkillDelCallback(CommandSource &_source, const Anope::string &numlist, Command *c) : NumberList(numlist, true), source(_source), deleted(0), cmd(c)
- {
- }
-
- ~AkillDelCallback()
- {
- if (!deleted)
- source.Reply(_("No matching entries on the AKILL list."));
- else if (deleted == 1)
- source.Reply(_("Deleted 1 entry from the AKILL list."));
- else
- source.Reply(_("Deleted %d entries from the AKILL list."), deleted);
- }
-
- void HandleNumber(unsigned number) anope_override
- {
- if (!number)
- return;
-
- XLine *x = akills->GetEntry(number - 1);
-
- if (!x)
- return;
-
- Log(LOG_ADMIN, source, cmd) << "to remove " << x->mask << " from the list";
-
- ++deleted;
- DoDel(source, x);
- }
-
- static void DoDel(CommandSource &source, XLine *x)
- {
- akills->DelXLine(x);
- }
-};
-
-class CommandOSAKill : public Command
-{
- private:
- void DoAdd(CommandSource &source, const std::vector<Anope::string> &params)
- {
- Anope::string expiry, mask;
-
- if (params.size() < 2)
- {
- this->OnSyntaxError(source, "ADD");
- return;
- }
-
- spacesepstream sep(params[1]);
- sep.GetToken(mask);
-
- if (mask[0] == '+')
- {
- expiry = mask;
- sep.GetToken(mask);
- }
-
- time_t expires = !expiry.empty() ? Anope::DoTime(expiry) : Config->GetModule("operserv")->Get<time_t>("autokillexpiry", "30d");
- /* If the expiry given does not contain a final letter, it's in days,
- * said the doc. Ah well.
- */
- if (!expiry.empty() && isdigit(expiry[expiry.length() - 1]))
- expires *= 86400;
- /* Do not allow less than a minute expiry time */
- if (expires && expires < 60)
- {
- source.Reply(BAD_EXPIRY_TIME);
- return;
- }
- else if (expires > 0)
- expires += Anope::CurTime;
-
- if (sep.StreamEnd())
- {
- this->OnSyntaxError(source, "ADD");
- return;
- }
-
- Anope::string reason;
- if (mask.find('#') != Anope::string::npos)
- {
- Anope::string remaining = sep.GetRemaining();
-
- size_t co = remaining[0] == ':' ? 0 : remaining.rfind(" :");
- if (co == Anope::string::npos)
- {
- this->OnSyntaxError(source, "ADD");
- return;
- }
-
- if (co != 0)
- ++co;
-
- reason = remaining.substr(co + 1);
- mask += " " + remaining.substr(0, co);
- mask.trim();
- }
- else
- reason = sep.GetRemaining();
-
- if (mask[0] == '/' && mask[mask.length() - 1] == '/')
- {
- const Anope::string &regexengine = Config->GetBlock("options")->Get<const Anope::string>("regexengine");
-
- if (regexengine.empty())
- {
- source.Reply(_("Regex is disabled."));
- return;
- }
-
- ServiceReference<RegexProvider> provider("Regex", regexengine);
- if (!provider)
- {
- source.Reply(_("Unable to find regex engine %s."), regexengine.c_str());
- return;
- }
-
- try
- {
- Anope::string stripped_mask = mask.substr(1, mask.length() - 2);
- delete provider->Compile(stripped_mask);
- }
- catch (const RegexException &ex)
- {
- source.Reply("%s", ex.GetReason().c_str());
- return;
- }
- }
-
- User *targ = User::Find(mask, true);
- if (targ)
- mask = "*@" + targ->host;
-
- if (Config->GetModule("operserv")->Get<bool>("addakiller", "yes") && !source.GetNick().empty())
- reason = "[" + source.GetNick() + "] " + reason;
-
- if (mask.find_first_not_of("/~@.*?") == Anope::string::npos)
- {
- source.Reply(USERHOST_MASK_TOO_WIDE, mask.c_str());
- return;
- }
- else if (mask.find('@') == Anope::string::npos)
- {
- source.Reply(BAD_USERHOST_MASK);
- return;
- }
-
- XLine *x = new XLine(mask, source.GetNick(), expires, reason);
- if (Config->GetModule("operserv")->Get<bool>("akillids"))
- x->id = XLineManager::GenerateUID();
-
- unsigned int affected = 0;
- for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it)
- if (akills->Check(it->second, x))
- ++affected;
- float percent = static_cast<float>(affected) / static_cast<float>(UserListByNick.size()) * 100.0;
-
- if (percent > 95)
- {
- source.Reply(USERHOST_MASK_TOO_WIDE, mask.c_str());
- Log(LOG_ADMIN, source, this) << "tried to akill " << percent << "% of the network (" << affected << " users)";
- delete x;
- return;
- }
-
- if (!akills->CanAdd(source, mask, expires, reason))
- return;
-
- EventReturn MOD_RESULT;
- FOREACH_RESULT(OnAddXLine, MOD_RESULT, (source, x, akills));
- if (MOD_RESULT == EVENT_STOP)
- {
- delete x;
- return;
- }
-
- akills->AddXLine(x);
- if (Config->GetModule("operserv")->Get<bool>("akillonadd"))
- akills->Send(NULL, x);
-
- source.Reply(_("\002%s\002 added to the AKILL list."), mask.c_str());
-
- Log(LOG_ADMIN, source, this) << "on " << mask << " (" << x->reason << "), expires in " << (expires ? Anope::Duration(expires - Anope::CurTime) : "never") << " [affects " << affected << " user(s) (" << percent << "%)]";
- if (Anope::ReadOnly)
- source.Reply(READ_ONLY_MODE);
- }
-
- void DoDel(CommandSource &source, const std::vector<Anope::string> &params)
- {
- const Anope::string &mask = params.size() > 1 ? params[1] : "";
-
- if (mask.empty())
- {
- this->OnSyntaxError(source, "DEL");
- return;
- }
-
- if (akills->GetList().empty())
- {
- source.Reply(_("AKILL list is empty."));
- return;
- }
-
- if (isdigit(mask[0]) && mask.find_first_not_of("1234567890,-") == Anope::string::npos)
- {
- AkillDelCallback list(source, mask, this);
- list.Process();
- }
- else
- {
- XLine *x = akills->HasEntry(mask);
-
- if (!x)
- {
- source.Reply(_("\002%s\002 not found on the AKILL list."), mask.c_str());
- return;
- }
-
- do
- {
- FOREACH_MOD(OnDelXLine, (source, x, akills));
-
- Log(LOG_ADMIN, source, this) << "to remove " << x->mask << " from the list";
- source.Reply(_("\002%s\002 deleted from the AKILL list."), x->mask.c_str());
- AkillDelCallback::DoDel(source, x);
- }
- while ((x = akills->HasEntry(mask)));
-
- }
-
- if (Anope::ReadOnly)
- source.Reply(READ_ONLY_MODE);
-
- return;
- }
-
- void ProcessList(CommandSource &source, const std::vector<Anope::string> &params, ListFormatter &list)
- {
- const Anope::string &mask = params.size() > 1 ? params[1] : "";
-
- if (!mask.empty() && isdigit(mask[0]) && mask.find_first_not_of("1234567890,-") == Anope::string::npos)
- {
- class ListCallback : public NumberList
- {
- CommandSource &source;
- ListFormatter &list;
- public:
- ListCallback(CommandSource &_source, ListFormatter &_list, const Anope::string &numstr) : NumberList(numstr, false), source(_source), list(_list)
- {
- }
-
- void HandleNumber(unsigned number) anope_override
- {
- if (!number)
- return;
-
- const XLine *x = akills->GetEntry(number - 1);
-
- if (!x)
- return;
-
- ListFormatter::ListEntry entry;
- entry["Number"] = stringify(number);
- entry["Mask"] = x->mask;
- entry["Creator"] = x->by;
- entry["Created"] = Anope::strftime(x->created, NULL, true);
- entry["Expires"] = Anope::Expires(x->expires, source.nc);
- entry["ID"] = x->id;
- entry["Reason"] = x->reason;
- this->list.AddEntry(entry);
- }
- }
- nl_list(source, list, mask);
- nl_list.Process();
- }
- else
- {
- for (unsigned i = 0, end = akills->GetCount(); i < end; ++i)
- {
- const XLine *x = akills->GetEntry(i);
-
- if (mask.empty() || mask.equals_ci(x->mask) || mask == x->id || Anope::Match(x->mask, mask, false, true))
- {
- ListFormatter::ListEntry entry;
- entry["Number"] = stringify(i + 1);
- entry["Mask"] = x->mask;
- entry["Creator"] = x->by;
- entry["Created"] = Anope::strftime(x->created, NULL, true);
- entry["Expires"] = Anope::Expires(x->expires, source.nc);
- entry["ID"] = x->id;
- entry["Reason"] = x->reason;
- list.AddEntry(entry);
- }
- }
- }
-
- if (list.IsEmpty())
- source.Reply(_("No matching entries on the AKILL list."));
- else
- {
- source.Reply(_("Current AKILL list:"));
-
- std::vector<Anope::string> replies;
- list.Process(replies);
-
- for (unsigned i = 0; i < replies.size(); ++i)
- source.Reply(replies[i]);
-
- source.Reply(_("End of AKILL list."));
- }
- }
-
- void DoList(CommandSource &source, const std::vector<Anope::string> &params)
- {
- if (akills->GetList().empty())
- {
- source.Reply(_("AKILL list is empty."));
- return;
- }
-
- ListFormatter list(source.GetAccount());
- list.AddColumn(_("Number")).AddColumn(_("Mask")).AddColumn(_("Reason"));
-
- this->ProcessList(source, params, list);
- }
-
- void DoView(CommandSource &source, const std::vector<Anope::string> &params)
- {
- if (akills->GetList().empty())
- {
- source.Reply(_("AKILL list is empty."));
- return;
- }
-
- ListFormatter list(source.GetAccount());
- list.AddColumn(_("Number")).AddColumn(_("Mask")).AddColumn(_("Creator")).AddColumn(_("Created")).AddColumn(_("Expires"));
- if (Config->GetModule("operserv")->Get<bool>("akillids"))
- list.AddColumn(_("ID"));
- list.AddColumn(_("Reason"));
-
- this->ProcessList(source, params, list);
- }
-
- void DoClear(CommandSource &source)
- {
-
- for (unsigned i = akills->GetCount(); i > 0; --i)
- {
- XLine *x = akills->GetEntry(i - 1);
- FOREACH_MOD(OnDelXLine, (source, x, akills));
- akills->DelXLine(x);
- }
-
- Log(LOG_ADMIN, source, this) << "to CLEAR the list";
- source.Reply(_("The AKILL list has been cleared."));
-
- if (Anope::ReadOnly)
- source.Reply(READ_ONLY_MODE);
- }
- public:
- CommandOSAKill(Module *creator) : Command(creator, "operserv/akill", 1, 2)
- {
- this->SetDesc(_("Manipulate the AKILL list"));
- this->SetSyntax(_("ADD [+\037expiry\037] \037mask\037 \037reason\037"));
- this->SetSyntax(_("DEL {\037mask\037 | \037entry-num\037 | \037list\037 | \037id\037}"));
- this->SetSyntax(_("LIST [\037mask\037 | \037list\037 | \037id\037]"));
- this->SetSyntax(_("VIEW [\037mask\037 | \037list\037 | \037id\037]"));
- this->SetSyntax("CLEAR");
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &cmd = params[0];
-
- if (!akills)
- return;
-
- if (cmd.equals_ci("ADD"))
- return this->DoAdd(source, params);
- else if (cmd.equals_ci("DEL"))
- return this->DoDel(source, params);
- else if (cmd.equals_ci("LIST"))
- return this->DoList(source, params);
- else if (cmd.equals_ci("VIEW"))
- return this->DoView(source, params);
- else if (cmd.equals_ci("CLEAR"))
- return this->DoClear(source);
- else
- this->OnSyntaxError(source, "");
-
- return;
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Allows Services Operators to manipulate the AKILL list. If\n"
- "a user matching an AKILL mask attempts to connect, Services\n"
- "will issue a KILL for that user and, on supported server\n"
- "types, will instruct all servers to add a ban for the mask\n"
- "which the user matched.\n"
- " \n"
- "\002AKILL ADD\002 adds the given mask to the AKILL\n"
- "list for the given reason, which \002must\002 be given.\n"
- "Mask should be in the format of nick!user@host#real name,\n"
- "though all that is required is user@host. If a real name is specified,\n"
- "the reason must be prepended with a :.\n"
- "\037expiry\037 is specified as an integer followed by one of \037d\037\n"
- "(days), \037h\037 (hours), or \037m\037 (minutes). Combinations (such as\n"
- "\0371h30m\037) are not permitted. If a unit specifier is not\n"
- "included, the default is days (so \037+30\037 by itself means 30\n"
- "days). To add an AKILL which does not expire, use \037+0\037. If the\n"
- "usermask to be added starts with a \037+\037, an expiry time must\n"
- "be given, even if it is the same as the default. The\n"
- "current AKILL default expiry time can be found with the\n"
- "\002STATS AKILL\002 command."));
- const Anope::string &regexengine = Config->GetBlock("options")->Get<const Anope::string>("regexengine");
- if (!regexengine.empty())
- {
- source.Reply(" ");
- source.Reply(_("Regex matches are also supported using the %s engine.\n"
- "Enclose your mask in // if this is desired."), regexengine.c_str());
- }
- source.Reply(_(
- " \n"
- "The \002AKILL DEL\002 command removes the given mask from the\n"
- "AKILL list if it is present. If a list of entry numbers is\n"
- "given, those entries are deleted. (See the example for LIST\n"
- "below.)\n"
- " \n"
- "The \002AKILL LIST\002 command displays the AKILL list.\n"
- "If a wildcard mask is given, only those entries matching the\n"
- "mask are displayed. If a list of entry numbers is given,\n"
- "only those entries are shown; for example:\n"
- " \002AKILL LIST 2-5,7-9\002\n"
- " Lists AKILL entries numbered 2 through 5 and 7\n"
- " through 9.\n"
- " \n"
- "\002AKILL VIEW\002 is a more verbose version of \002AKILL LIST\002, and\n"
- "will show who added an AKILL, the date it was added, and when\n"
- "it expires, as well as the user@host/ip mask and reason.\n"
- " \n"
- "\002AKILL CLEAR\002 clears all entries of the AKILL list."));
- return true;
- }
-};
-
-class OSAKill : public Module
-{
- CommandOSAKill commandosakill;
-
- public:
- OSAKill(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandosakill(this)
- {
-
- }
-};
-
-MODULE_INIT(OSAKill)
diff --git a/modules/commands/os_chankill.cpp b/modules/commands/os_chankill.cpp
deleted file mode 100644
index 56b5f6b33..000000000
--- a/modules/commands/os_chankill.cpp
+++ /dev/null
@@ -1,119 +0,0 @@
-/* OperServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-static ServiceReference<XLineManager> akills("XLineManager", "xlinemanager/sgline");
-
-class CommandOSChanKill : public Command
-{
- public:
- CommandOSChanKill(Module *creator) : Command(creator, "operserv/chankill", 2, 3)
- {
- this->SetDesc(_("AKILL all users on a specific channel"));
- this->SetSyntax(_("[+\037expiry\037] \037channel\037 \037reason\037"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (!akills)
- return;
-
- Anope::string expiry, channel;
- unsigned last_param = 1;
- Channel *c;
-
- channel = params[0];
- if (!channel.empty() && channel[0] == '+')
- {
- expiry = channel;
- channel = params[1];
- last_param = 2;
- }
-
- time_t expires = !expiry.empty() ? Anope::DoTime(expiry) : Config->GetModule("operserv")->Get<time_t>("autokillexpiry", "30d");
- if (!expiry.empty() && isdigit(expiry[expiry.length() - 1]))
- expires *= 86400;
- if (expires && expires < 60)
- {
- source.Reply(BAD_EXPIRY_TIME);
- return;
- }
- else if (expires > 0)
- expires += Anope::CurTime;
-
- if (params.size() <= last_param)
- {
- this->OnSyntaxError(source, "");
- return;
- }
-
- Anope::string reason = params[last_param];
- if (params.size() > last_param + 1)
- reason += params[last_param + 1];
- if (!reason.empty())
- {
- Anope::string realreason;
- if (Config->GetModule("operserv")->Get<bool>("addakiller") && !source.GetNick().empty())
- realreason = "[" + source.GetNick() + "] " + reason;
- else
- realreason = reason;
-
- if ((c = Channel::Find(channel)))
- {
- for (Channel::ChanUserList::iterator it = c->users.begin(), it_end = c->users.end(); it != it_end; ++it)
- {
- ChanUserContainer *uc = it->second;
-
- if (uc->user->server == Me || uc->user->HasMode("OPER"))
- continue;
-
- Anope::string akillmask = "*@" + uc->user->host;
- if (akills->HasEntry(akillmask))
- continue;
-
- XLine *x = new XLine(akillmask, source.GetNick(), expires, realreason, XLineManager::GenerateUID());
- akills->AddXLine(x);
- akills->OnMatch(uc->user, x);
- }
-
- Log(LOG_ADMIN, source, this) << "on " << c->name << " (" << realreason << ")";
- }
- else
- source.Reply(CHAN_X_NOT_IN_USE, channel.c_str());
- }
- return;
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Puts an AKILL for every nick on the specified channel. It\n"
- "uses the entire real ident@host for every nick, and\n"
- "then enforces the AKILL."));
- return true;
- }
-};
-
-class OSChanKill : public Module
-{
- CommandOSChanKill commandoschankill;
-
- public:
- OSChanKill(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandoschankill(this)
- {
-
- }
-};
-
-MODULE_INIT(OSChanKill)
diff --git a/modules/commands/os_dns.cpp b/modules/commands/os_dns.cpp
deleted file mode 100644
index 687f44f4a..000000000
--- a/modules/commands/os_dns.cpp
+++ /dev/null
@@ -1,949 +0,0 @@
-/*
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- */
-
-#include "module.h"
-#include "modules/dns.h"
-
-static ServiceReference<DNS::Manager> dnsmanager("DNS::Manager", "dns/manager");
-
-struct DNSZone;
-class DNSServer;
-
-static Serialize::Checker<std::vector<DNSZone *> > zones("DNSZone");
-static Serialize::Checker<std::vector<DNSServer *> > dns_servers("DNSServer");
-
-static std::map<Anope::string, std::list<time_t> > server_quit_times;
-
-struct DNSZone : Serializable
-{
- Anope::string name;
- std::set<Anope::string, ci::less> servers;
-
- DNSZone(const Anope::string &n) : Serializable("DNSZone"), name(n)
- {
- zones->push_back(this);
- }
-
- ~DNSZone()
- {
- std::vector<DNSZone *>::iterator it = std::find(zones->begin(), zones->end(), this);
- if (it != zones->end())
- zones->erase(it);
- }
-
- void Serialize(Serialize::Data &data) const anope_override
- {
- data["name"] << name;
- unsigned count = 0;
- for (std::set<Anope::string, ci::less>::iterator it = servers.begin(), it_end = servers.end(); it != it_end; ++it)
- data["server" + stringify(count++)] << *it;
- }
-
- static Serializable* Unserialize(Serializable *obj, Serialize::Data &data)
- {
- DNSZone *zone;
- Anope::string zone_name;
-
- data["name"] >> zone_name;
-
- if (obj)
- {
- zone = anope_dynamic_static_cast<DNSZone *>(obj);
- data["name"] >> zone->name;
- }
- else
- zone = new DNSZone(zone_name);
-
- zone->servers.clear();
- for (unsigned count = 0; true; ++count)
- {
- Anope::string server_str;
- data["server" + stringify(count)] >> server_str;
- if (server_str.empty())
- break;
- zone->servers.insert(server_str);
- }
-
- return zone;
- }
-
- static DNSZone *Find(const Anope::string &name)
- {
- for (unsigned i = 0; i < zones->size(); ++i)
- if (zones->at(i)->name.equals_ci(name))
- {
- DNSZone *z = zones->at(i);
- z->QueueUpdate();
- return z;
- }
- return NULL;
- }
-};
-
-class DNSServer : public Serializable
-{
- Anope::string server_name;
- std::vector<Anope::string> ips;
- unsigned limit;
- /* wants to be in the pool */
- bool pooled;
- /* is actually in the pool */
- bool active;
-
- public:
- std::set<Anope::string, ci::less> zones;
- time_t repool;
-
- DNSServer(const Anope::string &sn) : Serializable("DNSServer"), server_name(sn), limit(0), pooled(false), active(false), repool(0)
- {
- dns_servers->push_back(this);
- }
-
- ~DNSServer()
- {
- std::vector<DNSServer *>::iterator it = std::find(dns_servers->begin(), dns_servers->end(), this);
- if (it != dns_servers->end())
- dns_servers->erase(it);
- }
-
- const Anope::string &GetName() const { return server_name; }
- std::vector<Anope::string> &GetIPs() { return ips; }
- unsigned GetLimit() const { return limit; }
- void SetLimit(unsigned l) { limit = l; }
-
- bool Pooled() const { return pooled; }
- void Pool(bool p)
- {
- if (!p)
- this->SetActive(p);
- pooled = p;
- }
-
- bool Active() const { return pooled && active; }
- void SetActive(bool p)
- {
- if (p)
- this->Pool(p);
- active = p;
-
- if (dnsmanager)
- {
- dnsmanager->UpdateSerial();
- for (std::set<Anope::string, ci::less>::iterator it = zones.begin(), it_end = zones.end(); it != it_end; ++it)
- dnsmanager->Notify(*it);
- }
- }
-
- void Serialize(Serialize::Data &data) const anope_override
- {
- data["server_name"] << server_name;
- for (unsigned i = 0; i < ips.size(); ++i)
- data["ip" + stringify(i)] << ips[i];
- data["limit"] << limit;
- data["pooled"] << pooled;
- unsigned count = 0;
- for (std::set<Anope::string, ci::less>::iterator it = zones.begin(), it_end = zones.end(); it != it_end; ++it)
- data["zone" + stringify(count++)] << *it;
- }
-
- static Serializable* Unserialize(Serializable *obj, Serialize::Data &data)
- {
- DNSServer *req;
- Anope::string server_name;
-
- data["server_name"] >> server_name;
-
- if (obj)
- {
- req = anope_dynamic_static_cast<DNSServer *>(obj);
- req->server_name = server_name;
- }
- else
- req = new DNSServer(server_name);
-
- for (unsigned i = 0; true; ++i)
- {
- Anope::string ip_str;
- data["ip" + stringify(i)] >> ip_str;
- if (ip_str.empty())
- break;
- req->ips.push_back(ip_str);
- }
-
- data["limit"] >> req->limit;
- data["pooled"] >> req->pooled;
-
- req->zones.clear();
- for (unsigned i = 0; true; ++i)
- {
- Anope::string zone_str;
- data["zone" + stringify(i)] >> zone_str;
- if (zone_str.empty())
- break;
- req->zones.insert(zone_str);
- }
-
- return req;
- }
-
- static DNSServer *Find(const Anope::string &s)
- {
- for (unsigned i = 0; i < dns_servers->size(); ++i)
- if (dns_servers->at(i)->GetName().equals_ci(s))
- {
- DNSServer *serv = dns_servers->at(i);
- serv->QueueUpdate();
- return serv;
- }
- return NULL;
- }
-};
-
-class CommandOSDNS : public Command
-{
- void DisplayPoolState(CommandSource &source)
- {
- if (dns_servers->empty())
- {
- source.Reply(_("There are no configured servers."));
- return;
- }
-
- ListFormatter lf(source.GetAccount());
- lf.AddColumn(_("Server")).AddColumn(_("IP")).AddColumn(_("Limit")).AddColumn(_("State"));
- for (unsigned i = 0; i < dns_servers->size(); ++i)
- {
- DNSServer *s = dns_servers->at(i);
- Server *srv = Server::Find(s->GetName(), true);
-
- ListFormatter::ListEntry entry;
- entry["Server"] = s->GetName();
- entry["Limit"] = s->GetLimit() ? stringify(s->GetLimit()) : Language::Translate(source.GetAccount(), _("None"));
-
- Anope::string ip_str;
- for (unsigned j = 0; j < s->GetIPs().size(); ++j)
- ip_str += s->GetIPs()[j] + " ";
- ip_str.trim();
- if (ip_str.empty())
- ip_str = "None";
- entry["IP"] = ip_str;
-
- if (s->Active())
- entry["State"] = Language::Translate(source.GetAccount(), _("Pooled/Active"));
- else if (s->Pooled())
- entry["State"] = Language::Translate(source.GetAccount(), _("Pooled/Not Active"));
- else
- entry["State"] = Language::Translate(source.GetAccount(), _("Unpooled"));
-
- if (!srv)
- entry["State"] += Anope::string(" ") + Language::Translate(source.GetAccount(), _("(Split)"));
-
- lf.AddEntry(entry);
- }
-
- std::vector<Anope::string> replies;
- lf.Process(replies);
-
- if (!zones->empty())
- {
- ListFormatter lf2(source.GetAccount());
- lf2.AddColumn(_("Zone")).AddColumn(_("Servers"));
-
- for (unsigned i = 0; i < zones->size(); ++i)
- {
- const DNSZone *z = zones->at(i);
-
- ListFormatter::ListEntry entry;
- entry["Zone"] = z->name;
-
- Anope::string server_str;
- for (std::set<Anope::string, ci::less>::iterator it = z->servers.begin(), it_end = z->servers.end(); it != it_end; ++it)
- server_str += *it + " ";
- server_str.trim();
-
- if (server_str.empty())
- server_str = "None";
-
- entry["Servers"] = server_str;
-
- lf2.AddEntry(entry);
- }
-
- lf2.Process(replies);
- }
-
- for (unsigned i = 0; i < replies.size(); ++i)
- source.Reply(replies[i]);
- }
-
- void AddZone(CommandSource &source, const std::vector<Anope::string> &params)
- {
- const Anope::string &zone = params[1];
-
- if (DNSZone::Find(zone))
- {
- source.Reply(_("Zone %s already exists."), zone.c_str());
- return;
- }
-
- if (Anope::ReadOnly)
- source.Reply(READ_ONLY_MODE);
-
- Log(LOG_ADMIN, source, this) << "to add zone " << zone;
-
- new DNSZone(zone);
- source.Reply(_("Added zone %s."), zone.c_str());
- }
-
- void DelZone(CommandSource &source, const std::vector<Anope::string> &params)
- {
- const Anope::string &zone = params[1];
-
- DNSZone *z = DNSZone::Find(zone);
- if (!z)
- {
- source.Reply(_("Zone %s does not exist."), zone.c_str());
- return;
- }
-
- if (Anope::ReadOnly)
- source.Reply(READ_ONLY_MODE);
-
- Log(LOG_ADMIN, source, this) << "to delete zone " << z->name;
-
- for (std::set<Anope::string, ci::less>::iterator it = z->servers.begin(), it_end = z->servers.end(); it != it_end; ++it)
- {
- DNSServer *s = DNSServer::Find(*it);
- if (s)
- s->zones.erase(z->name);
- }
-
- if (dnsmanager)
- {
- dnsmanager->UpdateSerial();
- dnsmanager->Notify(z->name);
- }
-
- source.Reply(_("Zone %s removed."), z->name.c_str());
- delete z;
- }
-
- void AddServer(CommandSource &source, const std::vector<Anope::string> &params)
- {
- DNSServer *s = DNSServer::Find(params[1]);
- const Anope::string &zone = params.size() > 2 ? params[2] : "";
-
- if (s)
- {
- if (zone.empty())
- {
- source.Reply(_("Server %s already exists."), s->GetName().c_str());
- }
- else
- {
- DNSZone *z = DNSZone::Find(zone);
- if (!z)
- {
- source.Reply(_("Zone %s does not exist."), zone.c_str());
- return;
- }
- else if (z->servers.count(s->GetName()))
- {
- source.Reply(_("Server %s is already in zone %s."), s->GetName().c_str(), z->name.c_str());
- return;
- }
-
- if (Anope::ReadOnly)
- source.Reply(READ_ONLY_MODE);
-
- z->servers.insert(s->GetName());
- s->zones.insert(zone);
-
- if (dnsmanager)
- {
- dnsmanager->UpdateSerial();
- dnsmanager->Notify(zone);
- }
-
- Log(LOG_ADMIN, source, this) << "to add server " << s->GetName() << " to zone " << z->name;
-
- source.Reply(_("Server %s added to zone %s."), s->GetName().c_str(), z->name.c_str());
- }
-
- return;
- }
-
- Server *serv = Server::Find(params[1], true);
- if (!serv || serv == Me || serv->IsJuped())
- {
- source.Reply(_("Server %s is not linked to the network."), params[1].c_str());
- return;
- }
-
- s = new DNSServer(params[1]);
- if (zone.empty())
- {
- if (Anope::ReadOnly)
- source.Reply(READ_ONLY_MODE);
-
- Log(LOG_ADMIN, source, this) << "to add server " << s->GetName();
- source.Reply(_("Added server %s."), s->GetName().c_str());
- }
- else
- {
- DNSZone *z = DNSZone::Find(zone);
- if (!z)
- {
- source.Reply(_("Zone %s does not exist."), zone.c_str());
- delete s;
- return;
- }
-
- if (Anope::ReadOnly)
- source.Reply(READ_ONLY_MODE);
-
- Log(LOG_ADMIN, source, this) << "to add server " << s->GetName() << " to zone " << zone;
-
- z->servers.insert(s->GetName());
- s->zones.insert(z->name);
-
- if (dnsmanager)
- {
- dnsmanager->UpdateSerial();
- dnsmanager->Notify(z->name);
- }
- }
- }
-
- void DelServer(CommandSource &source, const std::vector<Anope::string> &params)
- {
- DNSServer *s = DNSServer::Find(params[1]);
- const Anope::string &zone = params.size() > 2 ? params[2] : "";
-
- if (!s)
- {
- source.Reply(_("Server %s does not exist."), params[1].c_str());
- return;
- }
- else if (!zone.empty())
- {
- DNSZone *z = DNSZone::Find(zone);
- if (!z)
- {
- source.Reply(_("Zone %s does not exist."), zone.c_str());
- return;
- }
- else if (!z->servers.count(s->GetName()))
- {
- source.Reply(_("Server %s is not in zone %s."), s->GetName().c_str(), z->name.c_str());
- return;
- }
-
- if (Anope::ReadOnly)
- source.Reply(READ_ONLY_MODE);
-
- Log(LOG_ADMIN, source, this) << "to remove server " << s->GetName() << " from zone " << z->name;
-
- if (dnsmanager)
- {
- dnsmanager->UpdateSerial();
- dnsmanager->Notify(z->name);
- }
-
- z->servers.erase(s->GetName());
- s->zones.erase(z->name);
- source.Reply(_("Removed server %s from zone %s."), s->GetName().c_str(), z->name.c_str());
- return;
- }
- else if (Server::Find(s->GetName(), true))
- {
- source.Reply(_("Server %s must be quit before it can be deleted."), s->GetName().c_str());
- return;
- }
-
- for (std::set<Anope::string, ci::less>::iterator it = s->zones.begin(), it_end = s->zones.end(); it != it_end; ++it)
- {
- DNSZone *z = DNSZone::Find(*it);
- if (z)
- z->servers.erase(s->GetName());
- }
-
- if (Anope::ReadOnly)
- source.Reply(READ_ONLY_MODE);
-
- if (dnsmanager)
- dnsmanager->UpdateSerial();
-
- Log(LOG_ADMIN, source, this) << "to delete server " << s->GetName();
- source.Reply(_("Removed server %s."), s->GetName().c_str());
- delete s;
- }
-
- void AddIP(CommandSource &source, const std::vector<Anope::string> &params)
- {
- DNSServer *s = DNSServer::Find(params[1]);
-
- if (!s)
- {
- source.Reply(_("Server %s does not exist."), params[1].c_str());
- return;
- }
-
- for (unsigned i = 0; i < s->GetIPs().size(); ++i)
- if (params[2].equals_ci(s->GetIPs()[i]))
- {
- source.Reply(_("IP %s already exists for %s."), s->GetIPs()[i].c_str(), s->GetName().c_str());
- return;
- }
-
- sockaddrs addr(params[2]);
- if (!addr.valid())
- {
- source.Reply(_("%s is not a valid IP address."), params[2].c_str());
- return;
- }
-
- if (Anope::ReadOnly)
- source.Reply(READ_ONLY_MODE);
-
- s->GetIPs().push_back(params[2]);
- source.Reply(_("Added IP %s to %s."), params[2].c_str(), s->GetName().c_str());
- Log(LOG_ADMIN, source, this) << "to add IP " << params[2] << " to " << s->GetName();
-
- if (s->Active() && dnsmanager)
- {
- dnsmanager->UpdateSerial();
- for (std::set<Anope::string, ci::less>::iterator it = s->zones.begin(), it_end = s->zones.end(); it != it_end; ++it)
- dnsmanager->Notify(*it);
- }
- }
-
- void DelIP(CommandSource &source, const std::vector<Anope::string> &params)
- {
- DNSServer *s = DNSServer::Find(params[1]);
-
- if (!s)
- {
- source.Reply(_("Server %s does not exist."), params[1].c_str());
- return;
- }
-
- if (Anope::ReadOnly)
- source.Reply(READ_ONLY_MODE);
-
- for (unsigned i = 0; i < s->GetIPs().size(); ++i)
- if (params[2].equals_ci(s->GetIPs()[i]))
- {
- s->GetIPs().erase(s->GetIPs().begin() + i);
- source.Reply(_("Removed IP %s from %s."), params[2].c_str(), s->GetName().c_str());
- Log(LOG_ADMIN, source, this) << "to remove IP " << params[2] << " from " << s->GetName();
-
- if (s->GetIPs().empty())
- {
- s->repool = 0;
- s->Pool(false);
- }
-
- if (s->Active() && dnsmanager)
- {
- dnsmanager->UpdateSerial();
- for (std::set<Anope::string, ci::less>::iterator it = s->zones.begin(), it_end = s->zones.end(); it != it_end; ++it)
- dnsmanager->Notify(*it);
- }
-
- return;
- }
-
- source.Reply(_("IP %s does not exist for %s."), params[2].c_str(), s->GetName().c_str());
- }
-
- void OnSet(CommandSource &source, const std::vector<Anope::string> &params)
- {
- DNSServer *s = DNSServer::Find(params[1]);
-
- if (!s)
- {
- source.Reply(_("Server %s does not exist."), params[1].c_str());
- return;
- }
-
- if (Anope::ReadOnly)
- source.Reply(READ_ONLY_MODE);
-
- if (params[2].equals_ci("LIMIT"))
- {
- try
- {
- unsigned l = convertTo<unsigned>(params[3]);
- s->SetLimit(l);
- if (l)
- source.Reply(_("User limit for %s set to %d."), s->GetName().c_str(), l);
- else
- source.Reply(_("User limit for %s removed."), s->GetName().c_str());
- }
- catch (const ConvertException &ex)
- {
- source.Reply(_("Invalid value for LIMIT. Must be numerical."));
- }
- }
- else
- source.Reply(_("Unknown SET option."));
- }
-
- void OnPool(CommandSource &source, const std::vector<Anope::string> &params)
- {
- DNSServer *s = DNSServer::Find(params[1]);
-
- if (!s)
- {
- source.Reply(_("Server %s does not exist."), params[1].c_str());
- return;
- }
- else if (!Server::Find(s->GetName(), true))
- {
- source.Reply(_("Server %s is not currently linked."), s->GetName().c_str());
- return;
- }
- else if (s->Pooled())
- {
- source.Reply(_("Server %s is already pooled."), s->GetName().c_str());
- return;
- }
- else if (s->GetIPs().empty())
- {
- source.Reply(_("Server %s has no configured IPs."), s->GetName().c_str());
- return;
- }
-
- if (Anope::ReadOnly)
- source.Reply(READ_ONLY_MODE);
-
- s->SetActive(true);
-
- source.Reply(_("Pooled %s."), s->GetName().c_str());
- Log(LOG_ADMIN, source, this) << "to pool " << s->GetName();
- }
-
-
- void OnDepool(CommandSource &source, const std::vector<Anope::string> &params)
- {
- DNSServer *s = DNSServer::Find(params[1]);
-
- if (!s)
- {
- source.Reply(_("Server %s does not exist."), params[1].c_str());
- return;
- }
- else if (!s->Pooled())
- {
- source.Reply(_("Server %s is not pooled."), s->GetName().c_str());
- return;
- }
-
- if (Anope::ReadOnly)
- source.Reply(READ_ONLY_MODE);
-
- s->Pool(false);
-
- source.Reply(_("Depooled %s."), s->GetName().c_str());
- Log(LOG_ADMIN, source, this) << "to depool " << s->GetName();
- }
-
- public:
- CommandOSDNS(Module *creator) : Command(creator, "operserv/dns", 0, 4)
- {
- this->SetDesc(_("Manage DNS zones for this network"));
- this->SetSyntax(_("ADDZONE \037zone.name\037"));
- this->SetSyntax(_("DELZONE \037zone.name\037"));
- this->SetSyntax(_("ADDSERVER \037server.name\037 [\037zone.name\037]"));
- this->SetSyntax(_("DELSERVER \037server.name\037 [\037zone.name\037]"));
- this->SetSyntax(_("ADDIP \037server.name\037 \037ip\037"));
- this->SetSyntax(_("DELIP \037server.name\037 \037ip\037"));
- this->SetSyntax(_("SET \037server.name\037 \037option\037 \037value\037"));
- this->SetSyntax(_("POOL \037server.name\037"));
- this->SetSyntax(_("DEPOOL \037server.name\037"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (params.empty())
- this->DisplayPoolState(source);
- else if (params[0].equals_ci("ADDZONE") && params.size() > 1)
- this->AddZone(source, params);
- else if (params[0].equals_ci("DELZONE") && params.size() > 1)
- this->DelZone(source, params);
- else if (params[0].equals_ci("ADDSERVER") && params.size() > 1)
- this->AddServer(source, params);
- else if (params[0].equals_ci("DELSERVER") && params.size() > 1)
- this->DelServer(source, params);
- else if (params[0].equals_ci("ADDIP") && params.size() > 2)
- this->AddIP(source, params);
- else if (params[0].equals_ci("DELIP") && params.size() > 2)
- this->DelIP(source, params);
- else if (params[0].equals_ci("SET") && params.size() > 3)
- this->OnSet(source, params);
- else if (params[0].equals_ci("POOL") && params.size() > 1)
- this->OnPool(source, params);
- else if (params[0].equals_ci("DEPOOL") && params.size() > 1)
- this->OnDepool(source, params);
- else
- this->OnSyntaxError(source, "");
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("This command allows managing DNS zones used for controlling what servers users\n"
- "are directed to when connecting. Omitting all parameters prints out the status of\n"
- "the DNS zone.\n"
- " \n"
- "\002ADDZONE\002 adds a zone, eg us.yournetwork.tld. Servers can then be added to this\n"
- "zone with the \002ADDSERVER\002 command.\n"
- " \n"
- "The \002ADDSERVER\002 command adds a server to the given zone. When a query is done, the\n"
- "zone in question is served if it exists, else all servers in all zones are served.\n"
- "A server may be in more than one zone.\n"
- " \n"
- "The \002ADDIP\002 command associates an IP with a server.\n"
- " \n"
- "The \002POOL\002 and \002DEPOOL\002 commands actually add and remove servers to their given zones."));
- return true;
- }
-};
-
-class ModuleDNS : public Module
-{
- Serialize::Type zone_type, dns_type;
- CommandOSDNS commandosdns;
-
- time_t ttl;
- int user_drop_mark;
- time_t user_drop_time;
- time_t user_drop_readd_time;
- bool remove_split_servers;
- bool readd_connected_servers;
-
- time_t last_warn;
-
- public:
- ModuleDNS(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, EXTRA | VENDOR),
- zone_type("DNSZone", DNSZone::Unserialize), dns_type("DNSServer", DNSServer::Unserialize), commandosdns(this),
- last_warn(0)
- {
-
-
- for (unsigned j = 0; j < dns_servers->size(); ++j)
- {
- DNSServer *s = dns_servers->at(j);
- if (s->Pooled() && Server::Find(s->GetName(), true))
- s->SetActive(true);
- }
- }
-
- ~ModuleDNS()
- {
- for (unsigned i = zones->size(); i > 0; --i)
- delete zones->at(i - 1);
- for (unsigned i = dns_servers->size(); i > 0; --i)
- delete dns_servers->at(i - 1);
- }
-
- void OnReload(Configuration::Conf *conf) anope_override
- {
- Configuration::Block *block = conf->GetModule(this);
- this->ttl = block->Get<time_t>("ttl");
- this->user_drop_mark = block->Get<int>("user_drop_mark");
- this->user_drop_time = block->Get<time_t>("user_drop_time");
- this->user_drop_readd_time = block->Get<time_t>("user_drop_readd_time");
- this->remove_split_servers = block->Get<bool>("remove_split_servers");
- this->readd_connected_servers = block->Get<bool>("readd_connected_servers");
- }
-
- void OnNewServer(Server *s) anope_override
- {
- if (s == Me || s->IsJuped())
- return;
- if (!Me->IsSynced() || this->readd_connected_servers)
- {
- DNSServer *dns = DNSServer::Find(s->GetName());
- if (dns && dns->Pooled() && !dns->Active() && !dns->GetIPs().empty())
- {
- dns->SetActive(true);
- Log(this) << "Pooling server " << s->GetName();
- }
- }
- }
-
- void OnServerQuit(Server *s) anope_override
- {
- DNSServer *dns = DNSServer::Find(s->GetName());
- if (remove_split_servers && dns && dns->Pooled() && dns->Active())
- {
- if (readd_connected_servers)
- dns->SetActive(false); // Will be reactivated when it comes back
- else
- dns->Pool(false); // Otherwise permanently pull this
- Log(this) << "Depooling delinked server " << s->GetName();
- }
- }
-
- void OnUserConnect(User *u, bool &exempt) anope_override
- {
- if (!u->Quitting() && u->server)
- {
- DNSServer *s = DNSServer::Find(u->server->GetName());
- /* Check for user limit reached */
- if (s && s->Pooled() && s->Active() && s->GetLimit() && u->server->users >= s->GetLimit())
- {
- Log(this) << "Depooling full server " << s->GetName() << ": " << u->server->users << " users";
- s->SetActive(false);
- }
- }
- }
-
- void OnPreUserLogoff(User *u) anope_override
- {
- if (u && u->server)
- {
- DNSServer *s = DNSServer::Find(u->server->GetName());
- if (!s || !s->Pooled())
- return;
-
- /* Check for dropping under userlimit */
- if (s->GetLimit() && !s->Active() && s->GetLimit() > u->server->users)
- {
- Log(this) << "Pooling server " << s->GetName();
- s->SetActive(true);
- }
-
- if (this->user_drop_mark > 0)
- {
- std::list<time_t>& times = server_quit_times[u->server->GetName()];
- times.push_back(Anope::CurTime);
- if (times.size() > static_cast<unsigned>(this->user_drop_mark))
- times.pop_front();
-
- if (times.size() == static_cast<unsigned>(this->user_drop_mark))
- {
- time_t diff = Anope::CurTime - *times.begin();
-
- /* Check for very fast user drops */
- if (s->Active() && diff <= this->user_drop_time)
- {
- Log(this) << "Depooling server " << s->GetName() << ": dropped " << this->user_drop_mark << " users in " << diff << " seconds";
- s->repool = Anope::CurTime + this->user_drop_readd_time;
- s->SetActive(false);
- }
- /* Check for needing to re-pool a server that dropped users */
- else if (!s->Active() && s->repool && s->repool <= Anope::CurTime)
- {
- s->SetActive(true);
- s->repool = 0;
- Log(this) << "Pooling server " << s->GetName();
- }
- }
- }
- }
- }
-
- void OnDnsRequest(DNS::Query &req, DNS::Query *packet) anope_override
- {
- if (req.questions.empty())
- return;
- /* Currently we reply to any QR for A/AAAA */
- const DNS::Question& q = req.questions[0];
- if (q.type != DNS::QUERY_A && q.type != DNS::QUERY_AAAA && q.type != DNS::QUERY_AXFR && q.type != DNS::QUERY_ANY)
- return;
-
- DNSZone *zone = DNSZone::Find(q.name);
- size_t answer_size = packet->answers.size();
- if (zone)
- {
- for (std::set<Anope::string, ci::less>::iterator it = zone->servers.begin(), it_end = zone->servers.end(); it != it_end; ++it)
- {
- DNSServer *s = DNSServer::Find(*it);
- if (!s || !s->Active())
- continue;
-
- for (unsigned j = 0; j < s->GetIPs().size(); ++j)
- {
- DNS::QueryType q_type = s->GetIPs()[j].find(':') != Anope::string::npos ? DNS::QUERY_AAAA : DNS::QUERY_A;
-
- if (q.type == DNS::QUERY_AXFR || q.type == DNS::QUERY_ANY || q_type == q.type)
- {
- DNS::ResourceRecord rr(q.name, q_type);
- rr.ttl = this->ttl;
- rr.rdata = s->GetIPs()[j];
- packet->answers.push_back(rr);
- }
- }
- }
- }
-
- if (packet->answers.size() == answer_size)
- {
- /* Default zone */
- for (unsigned i = 0; i < dns_servers->size(); ++i)
- {
- DNSServer *s = dns_servers->at(i);
- if (!s->Active())
- continue;
-
- for (unsigned j = 0; j < s->GetIPs().size(); ++j)
- {
- DNS::QueryType q_type = s->GetIPs()[j].find(':') != Anope::string::npos ? DNS::QUERY_AAAA : DNS::QUERY_A;
-
- if (q.type == DNS::QUERY_AXFR || q.type == DNS::QUERY_ANY || q_type == q.type)
- {
- DNS::ResourceRecord rr(q.name, q_type);
- rr.ttl = this->ttl;
- rr.rdata = s->GetIPs()[j];
- packet->answers.push_back(rr);
- }
- }
- }
- }
-
- if (packet->answers.size() == answer_size)
- {
- if (last_warn + 60 < Anope::CurTime)
- {
- last_warn = Anope::CurTime;
- Log(this) << "Warning! There are no pooled servers!";
- }
-
- /* Something messed up, just return them all and hope one is available */
- for (unsigned i = 0; i < dns_servers->size(); ++i)
- {
- DNSServer *s = dns_servers->at(i);
-
- for (unsigned j = 0; j < s->GetIPs().size(); ++j)
- {
- DNS::QueryType q_type = s->GetIPs()[j].find(':') != Anope::string::npos ? DNS::QUERY_AAAA : DNS::QUERY_A;
-
- if (q.type == DNS::QUERY_AXFR || q.type == DNS::QUERY_ANY || q_type == q.type)
- {
- DNS::ResourceRecord rr(q.name, q_type);
- rr.ttl = this->ttl;
- rr.rdata = s->GetIPs()[j];
- packet->answers.push_back(rr);
- }
- }
- }
-
- if (packet->answers.size() == answer_size)
- {
- Log(this) << "Error! There are no servers with any IPs of type " << q.type;
- /* Send back an empty answer anyway */
- }
- }
- }
-};
-
-MODULE_INIT(ModuleDNS)
diff --git a/modules/commands/os_forbid.cpp b/modules/commands/os_forbid.cpp
deleted file mode 100644
index 8c723b337..000000000
--- a/modules/commands/os_forbid.cpp
+++ /dev/null
@@ -1,559 +0,0 @@
-/* OperServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-#include "modules/os_forbid.h"
-
-static ServiceReference<NickServService> nickserv("NickServService", "NickServ");
-
-struct ForbidDataImpl : ForbidData, Serializable
-{
- ForbidDataImpl() : Serializable("ForbidData") { }
- void Serialize(Serialize::Data &data) const anope_override;
- static Serializable* Unserialize(Serializable *obj, Serialize::Data &data);
-};
-
-void ForbidDataImpl::Serialize(Serialize::Data &data) const
-{
- data["mask"] << this->mask;
- data["creator"] << this->creator;
- data["reason"] << this->reason;
- data["created"] << this->created;
- data["expires"] << this->expires;
- data["type"] << this->type;
-}
-
-Serializable* ForbidDataImpl::Unserialize(Serializable *obj, Serialize::Data &data)
-{
- if (!forbid_service)
- return NULL;
-
- ForbidDataImpl *fb;
- if (obj)
- fb = anope_dynamic_static_cast<ForbidDataImpl *>(obj);
- else
- fb = new ForbidDataImpl();
-
- data["mask"] >> fb->mask;
- data["creator"] >> fb->creator;
- data["reason"] >> fb->reason;
- data["created"] >> fb->created;
- data["expires"] >> fb->expires;
- unsigned int t;
- data["type"] >> t;
- fb->type = static_cast<ForbidType>(t);
-
- if (t > FT_SIZE - 1)
- return NULL;
-
- if (!obj)
- forbid_service->AddForbid(fb);
- return fb;
-}
-
-class MyForbidService : public ForbidService
-{
- Serialize::Checker<std::vector<ForbidData *>[FT_SIZE - 1]> forbid_data;
-
- inline std::vector<ForbidData *>& forbids(unsigned t) { return (*this->forbid_data)[t - 1]; }
-
- public:
- MyForbidService(Module *m) : ForbidService(m), forbid_data("ForbidData") { }
-
- ~MyForbidService()
- {
- std::vector<ForbidData *> f = GetForbids();
- for (unsigned i = 0; i < f.size(); ++i)
- delete f[i];
- }
-
- void AddForbid(ForbidData *d) anope_override
- {
- this->forbids(d->type).push_back(d);
- }
-
- void RemoveForbid(ForbidData *d) anope_override
- {
- std::vector<ForbidData *>::iterator it = std::find(this->forbids(d->type).begin(), this->forbids(d->type).end(), d);
- if (it != this->forbids(d->type).end())
- this->forbids(d->type).erase(it);
- delete d;
- }
-
- ForbidData *CreateForbid() anope_override
- {
- return new ForbidDataImpl();
- }
-
- ForbidData *FindForbid(const Anope::string &mask, ForbidType ftype) anope_override
- {
- for (unsigned i = this->forbids(ftype).size(); i > 0; --i)
- {
- ForbidData *d = this->forbids(ftype)[i - 1];
-
- if (Anope::Match(mask, d->mask, false, true))
- return d;
- }
- return NULL;
- }
-
- std::vector<ForbidData *> GetForbids() anope_override
- {
- std::vector<ForbidData *> f;
- for (unsigned j = FT_NICK; j < FT_SIZE; ++j)
- for (unsigned i = this->forbids(j).size(); i > 0; --i)
- {
- ForbidData *d = this->forbids(j).at(i - 1);
-
- if (d->expires && !Anope::NoExpire && Anope::CurTime >= d->expires)
- {
- Anope::string ftype = "none";
- if (d->type == FT_NICK)
- ftype = "nick";
- else if (d->type == FT_CHAN)
- ftype = "chan";
- else if (d->type == FT_EMAIL)
- ftype = "email";
-
- Log(LOG_NORMAL, "expire/forbid", Config->GetClient("OperServ")) << "Expiring forbid for " << d->mask << " type " << ftype;
- this->forbids(j).erase(this->forbids(j).begin() + i - 1);
- delete d;
- }
- else
- f.push_back(d);
- }
-
- return f;
- }
-};
-
-class CommandOSForbid : public Command
-{
- ServiceReference<ForbidService> fs;
- public:
- CommandOSForbid(Module *creator) : Command(creator, "operserv/forbid", 1, 5), fs("ForbidService", "forbid")
- {
- this->SetDesc(_("Forbid usage of nicknames, channels, and emails"));
- this->SetSyntax(_("ADD {NICK|CHAN|EMAIL|REGISTER} [+\037expiry\037] \037entry\037 \037reason\037"));
- this->SetSyntax(_("DEL {NICK|CHAN|EMAIL|REGISTER} \037entry\037"));
- this->SetSyntax("LIST [NICK|CHAN|EMAIL|REGISTER]");
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (!this->fs)
- return;
-
- const Anope::string &command = params[0];
- const Anope::string &subcommand = params.size() > 1 ? params[1] : "";
-
- ForbidType ftype = FT_SIZE;
- if (subcommand.equals_ci("NICK"))
- ftype = FT_NICK;
- else if (subcommand.equals_ci("CHAN"))
- ftype = FT_CHAN;
- else if (subcommand.equals_ci("EMAIL"))
- ftype = FT_EMAIL;
- else if (subcommand.equals_ci("REGISTER"))
- ftype = FT_REGISTER;
-
- if (command.equals_ci("ADD") && params.size() > 3 && ftype != FT_SIZE)
- {
- const Anope::string &expiry = params[2][0] == '+' ? params[2] : "";
- const Anope::string &entry = !expiry.empty() ? params[3] : params[2];
- Anope::string reason;
- if (expiry.empty())
- reason = params[3] + " ";
- if (params.size() > 4)
- reason += params[4];
- reason.trim();
-
- if (entry.replace_all_cs("?*", "").empty())
- {
- source.Reply(_("The mask must contain at least one non wildcard character."));
- return;
- }
-
- time_t expiryt = 0;
-
- if (!expiry.empty())
- {
- expiryt = Anope::DoTime(expiry);
- if (expiryt == -1)
- {
- source.Reply(BAD_EXPIRY_TIME);
- return;
- }
- else if (expiryt)
- expiryt += Anope::CurTime;
- }
-
- NickAlias *target = NickAlias::Find(entry);
- if (target != NULL && Config->GetModule("nickserv")->Get<bool>("secureadmins", "yes") && target->nc->IsServicesOper())
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- ForbidData *d = this->fs->FindForbid(entry, ftype);
- bool created = false;
- if (d == NULL)
- {
- d = new ForbidDataImpl();
- created = true;
- }
-
- d->mask = entry;
- d->creator = source.GetNick();
- d->reason = reason;
- d->created = Anope::CurTime;
- d->expires = expiryt;
- d->type = ftype;
- if (created)
- this->fs->AddForbid(d);
-
- if (Anope::ReadOnly)
- source.Reply(READ_ONLY_MODE);
-
- Log(LOG_ADMIN, source, this) << "to add a forbid on " << entry << " of type " << subcommand;
- source.Reply(_("Added a forbid on %s of type %s to expire on %s."), entry.c_str(), subcommand.lower().c_str(), d->expires ? Anope::strftime(d->expires, source.GetAccount()).c_str() : "never");
-
- /* apply forbid */
- switch (ftype)
- {
- case FT_NICK:
- {
- int na_matches = 0;
-
- for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it)
- module->OnUserNickChange(it->second, "");
-
- for (nickalias_map::const_iterator it = NickAliasList->begin(), it_end = NickAliasList->end(); it != it_end;)
- {
- NickAlias *na = it->second;
- ++it;
-
- d = this->fs->FindForbid(na->nick, FT_NICK);
- if (d == NULL)
- continue;
-
- ++na_matches;
-
- delete na;
- }
-
- source.Reply(_("\002%d\002 nickname(s) dropped."), na_matches);
- break;
- }
- case FT_CHAN:
- {
- int chan_matches = 0, ci_matches = 0;
-
- for (channel_map::const_iterator it = ChannelList.begin(), it_end = ChannelList.end(); it != it_end;)
- {
- Channel *c = it->second;
- ++it;
-
- d = this->fs->FindForbid(c->name, FT_CHAN);
- if (d == NULL)
- continue;
-
- ServiceReference<ChanServService> chanserv("ChanServService", "ChanServ");
- BotInfo *OperServ = Config->GetClient("OperServ");
- if (IRCD->CanSQLineChannel && OperServ)
- {
- time_t inhabit = Config->GetModule("chanserv")->Get<time_t>("inhabit", "15s");
- XLine x(c->name, OperServ->nick, Anope::CurTime + inhabit, d->reason);
- IRCD->SendSQLine(NULL, &x);
- }
- else if (chanserv)
- {
- chanserv->Hold(c);
- }
-
- ++chan_matches;
-
- for (Channel::ChanUserList::const_iterator cit = c->users.begin(), cit_end = c->users.end(); cit != cit_end;)
- {
- User *u = cit->first;
- ++cit;
-
- if (u->server == Me || u->HasMode("OPER"))
- continue;
-
- reason = Anope::printf(Language::Translate(u, _("This channel has been forbidden: %s")), d->reason.c_str());
-
- c->Kick(source.service, u, "%s", reason.c_str());
- }
- }
-
- for (registered_channel_map::const_iterator it = RegisteredChannelList->begin(); it != RegisteredChannelList->end();)
- {
- ChannelInfo *ci = it->second;
- ++it;
-
- d = this->fs->FindForbid(ci->name, FT_CHAN);
- if (d == NULL)
- continue;
-
- ++ci_matches;
-
- delete ci;
- }
-
- source.Reply(_("\002%d\002 channel(s) cleared, and \002%d\002 channel(s) dropped."), chan_matches, ci_matches);
-
- break;
- }
- default:
- break;
- }
-
- }
- else if (command.equals_ci("DEL") && params.size() > 2 && ftype != FT_SIZE)
- {
- const Anope::string &entry = params[2];
-
- ForbidData *d = this->fs->FindForbid(entry, ftype);
- if (d != NULL)
- {
- if (Anope::ReadOnly)
- source.Reply(READ_ONLY_MODE);
-
- Log(LOG_ADMIN, source, this) << "to remove forbid on " << d->mask << " of type " << subcommand;
- source.Reply(_("%s deleted from the %s forbid list."), d->mask.c_str(), subcommand.c_str());
- this->fs->RemoveForbid(d);
- }
- else
- source.Reply(_("Forbid on %s was not found."), entry.c_str());
- }
- else if (command.equals_ci("LIST"))
- {
- const std::vector<ForbidData *> &forbids = this->fs->GetForbids();
- if (forbids.empty())
- source.Reply(_("Forbid list is empty."));
- else
- {
- ListFormatter list(source.GetAccount());
- list.AddColumn(_("Mask")).AddColumn(_("Type")).AddColumn(_("Creator")).AddColumn(_("Expires")).AddColumn(_("Reason"));
-
- unsigned shown = 0;
- for (unsigned i = 0; i < forbids.size(); ++i)
- {
- ForbidData *d = forbids[i];
-
- if (ftype != FT_SIZE && ftype != d->type)
- continue;
-
- Anope::string stype;
- if (d->type == FT_NICK)
- stype = "NICK";
- else if (d->type == FT_CHAN)
- stype = "CHAN";
- else if (d->type == FT_EMAIL)
- stype = "EMAIL";
- else if (d->type == FT_REGISTER)
- stype = "REGISTER";
- else
- continue;
-
- ListFormatter::ListEntry entry;
- entry["Mask"] = d->mask;
- entry["Type"] = stype;
- entry["Creator"] = d->creator;
- entry["Expires"] = d->expires ? Anope::strftime(d->expires, NULL, true).c_str() : Language::Translate(source.GetAccount(), _("Never"));
- entry["Reason"] = d->reason;
- list.AddEntry(entry);
- ++shown;
- }
-
- if (!shown)
- {
- source.Reply(_("There are no forbids of type %s."), subcommand.upper().c_str());
- }
- else
- {
- source.Reply(_("Forbid list:"));
-
- std::vector<Anope::string> replies;
- list.Process(replies);
-
- for (unsigned i = 0; i < replies.size(); ++i)
- source.Reply(replies[i]);
-
- if (shown >= forbids.size())
- source.Reply(_("End of forbid list."));
- else
- source.Reply(_("End of forbid list - %d/%d entries shown."), shown, forbids.size());
- }
- }
- }
- else
- this->OnSyntaxError(source, command);
-
- return;
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Forbid allows you to forbid usage of certain nicknames, channels,\n"
- "and email addresses. Wildcards are accepted for all entries."));
-
- const Anope::string &regexengine = Config->GetBlock("options")->Get<const Anope::string>("regexengine");
- if (!regexengine.empty())
- {
- source.Reply(" ");
- source.Reply(_("Regex matches are also supported using the %s engine.\n"
- "Enclose your pattern in // if this is desired."), regexengine.c_str());
- }
-
- return true;
- }
-};
-
-class OSForbid : public Module
-{
- MyForbidService forbidService;
- Serialize::Type forbiddata_type;
- CommandOSForbid commandosforbid;
-
- public:
- OSForbid(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- forbidService(this), forbiddata_type("ForbidData", ForbidDataImpl::Unserialize), commandosforbid(this)
- {
-
- }
-
- void OnUserConnect(User *u, bool &exempt) anope_override
- {
- if (u->Quitting() || exempt)
- return;
-
- this->OnUserNickChange(u, "");
- }
-
- void OnUserNickChange(User *u, const Anope::string &) anope_override
- {
- if (u->HasMode("OPER"))
- return;
-
- ForbidData *d = this->forbidService.FindForbid(u->nick, FT_NICK);
- if (d != NULL)
- {
- BotInfo *bi = Config->GetClient("NickServ");
- if (!bi)
- bi = Config->GetClient("OperServ");
- if (bi)
- u->SendMessage(bi, _("This nickname has been forbidden: %s"), d->reason.c_str());
- if (nickserv)
- nickserv->Collide(u, NULL);
- }
- }
-
- EventReturn OnCheckKick(User *u, Channel *c, Anope::string &mask, Anope::string &reason) anope_override
- {
- BotInfo *OperServ = Config->GetClient("OperServ");
- if (u->HasMode("OPER") || !OperServ)
- return EVENT_CONTINUE;
-
- ForbidData *d = this->forbidService.FindForbid(c->name, FT_CHAN);
- if (d != NULL)
- {
- ServiceReference<ChanServService> chanserv("ChanServService", "ChanServ");
- if (IRCD->CanSQLineChannel)
- {
- time_t inhabit = Config->GetModule("chanserv")->Get<time_t>("inhabit", "15s");
- XLine x(c->name, OperServ->nick, Anope::CurTime + inhabit, d->reason);
- IRCD->SendSQLine(NULL, &x);
- }
- else if (chanserv)
- {
- chanserv->Hold(c);
- }
-
- reason = Anope::printf(Language::Translate(u, _("This channel has been forbidden: %s")), d->reason.c_str());
-
- return EVENT_STOP;
- }
-
- return EVENT_CONTINUE;
- }
-
- EventReturn OnPreCommand(CommandSource &source, Command *command, std::vector<Anope::string> &params) anope_override
- {
- if (command->name == "nickserv/info" && params.size() > 0)
- {
- ForbidData *d = this->forbidService.FindForbid(params[0], FT_NICK);
- if (d != NULL)
- {
- if (source.IsOper())
- source.Reply(_("Nick \002%s\002 is forbidden by %s: %s"), params[0].c_str(), d->creator.c_str(), d->reason.c_str());
- else
- source.Reply(_("Nick \002%s\002 is forbidden."), params[0].c_str());
- return EVENT_STOP;
- }
- }
- else if (command->name == "chanserv/info" && params.size() > 0)
- {
- ForbidData *d = this->forbidService.FindForbid(params[0], FT_CHAN);
- if (d != NULL)
- {
- if (source.IsOper())
- source.Reply(_("Channel \002%s\002 is forbidden by %s: %s"), params[0].c_str(), d->creator.c_str(), d->reason.c_str());
- else
- source.Reply(_("Channel \002%s\002 is forbidden."), params[0].c_str());
- return EVENT_STOP;
- }
- }
- else if (source.IsOper())
- return EVENT_CONTINUE;
- else if (command->name == "nickserv/register" && params.size() > 1)
- {
- ForbidData *d = this->forbidService.FindForbid(source.GetNick(), FT_REGISTER);
- if (d != NULL)
- {
- source.Reply(NICK_CANNOT_BE_REGISTERED, source.GetNick().c_str());
- return EVENT_STOP;
- }
-
- d = this->forbidService.FindForbid(params[1], FT_EMAIL);
- if (d != NULL)
- {
- source.Reply(_("Your email address is not allowed, choose a different one."));
- return EVENT_STOP;
- }
- }
- else if (command->name == "nickserv/set/email" && params.size() > 0)
- {
- ForbidData *d = this->forbidService.FindForbid(params[0], FT_EMAIL);
- if (d != NULL)
- {
- source.Reply(_("Your email address is not allowed, choose a different one."));
- return EVENT_STOP;
- }
- }
- else if (command->name == "chanserv/register" && !params.empty())
- {
- ForbidData *d = this->forbidService.FindForbid(params[0], FT_REGISTER);
- if (d != NULL)
- {
- source.Reply(CHAN_X_INVALID, params[0].c_str());
- return EVENT_STOP;
- }
- }
-
- return EVENT_CONTINUE;
- }
-};
-
-MODULE_INIT(OSForbid)
diff --git a/modules/commands/os_ignore.cpp b/modules/commands/os_ignore.cpp
deleted file mode 100644
index 2f4fca3e2..000000000
--- a/modules/commands/os_ignore.cpp
+++ /dev/null
@@ -1,416 +0,0 @@
-/* OperServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-#include "modules/os_ignore.h"
-
-struct IgnoreDataImpl : IgnoreData, Serializable
-{
- IgnoreDataImpl() : Serializable("IgnoreData") { }
- ~IgnoreDataImpl();
- void Serialize(Serialize::Data &data) const anope_override;
- static Serializable* Unserialize(Serializable *obj, Serialize::Data &data);
-};
-
-IgnoreDataImpl::~IgnoreDataImpl()
-{
- if (ignore_service)
- ignore_service->DelIgnore(this);
-}
-
-void IgnoreDataImpl::Serialize(Serialize::Data &data) const
-{
- data["mask"] << this->mask;
- data["creator"] << this->creator;
- data["reason"] << this->reason;
- data["time"] << this->time;
-}
-
-Serializable* IgnoreDataImpl::Unserialize(Serializable *obj, Serialize::Data &data)
-{
- if (!ignore_service)
- return NULL;
-
- IgnoreDataImpl *ign;
- if (obj)
- ign = anope_dynamic_static_cast<IgnoreDataImpl *>(obj);
- else
- {
- ign = new IgnoreDataImpl();
- ignore_service->AddIgnore(ign);
- }
-
- data["mask"] >> ign->mask;
- data["creator"] >> ign->creator;
- data["reason"] >> ign->reason;
- data["time"] >> ign->time;
-
- return ign;
-}
-
-
-class OSIgnoreService : public IgnoreService
-{
- Serialize::Checker<std::vector<IgnoreData *> > ignores;
-
- public:
- OSIgnoreService(Module *o) : IgnoreService(o), ignores("IgnoreData") { }
-
- void AddIgnore(IgnoreData *ign) anope_override
- {
- ignores->push_back(ign);
- }
-
- void DelIgnore(IgnoreData *ign) anope_override
- {
- std::vector<IgnoreData *>::iterator it = std::find(ignores->begin(), ignores->end(), ign);
- if (it != ignores->end())
- ignores->erase(it);
- }
-
- void ClearIgnores() anope_override
- {
- for (unsigned i = ignores->size(); i > 0; --i)
- {
- IgnoreData *ign = ignores->at(i - 1);
- delete ign;
- }
- }
-
- IgnoreData *Create() anope_override
- {
- return new IgnoreDataImpl();
- }
-
- IgnoreData *Find(const Anope::string &mask) anope_override
- {
- User *u = User::Find(mask, true);
- std::vector<IgnoreData *>::iterator ign = this->ignores->begin(), ign_end = this->ignores->end();
-
- if (u)
- {
- for (; ign != ign_end; ++ign)
- {
- Entry ignore_mask("", (*ign)->mask);
- if (ignore_mask.Matches(u, true))
- break;
- }
- }
- else
- {
- size_t user, host;
- Anope::string tmp;
- /* We didn't get a user.. generate a valid mask. */
- if ((host = mask.find('@')) != Anope::string::npos)
- {
- if ((user = mask.find('!')) != Anope::string::npos)
- {
- /* this should never happen */
- if (user > host)
- return NULL;
- tmp = mask;
- }
- else
- /* We have user@host. Add nick wildcard. */
- tmp = "*!" + mask;
- }
- /* We only got a nick.. */
- else
- tmp = mask + "!*@*";
-
- for (; ign != ign_end; ++ign)
- if (Anope::Match(tmp, (*ign)->mask, false, true))
- break;
- }
-
- /* Check whether the entry has timed out */
- if (ign != ign_end)
- {
- IgnoreData *id = *ign;
-
- if (id->time && !Anope::NoExpire && id->time <= Anope::CurTime)
- {
- Log(LOG_NORMAL, "expire/ignore", Config->GetClient("OperServ")) << "Expiring ignore entry " << id->mask;
- delete id;
- }
- else
- return id;
- }
-
- return NULL;
- }
-
- std::vector<IgnoreData *> &GetIgnores() anope_override
- {
- return *ignores;
- }
-};
-
-class CommandOSIgnore : public Command
-{
- private:
- Anope::string RealMask(const Anope::string &mask)
- {
- /* If it s an existing user, we ignore the hostmask. */
- User *u = User::Find(mask, true);
- if (u)
- return "*!*@" + u->host;
-
- size_t host = mask.find('@');
- /* Determine whether we get a nick or a mask. */
- if (host != Anope::string::npos)
- {
- size_t user = mask.find('!');
- /* Check whether we have a nick too.. */
- if (user != Anope::string::npos)
- {
- if (user > host)
- /* this should never happen */
- return "";
- else
- return mask;
- }
- else
- /* We have user@host. Add nick wildcard. */
- return "*!" + mask;
- }
-
- /* We only got a nick.. */
- return mask + "!*@*";
- }
-
- void DoAdd(CommandSource &source, const std::vector<Anope::string> &params)
- {
- if (!ignore_service)
- return;
-
- const Anope::string &time = params.size() > 1 ? params[1] : "";
- const Anope::string &nick = params.size() > 2 ? params[2] : "";
- const Anope::string &reason = params.size() > 3 ? params[3] : "";
-
- if (time.empty() || nick.empty())
- {
- this->OnSyntaxError(source, "ADD");
- return;
- }
- else
- {
- time_t t = Anope::DoTime(time);
-
- if (t <= -1)
- {
- source.Reply(BAD_EXPIRY_TIME);
- return;
- }
-
- Anope::string mask = RealMask(nick);
- if (mask.empty())
- {
- source.Reply(BAD_USERHOST_MASK);
- return;
- }
-
- if (Anope::ReadOnly)
- source.Reply(READ_ONLY_MODE);
-
- IgnoreData *ign = new IgnoreDataImpl();
- ign->mask = mask;
- ign->creator = source.GetNick();
- ign->reason = reason;
- ign->time = t ? Anope::CurTime + t : 0;
-
- ignore_service->AddIgnore(ign);
- if (!t)
- {
- source.Reply(_("\002%s\002 will now permanently be ignored."), mask.c_str());
- Log(LOG_ADMIN, source, this) << "to add a permanent ignore for " << mask;
- }
- else
- {
- source.Reply(_("\002%s\002 will now be ignored for \002%s\002."), mask.c_str(), Anope::Duration(t, source.GetAccount()).c_str());
- Log(LOG_ADMIN, source, this) << "to add an ignore on " << mask << " for " << Anope::Duration(t);
- }
- }
- }
-
- void DoList(CommandSource &source)
- {
- if (!ignore_service)
- return;
-
- std::vector<IgnoreData *> &ignores = ignore_service->GetIgnores();
- for (unsigned i = ignores.size(); i > 0; --i)
- {
- IgnoreData *id = ignores[i - 1];
-
- if (id->time && !Anope::NoExpire && id->time <= Anope::CurTime)
- {
- Log(LOG_NORMAL, "expire/ignore", Config->GetClient("OperServ")) << "Expiring ignore entry " << id->mask;
- delete id;
- }
- }
-
- if (ignores.empty())
- source.Reply(_("Ignore list is empty."));
- else
- {
- ListFormatter list(source.GetAccount());
- list.AddColumn(_("Mask")).AddColumn(_("Creator")).AddColumn(_("Reason")).AddColumn(_("Expires"));
-
- for (unsigned i = ignores.size(); i > 0; --i)
- {
- const IgnoreData *ignore = ignores[i - 1];
-
- ListFormatter::ListEntry entry;
- entry["Mask"] = ignore->mask;
- entry["Creator"] = ignore->creator;
- entry["Reason"] = ignore->reason;
- entry["Expires"] = Anope::Expires(ignore->time, source.GetAccount());
- list.AddEntry(entry);
- }
-
- source.Reply(_("Services ignore list:"));
-
- std::vector<Anope::string> replies;
- list.Process(replies);
-
- for (unsigned i = 0; i < replies.size(); ++i)
- source.Reply(replies[i]);
- }
- }
-
- void DoDel(CommandSource &source, const std::vector<Anope::string> &params)
- {
- if (!ignore_service)
- return;
-
- const Anope::string nick = params.size() > 1 ? params[1] : "";
- if (nick.empty())
- {
- this->OnSyntaxError(source, "DEL");
- return;
- }
-
- Anope::string mask = RealMask(nick);
- if (mask.empty())
- {
- source.Reply(BAD_USERHOST_MASK);
- return;
- }
-
- IgnoreData *ign = ignore_service->Find(mask);
- if (ign)
- {
- if (Anope::ReadOnly)
- source.Reply(READ_ONLY_MODE);
-
- Log(LOG_ADMIN, source, this) << "to remove an ignore on " << mask;
- source.Reply(_("\002%s\002 will no longer be ignored."), mask.c_str());
- delete ign;
- }
- else
- source.Reply(_("\002%s\002 not found on ignore list."), mask.c_str());
- }
-
- void DoClear(CommandSource &source)
- {
- if (!ignore_service)
- return;
-
- if (Anope::ReadOnly)
- source.Reply(READ_ONLY_MODE);
-
- ignore_service->ClearIgnores();
- Log(LOG_ADMIN, source, this) << "to CLEAR the list";
- source.Reply(_("Ignore list has been cleared."));
-
- return;
- }
-
- public:
- CommandOSIgnore(Module *creator) : Command(creator, "operserv/ignore", 1, 4)
- {
- this->SetDesc(_("Modify the Services ignore list"));
- this->SetSyntax(_("ADD \037expiry\037 {\037nick\037|\037mask\037} [\037reason\037]"));
- this->SetSyntax(_("DEL {\037nick\037|\037mask\037}"));
- this->SetSyntax("LIST");
- this->SetSyntax("CLEAR");
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &cmd = params[0];
-
- if (cmd.equals_ci("ADD"))
- return this->DoAdd(source, params);
- else if (cmd.equals_ci("LIST"))
- return this->DoList(source);
- else if (cmd.equals_ci("DEL"))
- return this->DoDel(source, params);
- else if (cmd.equals_ci("CLEAR"))
- return this->DoClear(source);
- else
- this->OnSyntaxError(source, "");
-
- return;
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Allows Services Operators to make Services ignore a nick or mask\n"
- "for a certain time or until the next restart. The default\n"
- "time format is seconds. You can specify it by using units.\n"
- "Valid units are: \037s\037 for seconds, \037m\037 for minutes,\n"
- "\037h\037 for hours and \037d\037 for days.\n"
- "Combinations of these units are not permitted.\n"
- "To make Services permanently ignore the user, type 0 as time.\n"
- "When adding a \037mask\037, it should be in the format nick!user@host,\n"
- "everything else will be considered a nick. Wildcards are permitted.\n"
- " \n"
- "Ignores will not be enforced on IRC Operators."));
-
- const Anope::string &regexengine = Config->GetBlock("options")->Get<const Anope::string>("regexengine");
- if (!regexengine.empty())
- {
- source.Reply(" ");
- source.Reply(_("Regex matches are also supported using the %s engine.\n"
- "Enclose your pattern in // if this is desired."), regexengine.c_str());
- }
-
- return true;
- }
-};
-
-class OSIgnore : public Module
-{
- Serialize::Type ignoredata_type;
- OSIgnoreService osignoreservice;
- CommandOSIgnore commandosignore;
-
- public:
- OSIgnore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- ignoredata_type("IgnoreData", IgnoreDataImpl::Unserialize), osignoreservice(this), commandosignore(this)
- {
-
- }
-
- EventReturn OnBotPrivmsg(User *u, BotInfo *bi, Anope::string &message) anope_override
- {
- if (!u->HasMode("OPER") && this->osignoreservice.Find(u->nick))
- return EVENT_STOP;
-
- return EVENT_CONTINUE;
- }
-};
-
-MODULE_INIT(OSIgnore)
diff --git a/modules/commands/os_info.cpp b/modules/commands/os_info.cpp
deleted file mode 100644
index 0965d5ea7..000000000
--- a/modules/commands/os_info.cpp
+++ /dev/null
@@ -1,290 +0,0 @@
-/* OperServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- */
-
-#include "module.h"
-
-struct OperInfo : Serializable
-{
- Anope::string target;
- Anope::string info;
- Anope::string adder;
- time_t created;
-
- OperInfo() : Serializable("OperInfo"), created(0) { }
- OperInfo(const Anope::string &t, const Anope::string &i, const Anope::string &a, time_t c) :
- Serializable("OperInfo"), target(t), info(i), adder(a), created(c) { }
-
- ~OperInfo();
-
- void Serialize(Serialize::Data &data) const anope_override
- {
- data["target"] << target;
- data["info"] << info;
- data["adder"] << adder;
- data["created"] << created;
- }
-
- static Serializable *Unserialize(Serializable *obj, Serialize::Data &data);
-};
-
-struct OperInfos : Serialize::Checker<std::vector<OperInfo *> >
-{
- OperInfos(Extensible *) : Serialize::Checker<std::vector<OperInfo *> >("OperInfo") { }
-
- ~OperInfos()
- {
- for (unsigned i = (*this)->size(); i > 0; --i)
- delete (*this)->at(i - 1);
- }
-
- static Extensible *Find(const Anope::string &target)
- {
- NickAlias *na = NickAlias::Find(target);
- if (na)
- return na->nc;
- return ChannelInfo::Find(target);
- }
-};
-
-OperInfo::~OperInfo()
-{
- Extensible *e = OperInfos::Find(target);
- if (e)
- {
- OperInfos *op = e->GetExt<OperInfos>("operinfo");
- if (op)
- {
- std::vector<OperInfo *>::iterator it = std::find((*op)->begin(), (*op)->end(), this);
- if (it != (*op)->end())
- (*op)->erase(it);
- }
- }
-}
-
-Serializable *OperInfo::Unserialize(Serializable *obj, Serialize::Data &data)
-{
- Anope::string starget;
- data["target"] >> starget;
-
- Extensible *e = OperInfos::Find(starget);
- if (!e)
- return NULL;
-
- OperInfos *oi = e->Require<OperInfos>("operinfo");
- OperInfo *o;
- if (obj)
- o = anope_dynamic_static_cast<OperInfo *>(obj);
- else
- {
- o = new OperInfo();
- o->target = starget;
- }
- data["info"] >> o->info;
- data["adder"] >> o->adder;
- data["created"] >> o->created;
-
- if (!obj)
- (*oi)->push_back(o);
- return o;
-}
-
-class CommandOSInfo : public Command
-{
- public:
- CommandOSInfo(Module *creator) : Command(creator, "operserv/info", 2, 3)
- {
- this->SetDesc(_("Associate oper info with a nick or channel"));
- this->SetSyntax(_("ADD \037target\037 \037info\037"));
- this->SetSyntax(_("DEL \037target\037 \037info\037"));
- this->SetSyntax(_("CLEAR \037target\037"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &cmd = params[0], target = params[1], info = params.size() > 2 ? params[2] : "";
-
- Extensible *e;
- if (IRCD->IsChannelValid(target))
- {
- ChannelInfo *ci = ChannelInfo::Find(target);
- if (!ci)
- {
- source.Reply(CHAN_X_NOT_REGISTERED, target.c_str());
- return;
- }
-
- e = ci;
- }
- else
- {
- NickAlias *na = NickAlias::Find(target);
- if (!na)
- {
- source.Reply(NICK_X_NOT_REGISTERED, target.c_str());
- return;
- }
-
- e = na->nc;
- }
-
- if (cmd.equals_ci("ADD"))
- {
- if (info.empty())
- {
- this->OnSyntaxError(source, cmd);
- return;
- }
-
- OperInfos *oi = e->Require<OperInfos>("operinfo");
-
- if ((*oi)->size() >= Config->GetModule(this->module)->Get<unsigned>("max", "10"))
- {
- source.Reply(_("The oper info list for \002%s\002 is full."), target.c_str());
- return;
- }
-
- for (unsigned i = 0; i < (*oi)->size(); ++i)
- {
- OperInfo *o = (*oi)->at(i);
-
- if (o->info.equals_ci(info))
- {
- source.Reply(_("The oper info already exists on \002%s\002."), target.c_str());
- return;
- }
- }
-
- (*oi)->push_back(new OperInfo(target, info, source.GetNick(), Anope::CurTime));
-
- source.Reply(_("Added info to \002%s\002."), target.c_str());
- Log(LOG_ADMIN, source, this) << "to add information to " << target;
-
- if (Anope::ReadOnly)
- source.Reply(READ_ONLY_MODE);
- }
- else if (cmd.equals_ci("DEL"))
- {
- if (info.empty())
- {
- this->OnSyntaxError(source, cmd);
- return;
- }
-
- OperInfos *oi = e->GetExt<OperInfos>("operinfo");
-
- if (!oi)
- {
- source.Reply(_("Oper info list for \002%s\002 is empty."), target.c_str());
- return;
- }
-
- bool found = false;
- for (unsigned i = (*oi)->size(); i > 0; --i)
- {
- OperInfo *o = (*oi)->at(i - 1);
-
- if (o->info.equals_ci(info))
- {
- delete o;
- found = true;
- break;
- }
- }
-
- if (!found)
- {
- source.Reply(_("No such info \"%s\" on \002%s\002."), info.c_str(), target.c_str());
- }
- else
- {
- if ((*oi)->empty())
- e->Shrink<OperInfos>("operinfo");
-
- source.Reply(_("Deleted info from \002%s\002."), target.c_str());
- Log(LOG_ADMIN, source, this) << "to remove information from " << target;
-
- if (Anope::ReadOnly)
- source.Reply(READ_ONLY_MODE);
- }
- }
- else if (cmd.equals_ci("CLEAR"))
- {
- OperInfos *oi = e->GetExt<OperInfos>("operinfo");
-
- if (!oi)
- {
- source.Reply(_("Oper info list for \002%s\002 is empty."), target.c_str());
- return;
- }
-
- e->Shrink<OperInfos>("operinfo");
-
- source.Reply(_("Cleared info from \002%s\002."), target.c_str());
- Log(LOG_ADMIN, source, this) << "to clear information for " << target;
-
- if (Anope::ReadOnly)
- source.Reply(READ_ONLY_MODE);
- }
- else
- {
- this->OnSyntaxError(source, cmd);
- }
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Add or delete oper information for a given nick or channel.\n"
- "This will show to opers in the respective info command for\n"
- "the nick or channel."));
- return true;
- }
-};
-
-class OSInfo : public Module
-{
- CommandOSInfo commandosinfo;
- ExtensibleItem<OperInfos> oinfo;
- Serialize::Type oinfo_type;
-
- void OnInfo(CommandSource &source, Extensible *e, InfoFormatter &info)
- {
- if (!source.IsOper())
- return;
-
- OperInfos *oi = oinfo.Get(e);
- if (!oi)
- return;
-
- for (unsigned i = 0; i < (*oi)->size(); ++i)
- {
- OperInfo *o = (*oi)->at(i);
- info[_("Oper Info")] = Anope::printf(_("(by %s on %s) %s"), o->adder.c_str(), Anope::strftime(o->created, source.GetAccount(), true).c_str(), o->info.c_str());
- }
- }
-
- public:
- OSInfo(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandosinfo(this), oinfo(this, "operinfo"), oinfo_type("OperInfo", OperInfo::Unserialize)
- {
-
- }
-
- void OnNickInfo(CommandSource &source, NickAlias *na, InfoFormatter &info, bool show_hidden) anope_override
- {
- OnInfo(source, na->nc, info);
- }
-
- void OnChanInfo(CommandSource &source, ChannelInfo *ci, InfoFormatter &info, bool show_hidden) anope_override
- {
- OnInfo(source, ci, info);
- }
-};
-
-MODULE_INIT(OSInfo)
diff --git a/modules/commands/os_jupe.cpp b/modules/commands/os_jupe.cpp
deleted file mode 100644
index c586feee3..000000000
--- a/modules/commands/os_jupe.cpp
+++ /dev/null
@@ -1,80 +0,0 @@
-/* OperServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class CommandOSJupe : public Command
-{
- public:
- CommandOSJupe(Module *creator) : Command(creator, "operserv/jupe", 1, 2)
- {
- this->SetDesc(_("\"Jupiter\" a server"));
- this->SetSyntax(_("\037server\037 [\037reason\037]"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &jserver = params[0];
- const Anope::string &reason = params.size() > 1 ? params[1] : "";
- Server *server = Server::Find(jserver, true);
-
- if (!IRCD->IsHostValid(jserver) || jserver.find('.') == Anope::string::npos)
- source.Reply(_("Please use a valid server name when juping."));
- else if (server == Me || server == Servers::GetUplink())
- source.Reply(_("You can not jupe your Services' pseudoserver or your uplink server."));
- else if (server && server->IsJuped())
- source.Reply(_("You can not jupe an already juped server."));
- else
- {
- Anope::string rbuf = "Juped by " + source.GetNick() + (!reason.empty() ? ": " + reason : "");
- /* Generate the new sid before quitting the old server, so they can't collide */
- Anope::string sid = IRCD->SID_Retrieve();
- if (server)
- {
- IRCD->SendSquit(server, rbuf);
- server->Delete(rbuf);
- }
- Server *juped_server = new Server(Me, jserver, 1, rbuf, sid, true);
- IRCD->SendServer(juped_server);
-
- Log(LOG_ADMIN, source, this) << "on " << jserver << " (" << rbuf << ")";
- }
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Tells Services to jupiter a server -- that is, to create\n"
- "a fake \"server\" connected to Services which prevents\n"
- "the real server of that name from connecting. The jupe\n"
- "may be removed using a standard \002SQUIT\002. If a reason is\n"
- "given, it is placed in the server information field;\n"
- "otherwise, the server information field will contain the\n"
- "text \"Juped by <nick>\", showing the nickname of the\n"
- "person who jupitered the server."));
- return true;
- }
-};
-
-class OSJupe : public Module
-{
- CommandOSJupe commandosjupe;
-
- public:
- OSJupe(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandosjupe(this)
- {
-
- }
-};
-
-MODULE_INIT(OSJupe)
diff --git a/modules/commands/os_kick.cpp b/modules/commands/os_kick.cpp
deleted file mode 100644
index 5a3584240..000000000
--- a/modules/commands/os_kick.cpp
+++ /dev/null
@@ -1,84 +0,0 @@
-/* OperServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class CommandOSKick : public Command
-{
- public:
- CommandOSKick(Module *creator) : Command(creator, "operserv/kick", 3, 3)
- {
- this->SetDesc(_("Kick a user from a channel"));
- this->SetSyntax(_("\037channel\037 \037user\037 \037reason\037"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &chan = params[0];
- const Anope::string &nick = params[1];
- const Anope::string &s = params[2];
- Channel *c;
- User *u2;
-
- if (!(c = Channel::Find(chan)))
- {
- source.Reply(CHAN_X_NOT_IN_USE, chan.c_str());
- return;
- }
-
- if (c->bouncy_modes)
- {
- source.Reply(_("Services is unable to change modes. Are your servers' U:lines configured correctly?"));
- return;
- }
-
- if (!(u2 = User::Find(nick, true)))
- {
- source.Reply(NICK_X_NOT_IN_USE, nick.c_str());
- return;
- }
-
- if (!c->Kick(source.service, u2, "%s (%s)", source.GetNick().c_str(), s.c_str()))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- Log(LOG_ADMIN, source, this) << "on " << u2->nick << " in " << c->name << " (" << s << ")";
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Allows staff to kick a user from any channel.\n"
- "Parameters are the same as for the standard /KICK\n"
- "command. The kick message will have the nickname of the\n"
- "IRCop sending the KICK command prepended; for example:\n"
- " \n"
- "*** SpamMan has been kicked off channel #my_channel by %s (Alcan (Flood))"), source.service->nick.c_str());
- return true;
- }
-};
-
-class OSKick : public Module
-{
- CommandOSKick commandoskick;
-
- public:
- OSKick(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandoskick(this)
- {
-
- }
-};
-
-MODULE_INIT(OSKick)
diff --git a/modules/commands/os_login.cpp b/modules/commands/os_login.cpp
deleted file mode 100644
index b18ffc633..000000000
--- a/modules/commands/os_login.cpp
+++ /dev/null
@@ -1,132 +0,0 @@
-/* OperServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class CommandOSLogin : public Command
-{
- public:
- CommandOSLogin(Module *creator) : Command(creator, "operserv/login", 1, 1)
- {
- this->SetSyntax(_("\037password\037"));
- this->RequireUser(true);
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &password = params[0];
-
- User *u = source.GetUser();
- Oper *o = source.nc->o;
- if (o == NULL)
- source.Reply(_("No oper block for your nick."));
- else if (o->password.empty())
- source.Reply(_("Your oper block doesn't require logging in."));
- else if (u->HasExt("os_login"))
- source.Reply(_("You are already identified."));
- else if (o->password != password)
- {
- source.Reply(PASSWORD_INCORRECT);
- u->BadPassword();
- }
- else
- {
- Log(LOG_ADMIN, source, this) << "and successfully identified to " << source.service->nick;
- u->Extend<bool>("os_login");
- source.Reply(_("Password accepted."));
- }
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Logs you in to %s so you gain Services Operator privileges.\n"
- "This command may be unnecessary if your oper block is\n"
- "configured without a password."), source.service->nick.c_str());
- return true;
- }
-
- const Anope::string GetDesc(CommandSource &source) const anope_override
- {
- return Anope::printf(Language::Translate(source.GetAccount(), _("Login to %s")), source.service->nick.c_str());
- }
-};
-
-class CommandOSLogout : public Command
-{
- public:
- CommandOSLogout(Module *creator) : Command(creator, "operserv/logout", 0, 0)
- {
- this->RequireUser(true);
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- User *u = source.GetUser();
- Oper *o = source.nc->o;
- if (o == NULL)
- source.Reply(_("No oper block for your nick."));
- else if (o->password.empty())
- source.Reply(_("Your oper block doesn't require logging in."));
- else if (!u->HasExt("os_login"))
- source.Reply(_("You are not identified."));
- else
- {
- Log(LOG_ADMIN, source, this);
- u->Shrink<bool>("os_login");
- source.Reply(_("You have been logged out."));
- }
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Logs you out from %s so you lose Services Operator privileges.\n"
- "This command is only useful if your oper block is configured\n"
- "with a password."), source.service->nick.c_str());
- return true;
- }
-
- const Anope::string GetDesc(CommandSource &source) const anope_override
- {
- return Anope::printf(Language::Translate(source.GetAccount(), _("Logout from %s")), source.service->nick.c_str());
- }
-};
-
-class OSLogin : public Module
-{
- CommandOSLogin commandoslogin;
- CommandOSLogout commandoslogout;
- ExtensibleItem<bool> os_login;
-
- public:
- OSLogin(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandoslogin(this), commandoslogout(this), os_login(this, "os_login")
- {
-
- }
-
- EventReturn IsServicesOper(User *u) anope_override
- {
- if (!u->Account()->o->password.empty())
- {
- if (os_login.HasExt(u))
- return EVENT_ALLOW;
- return EVENT_STOP;
- }
-
- return EVENT_CONTINUE;
- }
-};
-
-MODULE_INIT(OSLogin)
diff --git a/modules/commands/os_news.cpp b/modules/commands/os_news.cpp
deleted file mode 100644
index a35a74584..000000000
--- a/modules/commands/os_news.cpp
+++ /dev/null
@@ -1,464 +0,0 @@
-/* OperServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-#include "modules/os_news.h"
-
-/* List of messages for each news type. This simplifies message sending. */
-
-enum
-{
- MSG_SYNTAX,
- MSG_LIST_HEADER,
- MSG_LIST_NONE,
- MSG_ADDED,
- MSG_DEL_NOT_FOUND,
- MSG_DELETED,
- MSG_DEL_NONE,
- MSG_DELETED_ALL
-};
-
-struct NewsMessages msgarray[] = {
- {NEWS_LOGON, "LOGON",
- {_("LOGONNEWS {ADD|DEL|LIST} [\037text\037|\037num\037]\002"),
- _("Logon news items:"),
- _("There is no logon news."),
- _("Added new logon news item."),
- _("Logon news item #%s not found!"),
- _("Logon news item #%d deleted."),
- _("No logon news items to delete!"),
- _("All logon news items deleted.")}
- },
- {NEWS_OPER, "OPER",
- {_("OPERNEWS {ADD|DEL|LIST} [\037text\037|\037num\037]\002"),
- _("Oper news items:"),
- _("There is no oper news."),
- _("Added new oper news item."),
- _("Oper news item #%s not found!"),
- _("Oper news item #%d deleted."),
- _("No oper news items to delete!"),
- _("All oper news items deleted.")}
- },
- {NEWS_RANDOM, "RANDOM",
- {_("RANDOMNEWS {ADD|DEL|LIST} [\037text\037|\037num\037]\002"),
- _("Random news items:"),
- _("There is no random news."),
- _("Added new random news item."),
- _("Random news item #%s not found!"),
- _("Random news item #%d deleted."),
- _("No random news items to delete!"),
- _("All random news items deleted.")}
- }
-};
-
-struct MyNewsItem : NewsItem
-{
- void Serialize(Serialize::Data &data) const anope_override
- {
- data["type"] << this->type;
- data["text"] << this->text;
- data["who"] << this->who;
- data["time"] << this->time;
- }
-
- static Serializable* Unserialize(Serializable *obj, Serialize::Data &data)
- {
- if (!news_service)
- return NULL;
-
- NewsItem *ni;
- if (obj)
- ni = anope_dynamic_static_cast<NewsItem *>(obj);
- else
- ni = new MyNewsItem();
-
- unsigned int t;
- data["type"] >> t;
- ni->type = static_cast<NewsType>(t);
- data["text"] >> ni->text;
- data["who"] >> ni->who;
- data["time"] >> ni->time;
-
- if (!obj)
- news_service->AddNewsItem(ni);
- return ni;
- }
-};
-
-class MyNewsService : public NewsService
-{
- std::vector<NewsItem *> newsItems[3];
- public:
- MyNewsService(Module *m) : NewsService(m) { }
-
- ~MyNewsService()
- {
- for (unsigned i = 0; i < 3; ++i)
- for (unsigned j = 0; j < newsItems[i].size(); ++j)
- delete newsItems[i][j];
- }
-
- NewsItem *CreateNewsItem() anope_override
- {
- return new MyNewsItem();
- }
-
- void AddNewsItem(NewsItem *n)
- {
- this->newsItems[n->type].push_back(n);
- }
-
- void DelNewsItem(NewsItem *n)
- {
- std::vector<NewsItem *> &list = this->GetNewsList(n->type);
- std::vector<NewsItem *>::iterator it = std::find(list.begin(), list.end(), n);
- if (it != list.end())
- list.erase(it);
- delete n;
- }
-
- std::vector<NewsItem *> &GetNewsList(NewsType t)
- {
- return this->newsItems[t];
- }
-};
-
-#define lenof(a) (sizeof(a) / sizeof(*(a)))
-static const char **findmsgs(NewsType type)
-{
- for (unsigned i = 0; i < lenof(msgarray); ++i)
- if (msgarray[i].type == type)
- return msgarray[i].msgs;
- return NULL;
-}
-
-class NewsBase : public Command
-{
- ServiceReference<NewsService> ns;
-
- protected:
- void DoList(CommandSource &source, NewsType ntype, const char **msgs)
- {
- std::vector<NewsItem *> &list = this->ns->GetNewsList(ntype);
- if (list.empty())
- source.Reply(msgs[MSG_LIST_NONE]);
- else
- {
- ListFormatter lflist(source.GetAccount());
- lflist.AddColumn(_("Number")).AddColumn(_("Creator")).AddColumn(_("Created")).AddColumn(_("Text"));
-
- for (unsigned i = 0, end = list.size(); i < end; ++i)
- {
- ListFormatter::ListEntry entry;
- entry["Number"] = stringify(i + 1);
- entry["Creator"] = list[i]->who;
- entry["Created"] = Anope::strftime(list[i]->time, NULL, true);
- entry["Text"] = list[i]->text;
- lflist.AddEntry(entry);
- }
-
- source.Reply(msgs[MSG_LIST_HEADER]);
-
- std::vector<Anope::string> replies;
- lflist.Process(replies);
-
- for (unsigned i = 0; i < replies.size(); ++i)
- source.Reply(replies[i]);
-
- source.Reply(_("End of news list."));
- }
-
- return;
- }
-
- void DoAdd(CommandSource &source, const std::vector<Anope::string> &params, NewsType ntype, const char **msgs)
- {
- const Anope::string text = params.size() > 1 ? params[1] : "";
-
- if (text.empty())
- this->OnSyntaxError(source, "ADD");
- else
- {
- if (Anope::ReadOnly)
- source.Reply(READ_ONLY_MODE);
-
- NewsItem *news = new MyNewsItem();
- news->type = ntype;
- news->text = text;
- news->time = Anope::CurTime;
- news->who = source.GetNick();
-
- this->ns->AddNewsItem(news);
-
- source.Reply(msgs[MSG_ADDED]);
- Log(LOG_ADMIN, source, this) << "to add a news item";
- }
-
- return;
- }
-
- void DoDel(CommandSource &source, const std::vector<Anope::string> &params, NewsType ntype, const char **msgs)
- {
- const Anope::string &text = params.size() > 1 ? params[1] : "";
-
- if (text.empty())
- this->OnSyntaxError(source, "DEL");
- else
- {
- std::vector<NewsItem *> &list = this->ns->GetNewsList(ntype);
- if (list.empty())
- source.Reply(msgs[MSG_LIST_NONE]);
- else
- {
- if (Anope::ReadOnly)
- source.Reply(READ_ONLY_MODE);
- if (!text.equals_ci("ALL"))
- {
- try
- {
- unsigned num = convertTo<unsigned>(text);
- if (num > 0 && num <= list.size())
- {
- this->ns->DelNewsItem(list[num - 1]);
- source.Reply(msgs[MSG_DELETED], num);
- Log(LOG_ADMIN, source, this) << "to delete a news item";
- return;
- }
- }
- catch (const ConvertException &) { }
-
- source.Reply(msgs[MSG_DEL_NOT_FOUND], text.c_str());
- }
- else
- {
- for (unsigned i = list.size(); i > 0; --i)
- this->ns->DelNewsItem(list[i - 1]);
- source.Reply(msgs[MSG_DELETED_ALL]);
- Log(LOG_ADMIN, source, this) << "to delete all news items";
- }
- }
- }
-
- return;
- }
-
- void DoNews(CommandSource &source, const std::vector<Anope::string> &params, NewsType ntype)
- {
- if (!this->ns)
- return;
-
- const Anope::string &cmd = params[0];
-
- const char **msgs = findmsgs(ntype);
- if (!msgs)
- throw CoreException("news: Invalid type to do_news()");
-
- if (cmd.equals_ci("LIST"))
- return this->DoList(source, ntype, msgs);
- else if (cmd.equals_ci("ADD"))
- return this->DoAdd(source, params, ntype, msgs);
- else if (cmd.equals_ci("DEL"))
- return this->DoDel(source, params, ntype, msgs);
- else
- this->OnSyntaxError(source, "");
-
- return;
- }
- public:
- NewsBase(Module *creator, const Anope::string &newstype) : Command(creator, newstype, 1, 2), ns("NewsService", "news")
- {
- this->SetSyntax(_("ADD \037text\037"));
- this->SetSyntax(_("DEL {\037num\037 | ALL}"));
- this->SetSyntax("LIST");
- }
-
- virtual ~NewsBase()
- {
- }
-
- virtual void Execute(CommandSource &source, const std::vector<Anope::string> &params) = 0;
-
- virtual bool OnHelp(CommandSource &source, const Anope::string &subcommand) = 0;
-};
-
-class CommandOSLogonNews : public NewsBase
-{
- public:
- CommandOSLogonNews(Module *creator) : NewsBase(creator, "operserv/logonnews")
- {
- this->SetDesc(_("Define messages to be shown to users at logon"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- return this->DoNews(source, params, NEWS_LOGON);
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Edits or displays the list of logon news messages. When a\n"
- "user connects to the network, these messages will be sent\n"
- "to them. However, no more than \002%d\002 messages will be\n"
- "sent in order to avoid flooding the user. If there are\n"
- "more news messages, only the most recent will be sent."),
- Config->GetModule(this->owner)->Get<unsigned>("newscount", "3"));
- return true;
- }
-};
-
-class CommandOSOperNews : public NewsBase
-{
- public:
- CommandOSOperNews(Module *creator) : NewsBase(creator, "operserv/opernews")
- {
- this->SetDesc(_("Define messages to be shown to users who oper"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- return this->DoNews(source, params, NEWS_OPER);
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Edits or displays the list of oper news messages. When a\n"
- "user opers up (with the /OPER command), these messages will\n"
- "be sent to them. However, no more than \002%d\002 messages will\n"
- "be sent in order to avoid flooding the user. If there are\n"
- "more news messages, only the most recent will be sent."),
- Config->GetModule(this->owner)->Get<unsigned>("newscount", "3"));
- return true;
- }
-};
-
-class CommandOSRandomNews : public NewsBase
-{
- public:
- CommandOSRandomNews(Module *creator) : NewsBase(creator, "operserv/randomnews")
- {
- this->SetDesc(_("Define messages to be randomly shown to users at logon"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- return this->DoNews(source, params, NEWS_RANDOM);
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Edits or displays the list of random news messages. When a\n"
- "user connects to the network, one (and only one) of the\n"
- "random news will be randomly chosen and sent to them."));
- return true;
- }
-};
-
-static unsigned cur_rand_news = 0;
-
-class OSNews : public Module
-{
- MyNewsService newsservice;
- Serialize::Type newsitem_type;
-
- CommandOSLogonNews commandoslogonnews;
- CommandOSOperNews commandosopernews;
- CommandOSRandomNews commandosrandomnews;
-
- Anope::string oper_announcer, announcer;
- unsigned news_count;
-
- void DisplayNews(User *u, NewsType Type)
- {
- std::vector<NewsItem *> &newsList = this->newsservice.GetNewsList(Type);
- if (newsList.empty())
- return;
-
- BotInfo *bi = NULL;
- if (Type == NEWS_OPER)
- bi = BotInfo::Find(Config->GetModule(this)->Get<const Anope::string>("oper_announcer", "OperServ"), true);
- else
- bi = BotInfo::Find(Config->GetModule(this)->Get<const Anope::string>("announcer", "Global"), true);
- if (bi == NULL)
- return;
-
- Anope::string msg;
- if (Type == NEWS_LOGON)
- msg = _("[\002Logon News\002 - %s] %s");
- else if (Type == NEWS_OPER)
- msg = _("[\002Oper News\002 - %s] %s");
- else if (Type == NEWS_RANDOM)
- msg = _("[\002Random News\002 - %s] %s");
-
- int start = 0;
-
- if (Type != NEWS_RANDOM)
- {
- start = newsList.size() - news_count;
- if (start < 0)
- start = 0;
- }
-
- for (unsigned i = start, end = newsList.size(); i < end; ++i)
- {
- if (Type == NEWS_RANDOM && i != cur_rand_news)
- continue;
-
- u->SendMessage(bi, msg.c_str(), Anope::strftime(newsList[i]->time, u->Account(), true).c_str(), newsList[i]->text.c_str());
-
- if (Type == NEWS_RANDOM)
- {
- ++cur_rand_news;
- break;
- }
- }
-
- /* Reset to head of list to get first random news value */
- if (Type == NEWS_RANDOM && cur_rand_news >= newsList.size())
- cur_rand_news = 0;
- }
-
- public:
- OSNews(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- newsservice(this), newsitem_type("NewsItem", MyNewsItem::Unserialize),
- commandoslogonnews(this), commandosopernews(this), commandosrandomnews(this)
- {
- }
-
- void OnReload(Configuration::Conf *conf) anope_override
- {
- oper_announcer = conf->GetModule(this)->Get<const Anope::string>("oper_announcer", "OperServ");
- announcer = conf->GetModule(this)->Get<const Anope::string>("announcer", "Global");
- news_count = conf->GetModule(this)->Get<unsigned>("newscount", "3");
- }
-
- void OnUserModeSet(const MessageSource &setter, User *u, const Anope::string &mname) anope_override
- {
- if (mname == "OPER")
- DisplayNews(u, NEWS_OPER);
- }
-
- void OnUserConnect(User *user, bool &) anope_override
- {
- if (user->Quitting() || !user->server->IsSynced())
- return;
-
- DisplayNews(user, NEWS_LOGON);
- DisplayNews(user, NEWS_RANDOM);
- }
-};
-
-MODULE_INIT(OSNews)
diff --git a/modules/commands/os_oper.cpp b/modules/commands/os_oper.cpp
deleted file mode 100644
index f5ac80160..000000000
--- a/modules/commands/os_oper.cpp
+++ /dev/null
@@ -1,293 +0,0 @@
-/* OperServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-struct MyOper : Oper, Serializable
-{
- MyOper(const Anope::string &n, OperType *o) : Oper(n, o), Serializable("Oper") { }
-
- void Serialize(Serialize::Data &data) const anope_override
- {
- data["name"] << this->name;
- data["type"] << this->ot->GetName();
- }
-
- static Serializable* Unserialize(Serializable *obj, Serialize::Data &data)
- {
- Anope::string stype, sname;
-
- data["type"] >> stype;
- data["name"] >> sname;
-
- OperType *ot = OperType::Find(stype);
- if (ot == NULL)
- return NULL;
- NickCore *nc = NickCore::Find(sname);
- if (nc == NULL)
- return NULL;
-
- MyOper *myo;
- if (obj)
- myo = anope_dynamic_static_cast<MyOper *>(obj);
- else
- myo = new MyOper(nc->display, ot);
- nc->o = myo;
- Log(LOG_NORMAL, "operserv/oper") << "Tied oper " << nc->display << " to type " << ot->GetName();
- return myo;
- }
-};
-
-class CommandOSOper : public Command
-{
- bool HasPrivs(CommandSource &source, OperType *ot) const
- {
- std::list<Anope::string> commands = ot->GetCommands(), privs = ot->GetPrivs();
-
- for (std::list<Anope::string>::iterator it = commands.begin(); it != commands.end(); ++it)
- if (!source.HasCommand(*it))
- return false;
-
- for (std::list<Anope::string>::iterator it = privs.begin(); it != privs.end(); ++it)
- if (!source.HasPriv(*it))
- return false;
-
- return true;
- }
-
- public:
- CommandOSOper(Module *creator) : Command(creator, "operserv/oper", 1, 3)
- {
- this->SetDesc(_("View and change Services Operators"));
- this->SetSyntax(_("ADD \037oper\037 \037type\037"));
- this->SetSyntax(_("DEL \037oper\037"));
- this->SetSyntax(_("INFO [\037type\037]"));
- this->SetSyntax("LIST");
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &subcommand = params[0];
-
- if (subcommand.equals_ci("ADD") && params.size() > 2)
- {
- const Anope::string &oper = params[1];
- const Anope::string &otype = params[2];
-
- if (!source.HasPriv("operserv/oper/modify"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- const NickAlias *na = NickAlias::Find(oper);
- if (na == NULL)
- source.Reply(NICK_X_NOT_REGISTERED, oper.c_str());
- else if (na->nc->o)
- source.Reply(_("Nick \002%s\002 is already an operator."), na->nick.c_str());
- else
- {
- OperType *ot = OperType::Find(otype);
- if (ot == NULL)
- {
- source.Reply(_("Oper type \002%s\002 has not been configured."), otype.c_str());
- return;
- }
-
- if (!HasPrivs(source, ot))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- na->nc->o = new MyOper(na->nc->display, ot);
- na->nc->o->require_oper = true;
-
- if (Anope::ReadOnly)
- source.Reply(READ_ONLY_MODE);
-
- Log(LOG_ADMIN, source, this) << "ADD " << na->nick << " as type " << ot->GetName();
- source.Reply("%s (%s) added to the \002%s\002 list.", na->nick.c_str(), na->nc->display.c_str(), ot->GetName().c_str());
- }
- }
- else if (subcommand.equals_ci("DEL") && params.size() > 1)
- {
- const Anope::string &oper = params[1];
-
- if (!source.HasPriv("operserv/oper/modify"))
- {
- source.Reply(ACCESS_DENIED);
- return;
- }
-
- const NickAlias *na = NickAlias::Find(oper);
- if (na == NULL)
- source.Reply(NICK_X_NOT_REGISTERED, oper.c_str());
- else if (!na->nc || !na->nc->o)
- source.Reply(_("Nick \002%s\002 is not a Services Operator."), oper.c_str());
- else if (!HasPrivs(source, na->nc->o->ot))
- source.Reply(ACCESS_DENIED);
- else if (std::find(Config->Opers.begin(), Config->Opers.end(), na->nc->o) != Config->Opers.end())
- source.Reply(_("Oper \002%s\002 is configured in the configuration file(s) and can not be removed by this command."), na->nc->display.c_str());
- else
- {
- delete na->nc->o;
- na->nc->o = NULL;
-
- if (Anope::ReadOnly)
- source.Reply(READ_ONLY_MODE);
-
- Log(LOG_ADMIN, source, this) << "DEL " << na->nick;
- source.Reply(_("Oper privileges removed from %s (%s)."), na->nick.c_str(), na->nc->display.c_str());
- }
- }
- else if (subcommand.equals_ci("LIST"))
- {
- source.Reply(_("Name Type"));
- for (nickcore_map::const_iterator it = NickCoreList->begin(), it_end = NickCoreList->end(); it != it_end; ++it)
- {
- const NickCore *nc = it->second;
-
- if (!nc->o)
- continue;
-
- source.Reply(_("%-8s %s"), nc->o->name.c_str(), nc->o->ot->GetName().c_str());
- if (std::find(Config->Opers.begin(), Config->Opers.end(), nc->o) != Config->Opers.end())
- source.Reply(_(" This oper is configured in the configuration file."));
- for (std::list<User *>::const_iterator uit = nc->users.begin(); uit != nc->users.end(); ++uit)
- {
- User *u = *uit;
- source.Reply(_(" %s is online using this oper block."), u->nick.c_str());
- }
- }
- }
- else if (subcommand.equals_ci("INFO"))
- {
- if (params.size() < 2)
- {
- source.Reply(_("Available opertypes:"));
- for (unsigned i = 0; i < Config->MyOperTypes.size(); ++i)
- {
- OperType *ot = Config->MyOperTypes[i];
- source.Reply("%s", ot->GetName().c_str());
- }
- return;
- }
-
- Anope::string fulltype = params[1];
- if (params.size() > 2)
- fulltype += " " + params[2];
- OperType *ot = OperType::Find(fulltype);
- if (ot == NULL)
- source.Reply(_("Oper type \002%s\002 has not been configured."), fulltype.c_str());
- else
- {
- if (ot->GetCommands().empty())
- source.Reply(_("Opertype \002%s\002 has no allowed commands."), ot->GetName().c_str());
- else
- {
- source.Reply(_("Available commands for \002%s\002:"), ot->GetName().c_str());
- Anope::string buf;
- std::list<Anope::string> cmds = ot->GetCommands();
- for (std::list<Anope::string>::const_iterator it = cmds.begin(), it_end = cmds.end(); it != it_end; ++it)
- {
- buf += *it + " ";
- if (buf.length() > 400)
- {
- source.Reply("%s", buf.c_str());
- buf.clear();
- }
- }
- if (!buf.empty())
- {
- source.Reply("%s", buf.c_str());
- buf.clear();
- }
- }
- if (ot->GetPrivs().empty())
- source.Reply(_("Opertype \002%s\002 has no allowed privileges."), ot->GetName().c_str());
- else
- {
- source.Reply(_("Available privileges for \002%s\002:"), ot->GetName().c_str());
- Anope::string buf;
- std::list<Anope::string> privs = ot->GetPrivs();
- for (std::list<Anope::string>::const_iterator it = privs.begin(), it_end = privs.end(); it != it_end; ++it)
- {
- buf += *it + " ";
- if (buf.length() > 400)
- {
- source.Reply("%s", buf.c_str());
- buf.clear();
- }
- }
- if (!buf.empty())
- {
- source.Reply("%s", buf.c_str());
- buf.clear();
- }
- }
- if (!ot->modes.empty())
- source.Reply(_("Opertype \002%s\002 receives modes \002%s\002 once identified."), ot->GetName().c_str(), ot->modes.c_str());
- }
- }
- else
- this->OnSyntaxError(source, subcommand);
-
- return;
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Allows you to change and view Services Operators.\n"
- "Note that operators removed by this command but are still set in\n"
- "the configuration file are not permanently affected by this."));
- return true;
- }
-};
-
-class OSOper : public Module
-{
- Serialize::Type myoper_type;
- CommandOSOper commandosoper;
-
- public:
- OSOper(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- myoper_type("Oper", MyOper::Unserialize), commandosoper(this)
- {
- }
-
- ~OSOper()
- {
- for (nickcore_map::const_iterator it = NickCoreList->begin(), it_end = NickCoreList->end(); it != it_end; ++it)
- {
- NickCore *nc = it->second;
-
- if (nc->o && dynamic_cast<MyOper *>(nc->o))
- {
- delete nc->o;
- nc->o = NULL;
- }
- }
- }
-
- void OnDelCore(NickCore *nc) anope_override
- {
- if (nc->o && dynamic_cast<MyOper *>(nc->o))
- {
- delete nc->o;
- nc->o = NULL;
- }
- }
-};
-
-MODULE_INIT(OSOper)
diff --git a/modules/commands/os_reload.cpp b/modules/commands/os_reload.cpp
deleted file mode 100644
index 1ab3c3c87..000000000
--- a/modules/commands/os_reload.cpp
+++ /dev/null
@@ -1,67 +0,0 @@
-/* OperServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class CommandOSReload : public Command
-{
- public:
- CommandOSReload(Module *creator) : Command(creator, "operserv/reload", 0, 0)
- {
- this->SetDesc(_("Reload services' configuration file"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- try
- {
- Log(LOG_ADMIN, source, this);
-
- Configuration::Conf *new_config = new Configuration::Conf();
- Configuration::Conf *old = Config;
- Config = new_config;
- Config->Post(old);
- delete old;
-
- source.Reply(_("Services' configuration has been reloaded."));
- }
- catch (const ConfigException &ex)
- {
- Log(this->owner) << "Error reloading configuration file: " << ex.GetReason();
- source.Reply(_("Error reloading configuration file: %s"), ex.GetReason().c_str());
- }
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Causes Services to reload the configuration file. Note that\n"
- "some directives still need the restart of the Services to\n"
- "take effect (such as Services' nicknames, activation of the\n"
- "session limitation, etc.)."));
- return true;
- }
-};
-
-class OSReload : public Module
-{
- CommandOSReload commandosreload;
-
- public:
- OSReload(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandosreload(this)
- {
-
- }
-};
-
-MODULE_INIT(OSReload)
diff --git a/modules/commands/os_session.cpp b/modules/commands/os_session.cpp
deleted file mode 100644
index e8f1e241a..000000000
--- a/modules/commands/os_session.cpp
+++ /dev/null
@@ -1,737 +0,0 @@
-/* OperServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-#include "modules/os_session.h"
-
-namespace
-{
- /* The default session limit */
- unsigned session_limit;
- /* How many times to kill before adding an AKILL */
- unsigned max_session_kill;
- /* How long session akills should last */
- time_t session_autokill_expiry;
- /* Reason to use for session kills */
- Anope::string sle_reason;
- /* Optional second reason */
- Anope::string sle_detailsloc;
-
- /* Max limit that can be used for exceptions */
- unsigned max_exception_limit;
- /* How long before exceptions expire by default */
- time_t exception_expiry;
-
- /* Number of bits to use when comparing session IPs */
- unsigned ipv4_cidr;
- unsigned ipv6_cidr;
-}
-
-class MySessionService : public SessionService
-{
- SessionMap Sessions;
- Serialize::Checker<ExceptionVector> Exceptions;
- public:
- MySessionService(Module *m) : SessionService(m), Exceptions("Exception") { }
-
- Exception *CreateException() anope_override
- {
- return new Exception();
- }
-
- void AddException(Exception *e) anope_override
- {
- this->Exceptions->push_back(e);
- }
-
- void DelException(Exception *e) anope_override
- {
- ExceptionVector::iterator it = std::find(this->Exceptions->begin(), this->Exceptions->end(), e);
- if (it != this->Exceptions->end())
- this->Exceptions->erase(it);
- }
-
- Exception *FindException(User *u) anope_override
- {
- for (std::vector<Exception *>::const_iterator it = this->Exceptions->begin(), it_end = this->Exceptions->end(); it != it_end; ++it)
- {
- Exception *e = *it;
- if (Anope::Match(u->host, e->mask) || Anope::Match(u->ip.addr(), e->mask))
- return e;
-
- if (cidr(e->mask).match(u->ip))
- return e;
- }
- return NULL;
- }
-
- Exception *FindException(const Anope::string &host) anope_override
- {
- for (std::vector<Exception *>::const_iterator it = this->Exceptions->begin(), it_end = this->Exceptions->end(); it != it_end; ++it)
- {
- Exception *e = *it;
- if (Anope::Match(host, e->mask))
- return e;
-
- if (cidr(e->mask).match(sockaddrs(host)))
- return e;
- }
-
- return NULL;
- }
-
- ExceptionVector &GetExceptions() anope_override
- {
- return this->Exceptions;
- }
-
- void DelSession(Session *s)
- {
- this->Sessions.erase(s->addr);
- }
-
- Session *FindSession(const Anope::string &ip) anope_override
- {
- cidr c(ip, ip.find(':') != Anope::string::npos ? ipv6_cidr : ipv4_cidr);
- if (!c.valid())
- return NULL;
- SessionMap::iterator it = this->Sessions.find(c);
- if (it != this->Sessions.end())
- return it->second;
- return NULL;
- }
-
- SessionMap::iterator FindSessionIterator(const sockaddrs &ip)
- {
- cidr c(ip, ip.ipv6() ? ipv6_cidr : ipv4_cidr);
- if (!c.valid())
- return this->Sessions.end();
- return this->Sessions.find(c);
- }
-
- Session* &FindOrCreateSession(const cidr &ip)
- {
- return this->Sessions[ip];
- }
-
- SessionMap &GetSessions() anope_override
- {
- return this->Sessions;
- }
-};
-
-class ExceptionDelCallback : public NumberList
-{
- protected:
- CommandSource &source;
- unsigned deleted;
- Command *cmd;
- public:
- ExceptionDelCallback(CommandSource &_source, const Anope::string &numlist, Command *c) : NumberList(numlist, true), source(_source), deleted(0), cmd(c)
- {
- }
-
- ~ExceptionDelCallback()
- {
- if (!deleted)
- source.Reply(_("No matching entries on session-limit exception list."));
- else if (deleted == 1)
- source.Reply(_("Deleted 1 entry from session-limit exception list."));
- else
- source.Reply(_("Deleted %d entries from session-limit exception list."), deleted);
- }
-
- virtual void HandleNumber(unsigned number) anope_override
- {
- if (!number || number > session_service->GetExceptions().size())
- return;
-
- Log(LOG_ADMIN, source, cmd) << "to remove the session limit exception for " << session_service->GetExceptions()[number - 1]->mask;
-
- ++deleted;
- DoDel(source, number - 1);
- }
-
- static void DoDel(CommandSource &source, unsigned index)
- {
- Exception *e = session_service->GetExceptions()[index];
- FOREACH_MOD(OnExceptionDel, (source, e));
-
- session_service->DelException(e);
- delete e;
- }
-};
-
-class CommandOSSession : public Command
-{
- private:
- void DoList(CommandSource &source, const std::vector<Anope::string> &params)
- {
- Anope::string param = params[1];
-
- unsigned mincount = 0;
- try
- {
- mincount = convertTo<unsigned>(param);
- }
- catch (const ConvertException &) { }
-
- if (mincount <= 1)
- source.Reply(_("Invalid threshold value. It must be a valid integer greater than 1."));
- else
- {
- ListFormatter list(source.GetAccount());
- list.AddColumn(_("Session")).AddColumn(_("Host"));
-
- for (SessionService::SessionMap::iterator it = session_service->GetSessions().begin(), it_end = session_service->GetSessions().end(); it != it_end; ++it)
- {
- Session *session = it->second;
-
- if (session->count >= mincount)
- {
- ListFormatter::ListEntry entry;
- entry["Session"] = stringify(session->count);
- entry["Host"] = session->addr.mask();
- list.AddEntry(entry);
- }
- }
-
- source.Reply(_("Hosts with at least \002%d\002 sessions:"), mincount);
-
- std::vector<Anope::string> replies;
- list.Process(replies);
-
-
- for (unsigned i = 0; i < replies.size(); ++i)
- source.Reply(replies[i]);
- }
-
- return;
- }
-
- void DoView(CommandSource &source, const std::vector<Anope::string> &params)
- {
- Anope::string param = params[1];
- Session *session = session_service->FindSession(param);
-
- Exception *exception = session_service->FindException(param);
- Anope::string entry = "no entry";
- unsigned limit = session_limit;
- if (exception)
- {
- if (!exception->limit)
- limit = 0;
- else if (exception->limit > limit)
- limit = exception->limit;
- entry = exception->mask;
- }
-
- if (!session)
- source.Reply(_("\002%s\002 not found on session list, but has a limit of \002%d\002 because it matches entry: \002%s\002."), param.c_str(), limit, entry.c_str());
- else
- source.Reply(_("The host \002%s\002 currently has \002%d\002 sessions with a limit of \002%d\002 because it matches entry: \002%s\002."), session->addr.mask().c_str(), session->count, limit, entry.c_str());
- }
- public:
- CommandOSSession(Module *creator) : Command(creator, "operserv/session", 2, 2)
- {
- this->SetDesc(_("View the list of host sessions"));
- this->SetSyntax(_("LIST \037threshold\037"));
- this->SetSyntax(_("VIEW \037host\037"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &cmd = params[0];
-
- Log(LOG_ADMIN, source, this) << cmd << " " << params[1];
-
- if (!session_limit)
- source.Reply(_("Session limiting is disabled."));
- else if (cmd.equals_ci("LIST"))
- return this->DoList(source, params);
- else if (cmd.equals_ci("VIEW"))
- return this->DoView(source, params);
- else
- this->OnSyntaxError(source, "");
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Allows Services Operators to view the session list.\n"
- " \n"
- "\002SESSION LIST\002 lists hosts with at least \037threshold\037 sessions.\n"
- "The threshold must be a number greater than 1. This is to\n"
- "prevent accidental listing of the large number of single\n"
- "session hosts.\n"
- " \n"
- "\002SESSION VIEW\002 displays detailed information about a specific\n"
- "host - including the current session count and session limit.\n"
- "The \037host\037 value may not include wildcards.\n"
- " \n"
- "See the \002EXCEPTION\002 help for more information about session\n"
- "limiting and how to set session limits specific to certain\n"
- "hosts and groups thereof."));
- return true;
- }
-};
-
-class CommandOSException : public Command
-{
- private:
- void DoAdd(CommandSource &source, const std::vector<Anope::string> &params)
- {
- Anope::string mask, expiry, limitstr;
- unsigned last_param = 3;
-
- mask = params.size() > 1 ? params[1] : "";
- if (!mask.empty() && mask[0] == '+')
- {
- expiry = mask;
- mask = params.size() > 2 ? params[2] : "";
- last_param = 4;
- }
-
- limitstr = params.size() > last_param - 1 ? params[last_param - 1] : "";
-
- if (params.size() <= last_param)
- {
- this->OnSyntaxError(source, "ADD");
- return;
- }
-
- Anope::string reason = params[last_param];
- if (last_param == 3 && params.size() > 4)
- reason += " " + params[4];
- if (reason.empty())
- {
- this->OnSyntaxError(source, "ADD");
- return;
- }
-
- time_t expires = !expiry.empty() ? Anope::DoTime(expiry) : exception_expiry;
- if (expires < 0)
- {
- source.Reply(BAD_EXPIRY_TIME);
- return;
- }
- else if (expires > 0)
- expires += Anope::CurTime;
-
- unsigned limit = -1;
- try
- {
- limit = convertTo<unsigned>(limitstr);
- }
- catch (const ConvertException &) { }
-
- if (limit > max_exception_limit)
- {
- source.Reply(_("Invalid session limit. It must be a valid integer greater than or equal to zero and less than \002%d\002."), max_exception_limit);
- return;
- }
- else
- {
- if (mask.find('!') != Anope::string::npos || mask.find('@') != Anope::string::npos)
- {
- source.Reply(_("Invalid hostmask. Only real hostmasks are valid, as exceptions are not matched against nicks or usernames."));
- return;
- }
-
- for (std::vector<Exception *>::iterator it = session_service->GetExceptions().begin(), it_end = session_service->GetExceptions().end(); it != it_end; ++it)
- {
- Exception *e = *it;
- if (e->mask.equals_ci(mask))
- {
- if (e->limit != limit)
- {
- e->limit = limit;
- source.Reply(_("Exception for \002%s\002 has been updated to %d."), mask.c_str(), e->limit);
- }
- else
- source.Reply(_("\002%s\002 already exists on the EXCEPTION list."), mask.c_str());
- return;
- }
- }
-
- Exception *exception = new Exception();
- exception->mask = mask;
- exception->limit = limit;
- exception->reason = reason;
- exception->time = Anope::CurTime;
- exception->who = source.GetNick();
- exception->expires = expires;
-
- EventReturn MOD_RESULT;
- FOREACH_RESULT(OnExceptionAdd, MOD_RESULT, (exception));
- if (MOD_RESULT == EVENT_STOP)
- delete exception;
- else
- {
- Log(LOG_ADMIN, source, this) << "to set the session limit for " << mask << " to " << limit;
- session_service->AddException(exception);
- source.Reply(_("Session limit for \002%s\002 set to \002%d\002."), mask.c_str(), limit);
- if (Anope::ReadOnly)
- source.Reply(READ_ONLY_MODE);
- }
- }
-
- return;
- }
-
- void DoDel(CommandSource &source, const std::vector<Anope::string> &params)
- {
- const Anope::string &mask = params.size() > 1 ? params[1] : "";
-
- if (mask.empty())
- {
- this->OnSyntaxError(source, "DEL");
- return;
- }
-
- if (isdigit(mask[0]) && mask.find_first_not_of("1234567890,-") == Anope::string::npos)
- {
- ExceptionDelCallback list(source, mask, this);
- list.Process();
- }
- else
- {
- unsigned i = 0, end = session_service->GetExceptions().size();
- for (; i < end; ++i)
- if (mask.equals_ci(session_service->GetExceptions()[i]->mask))
- {
- Log(LOG_ADMIN, source, this) << "to remove the session limit exception for " << mask;
- ExceptionDelCallback::DoDel(source, i);
- source.Reply(_("\002%s\002 deleted from session-limit exception list."), mask.c_str());
- break;
- }
- if (i == end)
- source.Reply(_("\002%s\002 not found on session-limit exception list."), mask.c_str());
- }
-
- if (Anope::ReadOnly)
- source.Reply(READ_ONLY_MODE);
-
- return;
- }
-
- void ProcessList(CommandSource &source, const std::vector<Anope::string> &params, ListFormatter &list)
- {
- const Anope::string &mask = params.size() > 1 ? params[1] : "";
-
- if (session_service->GetExceptions().empty())
- {
- source.Reply(_("The session exception list is empty."));
- return;
- }
-
- if (!mask.empty() && mask.find_first_not_of("1234567890,-") == Anope::string::npos)
- {
- class ExceptionListCallback : public NumberList
- {
- CommandSource &source;
- ListFormatter &list;
- public:
- ExceptionListCallback(CommandSource &_source, ListFormatter &_list, const Anope::string &numlist) : NumberList(numlist, false), source(_source), list(_list)
- {
- }
-
- void HandleNumber(unsigned Number) anope_override
- {
- if (!Number || Number > session_service->GetExceptions().size())
- return;
-
- Exception *e = session_service->GetExceptions()[Number - 1];
-
- ListFormatter::ListEntry entry;
- entry["Number"] = stringify(Number);
- entry["Mask"] = e->mask;
- entry["By"] = e->who;
- entry["Created"] = Anope::strftime(e->time, NULL, true);
- entry["Expires"] = Anope::Expires(e->expires, source.GetAccount());
- entry["Limit"] = stringify(e->limit);
- entry["Reason"] = e->reason;
- this->list.AddEntry(entry);
- }
- }
- nl_list(source, list, mask);
- nl_list.Process();
- }
- else
- {
- for (unsigned i = 0, end = session_service->GetExceptions().size(); i < end; ++i)
- {
- Exception *e = session_service->GetExceptions()[i];
- if (mask.empty() || Anope::Match(e->mask, mask))
- {
- ListFormatter::ListEntry entry;
- entry["Number"] = stringify(i + 1);
- entry["Mask"] = e->mask;
- entry["By"] = e->who;
- entry["Created"] = Anope::strftime(e->time, NULL, true);
- entry["Expires"] = Anope::Expires(e->expires, source.GetAccount());
- entry["Limit"] = stringify(e->limit);
- entry["Reason"] = e->reason;
- list.AddEntry(entry);
- }
- }
- }
-
- if (list.IsEmpty())
- source.Reply(_("No matching entries on session-limit exception list."));
- else
- {
- source.Reply(_("Current Session Limit Exception list:"));
-
- std::vector<Anope::string> replies;
- list.Process(replies);
-
- for (unsigned i = 0; i < replies.size(); ++i)
- source.Reply(replies[i]);
- }
- }
-
- void DoList(CommandSource &source, const std::vector<Anope::string> &params)
- {
- ListFormatter list(source.GetAccount());
- list.AddColumn(_("Number")).AddColumn(_("Limit")).AddColumn(_("Mask"));
-
- this->ProcessList(source, params, list);
- }
-
- void DoView(CommandSource &source, const std::vector<Anope::string> &params)
- {
- ListFormatter list(source.GetAccount());
- list.AddColumn(_("Number")).AddColumn(_("Mask")).AddColumn(_("By")).AddColumn(_("Created")).AddColumn(_("Expires")).AddColumn(_("Limit")).AddColumn(_("Reason"));
-
- this->ProcessList(source, params, list);
- }
-
- public:
- CommandOSException(Module *creator) : Command(creator, "operserv/exception", 1, 5)
- {
- this->SetDesc(_("Modify the session-limit exception list"));
- this->SetSyntax(_("ADD [\037+expiry\037] \037mask\037 \037limit\037 \037reason\037"));
- this->SetSyntax(_("DEL {\037mask\037 | \037entry-num\037 | \037list\037}"));
- this->SetSyntax(_("LIST [\037mask\037 | \037list\037]"));
- this->SetSyntax(_("VIEW [\037mask\037 | \037list\037]"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &cmd = params[0];
-
- if (!session_limit)
- source.Reply(_("Session limiting is disabled."));
- else if (cmd.equals_ci("ADD"))
- return this->DoAdd(source, params);
- else if (cmd.equals_ci("DEL"))
- return this->DoDel(source, params);
- else if (cmd.equals_ci("LIST"))
- return this->DoList(source, params);
- else if (cmd.equals_ci("VIEW"))
- return this->DoView(source, params);
- else
- this->OnSyntaxError(source, "");
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Allows Services Operators to manipulate the list of hosts that\n"
- "have specific session limits - allowing certain machines,\n"
- "such as shell servers, to carry more than the default number\n"
- "of clients at a time. Once a host reaches its session limit,\n"
- "all clients attempting to connect from that host will be\n"
- "killed. Before the user is killed, they are notified, of a\n"
- "source of help regarding session limiting. The content of\n"
- "this notice is a config setting."));
- source.Reply(" ");
- source.Reply(_("\002EXCEPTION ADD\002 adds the given host mask to the exception list.\n"
- "Note that \002nick!user@host\002 and \002user@host\002 masks are invalid!\n"
- "Only real host masks, such as \002box.host.dom\002 and \002*.host.dom\002,\n"
- "are allowed because sessions limiting does not take nick or\n"
- "user names into account. \037limit\037 must be a number greater than\n"
- "or equal to zero. This determines how many sessions this host\n"
- "may carry at a time. A value of zero means the host has an\n"
- "unlimited session limit. See the \002AKILL\002 help for details about\n"
- "the format of the optional \037expiry\037 parameter.\n"
- " \n"
- "\002EXCEPTION DEL\002 removes the given mask from the exception list.\n"
- " \n"
- "\002EXCEPTION LIST\002 and \002EXCEPTION VIEW\002 show all current\n"
- "sessions if the optional mask is given, the list is limited\n"
- "to those sessions matching the mask. The difference is that\n"
- "\002EXCEPTION VIEW\002 is more verbose, displaying the name of the\n"
- "person who added the exception, its session limit, reason,\n"
- "host mask and the expiry date and time.\n"
- " \n"
- "Note that a connecting client will \"use\" the first exception\n"
- "their host matches."));
- return true;
- }
-};
-
-class OSSession : public Module
-{
- Serialize::Type exception_type;
- MySessionService ss;
- CommandOSSession commandossession;
- CommandOSException commandosexception;
- ServiceReference<XLineManager> akills;
-
- public:
- OSSession(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- exception_type("Exception", Exception::Unserialize), ss(this), commandossession(this), commandosexception(this), akills("XLineManager", "xlinemanager/sgline")
- {
- this->SetPermanent(true);
- }
-
- void Prioritize() anope_override
- {
- ModuleManager::SetPriority(this, PRIORITY_FIRST);
- }
-
- void OnReload(Configuration::Conf *conf) anope_override
- {
- Configuration::Block *block = Config->GetModule(this);
-
- session_limit = block->Get<int>("defaultsessionlimit");
- max_session_kill = block->Get<int>("maxsessionkill");
- session_autokill_expiry = block->Get<time_t>("sessionautokillexpiry");
- sle_reason = block->Get<const Anope::string>("sessionlimitexceeded");
- sle_detailsloc = block->Get<const Anope::string>("sessionlimitdetailsloc");
-
- max_exception_limit = block->Get<int>("maxsessionlimit");
- exception_expiry = block->Get<time_t>("exceptionexpiry");
-
- ipv4_cidr = block->Get<unsigned>("session_ipv4_cidr", "32");
- ipv6_cidr = block->Get<unsigned>("session_ipv6_cidr", "128");
-
- if (ipv4_cidr > 32 || ipv6_cidr > 128)
- throw ConfigException(this->name + ": session CIDR value out of range");
- }
-
- void OnUserConnect(User *u, bool &exempt) anope_override
- {
- if (u->Quitting() || !session_limit || exempt || !u->server || u->server->IsULined())
- return;
-
- cidr u_ip(u->ip, u->ip.ipv6() ? ipv6_cidr : ipv4_cidr);
- if (!u_ip.valid())
- return;
-
- Session* &session = this->ss.FindOrCreateSession(u_ip);
-
- if (session)
- {
- bool kill = false;
- if (session->count >= session_limit)
- {
- kill = true;
- Exception *exception = this->ss.FindException(u);
- if (exception)
- {
- kill = false;
- if (exception->limit && session->count >= exception->limit)
- kill = true;
- }
- }
-
- /* Previously on IRCds that send a QUIT (InspIRCD) when a user is killed, the session for a host was
- * decremented in do_quit, which caused problems and fixed here
- *
- * Now, we create the user struture before calling this to fix some user tracking issues,
- * so we must increment this here no matter what because it will either be
- * decremented when the user is killed or quits - Adam
- */
- ++session->count;
-
- if (kill && !exempt)
- {
- BotInfo *OperServ = Config->GetClient("OperServ");
- if (OperServ)
- {
- if (!sle_reason.empty())
- {
- Anope::string message = sle_reason.replace_all_cs("%IP%", u->ip.addr());
- u->SendMessage(OperServ, message);
- }
- if (!sle_detailsloc.empty())
- u->SendMessage(OperServ, sle_detailsloc);
- }
-
- ++session->hits;
-
- const Anope::string &akillmask = "*@" + session->addr.mask();
- if (max_session_kill && session->hits >= max_session_kill && akills && !akills->HasEntry(akillmask))
- {
- XLine *x = new XLine(akillmask, OperServ ? OperServ->nick : "", Anope::CurTime + session_autokill_expiry, "Session limit exceeded", XLineManager::GenerateUID());
- akills->AddXLine(x);
- akills->Send(NULL, x);
- Log(OperServ, "akill/session") << "Added a temporary AKILL for \002" << akillmask << "\002 due to excessive connections";
- }
- else
- {
- u->Kill(OperServ, "Session limit exceeded");
- }
- }
- }
- else
- {
- session = new Session(u->ip, u->ip.ipv6() ? ipv6_cidr : ipv4_cidr);
- }
- }
-
- void OnUserQuit(User *u, const Anope::string &msg) anope_override
- {
- if (!session_limit || !u->server || u->server->IsULined())
- return;
-
- SessionService::SessionMap &sessions = this->ss.GetSessions();
- SessionService::SessionMap::iterator sit = this->ss.FindSessionIterator(u->ip);
-
- if (sit == sessions.end())
- return;
-
- Session *session = sit->second;
-
- if (session->count > 1)
- {
- --session->count;
- return;
- }
-
- delete session;
- sessions.erase(sit);
- }
-
- void OnExpireTick() anope_override
- {
- if (Anope::NoExpire)
- return;
- for (unsigned i = this->ss.GetExceptions().size(); i > 0; --i)
- {
- Exception *e = this->ss.GetExceptions()[i - 1];
-
- if (!e->expires || e->expires > Anope::CurTime)
- continue;
- BotInfo *OperServ = Config->GetClient("OperServ");
- Log(OperServ, "expire/exception") << "Session exception for " << e->mask << " has expired.";
- this->ss.DelException(e);
- delete e;
- }
- }
-};
-
-MODULE_INIT(OSSession)
diff --git a/modules/commands/os_stats.cpp b/modules/commands/os_stats.cpp
deleted file mode 100644
index 306100cea..000000000
--- a/modules/commands/os_stats.cpp
+++ /dev/null
@@ -1,276 +0,0 @@
-/* OperServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-#include "modules/os_session.h"
-
-struct Stats : Serializable
-{
- static Stats *me;
-
- Stats() : Serializable("Stats")
- {
- me = this;
- }
-
- void Serialize(Serialize::Data &data) const anope_override
- {
- data["maxusercnt"] << MaxUserCount;
- data["maxusertime"] << MaxUserTime;
- }
-
- static Serializable* Unserialize(Serializable *obj, Serialize::Data &data)
- {
- data["maxusercnt"] >> MaxUserCount;
- data["maxusertime"] >> MaxUserTime;
- return me;
- }
-};
-
-Stats *Stats::me;
-
-/**
- * Count servers connected to server s
- * @param s The server to start counting from
- * @return Amount of servers connected to server s
- **/
-static int stats_count_servers(Server *s)
-{
- if (!s)
- return 0;
-
- int count = 1;
-
- if (!s->GetLinks().empty())
- for (unsigned i = 0, j = s->GetLinks().size(); i < j; ++i)
- count += stats_count_servers(s->GetLinks()[i]);
-
- return count;
-}
-
-class CommandOSStats : public Command
-{
- ServiceReference<XLineManager> akills, snlines, sqlines;
- private:
- void DoStatsAkill(CommandSource &source)
- {
- int timeout;
- if (akills)
- {
- /* AKILLs */
- source.Reply(_("Current number of AKILLs: \002%d\002"), akills->GetCount());
- timeout = Config->GetModule("operserv")->Get<time_t>("autokillexpiry", "30d") + 59;
- if (timeout >= 172800)
- source.Reply(_("Default AKILL expiry time: \002%d days\002"), timeout / 86400);
- else if (timeout >= 86400)
- source.Reply(_("Default AKILL expiry time: \0021 day\002"));
- else if (timeout >= 7200)
- source.Reply(_("Default AKILL expiry time: \002%d hours\002"), timeout / 3600);
- else if (timeout >= 3600)
- source.Reply(_("Default AKILL expiry time: \0021 hour\002"));
- else if (timeout >= 120)
- source.Reply(_("Default AKILL expiry time: \002%d minutes\002"), timeout / 60);
- else if (timeout >= 60)
- source.Reply(_("Default AKILL expiry time: \0021 minute\002"));
- else
- source.Reply(_("Default AKILL expiry time: \002No expiration\002"));
- }
- if (snlines)
- {
- /* SNLINEs */
- source.Reply(_("Current number of SNLINEs: \002%d\002"), snlines->GetCount());
- timeout = Config->GetModule("operserv")->Get<time_t>("snlineexpiry", "30d") + 59;
- if (timeout >= 172800)
- source.Reply(_("Default SNLINE expiry time: \002%d days\002"), timeout / 86400);
- else if (timeout >= 86400)
- source.Reply(_("Default SNLINE expiry time: \0021 day\002"));
- else if (timeout >= 7200)
- source.Reply(_("Default SNLINE expiry time: \002%d hours\002"), timeout / 3600);
- else if (timeout >= 3600)
- source.Reply(_("Default SNLINE expiry time: \0021 hour\002"));
- else if (timeout >= 120)
- source.Reply(_("Default SNLINE expiry time: \002%d minutes\002"), timeout / 60);
- else if (timeout >= 60)
- source.Reply(_("Default SNLINE expiry time: \0021 minute\002"));
- else
- source.Reply(_("Default SNLINE expiry time: \002No expiration\002"));
- }
- if (sqlines)
- {
- /* SQLINEs */
- source.Reply(_("Current number of SQLINEs: \002%d\002"), sqlines->GetCount());
- timeout = Config->GetModule("operserv")->Get<time_t>("sglineexpiry", "30d") + 59;
- if (timeout >= 172800)
- source.Reply(_("Default SQLINE expiry time: \002%d days\002"), timeout / 86400);
- else if (timeout >= 86400)
- source.Reply(_("Default SQLINE expiry time: \0021 day\002"));
- else if (timeout >= 7200)
- source.Reply(_("Default SQLINE expiry time: \002%d hours\002"), timeout / 3600);
- else if (timeout >= 3600)
- source.Reply(_("Default SQLINE expiry time: \0021 hour\002"));
- else if (timeout >= 120)
- source.Reply(_("Default SQLINE expiry time: \002%d minutes\002"), timeout / 60);
- else if (timeout >= 60)
- source.Reply(_("Default SQLINE expiry time: \0021 minute\002"));
- else
- source.Reply(_("Default SQLINE expiry time: \002No expiration\002"));
- }
- }
-
- void DoStatsReset(CommandSource &source)
- {
- MaxUserCount = UserListByNick.size();
- source.Reply(_("Statistics reset."));
- return;
- }
-
- void DoStatsUptime(CommandSource &source)
- {
- time_t uptime = Anope::CurTime - Anope::StartTime;
- source.Reply(_("Current users: \002%d\002 (\002%d\002 ops)"), UserListByNick.size(), OperCount);
- source.Reply(_("Maximum users: \002%d\002 (%s)"), MaxUserCount, Anope::strftime(MaxUserTime, source.GetAccount()).c_str());
- source.Reply(_("Services up %s."), Anope::Duration(uptime, source.GetAccount()).c_str());
-
- return;
- }
-
- void DoStatsUplink(CommandSource &source)
- {
- Anope::string buf;
- for (std::set<Anope::string>::iterator it = Servers::Capab.begin(); it != Servers::Capab.end(); ++it)
- buf += " " + *it;
- if (!buf.empty())
- buf.erase(buf.begin());
-
- source.Reply(_("Uplink server: %s"), Me->GetLinks().front()->GetName().c_str());
- source.Reply(_("Uplink capab: %s"), buf.c_str());
- source.Reply(_("Servers found: %d"), stats_count_servers(Me->GetLinks().front()));
- return;
- }
-
- template<typename T> void GetHashStats(const T& map, size_t& entries, size_t& buckets, size_t& max_chain)
- {
- entries = map.size(), buckets = map.bucket_count(), max_chain = 0;
- for (size_t i = 0; i < buckets; ++i)
- if (map.bucket_size(i) > max_chain)
- max_chain = map.bucket_size(i);
- }
-
- void DoStatsHash(CommandSource &source)
- {
- size_t entries, buckets, max_chain;
-
- GetHashStats(UserListByNick, entries, buckets, max_chain);
- source.Reply(_("Users (nick): %lu entries, %lu buckets, longest chain is %d"), entries, buckets, max_chain);
-
- if (!UserListByUID.empty())
- {
- GetHashStats(UserListByUID, entries, buckets, max_chain);
- source.Reply(_("Users (uid): %lu entries, %lu buckets, longest chain is %d"), entries, buckets, max_chain);
- }
-
- GetHashStats(ChannelList, entries, buckets, max_chain);
- source.Reply(_("Channels: %lu entries, %lu buckets, longest chain is %d"), entries, buckets, max_chain);
-
- GetHashStats(*RegisteredChannelList, entries, buckets, max_chain);
- source.Reply(_("Registered channels: %lu entries, %lu buckets, longest chain is %d"), entries, buckets, max_chain);
-
- GetHashStats(*NickAliasList, entries, buckets, max_chain);
- source.Reply(_("Registered nicknames: %lu entries, %lu buckets, longest chain is %d"), entries, buckets, max_chain);
-
- GetHashStats(*NickCoreList, entries, buckets, max_chain);
- source.Reply(_("Registered nick groups: %lu entries, %lu buckets, longest chain is %d"), entries, buckets, max_chain);
-
- if (session_service)
- {
- GetHashStats(session_service->GetSessions(), entries, buckets, max_chain);
- source.Reply(_("Sessions: %lu entries, %lu buckets, longest chain is %d"), entries, buckets, max_chain);
- }
- }
-
- public:
- CommandOSStats(Module *creator) : Command(creator, "operserv/stats", 0, 1),
- akills("XLineManager", "xlinemanager/sgline"), snlines("XLineManager", "xlinemanager/snline"), sqlines("XLineManager", "xlinemanager/sqline")
- {
- this->SetDesc(_("Show status of Services and network"));
- this->SetSyntax("[AKILL | HASH | UPLINK | UPTIME | ALL | RESET]");
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- Anope::string extra = !params.empty() ? params[0] : "";
-
- Log(LOG_ADMIN, source, this) << extra;
-
- if (extra.equals_ci("RESET"))
- return this->DoStatsReset(source);
-
- if (extra.equals_ci("ALL") || extra.equals_ci("AKILL"))
- this->DoStatsAkill(source);
-
- if (extra.equals_ci("ALL") || extra.equals_ci("HASH"))
- this->DoStatsHash(source);
-
- if (extra.equals_ci("ALL") || extra.equals_ci("UPLINK"))
- this->DoStatsUplink(source);
-
- if (extra.empty() || extra.equals_ci("ALL") || extra.equals_ci("UPTIME"))
- this->DoStatsUptime(source);
-
- if (!extra.empty() && !extra.equals_ci("ALL") && !extra.equals_ci("AKILL") && !extra.equals_ci("HASH") && !extra.equals_ci("UPLINK") && !extra.equals_ci("UPTIME"))
- source.Reply(_("Unknown STATS option: \002%s\002"), extra.c_str());
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Without any option, shows the current number of users online,\n"
- "and the highest number of users online since Services was\n"
- "started, and the length of time Services has been running.\n"
- " \n"
- "With the \002AKILL\002 option, displays the current size of the\n"
- "AKILL list and the current default expiry time.\n"
- " \n"
- "The \002RESET\002 option currently resets the maximum user count\n"
- "to the number of users currently present on the network.\n"
- " \n"
- "The \002UPLINK\002 option displays information about the current\n"
- "server Anope uses as an uplink to the network.\n"
- " \n"
- "The \002HASH\002 option displays information about the hash maps.\n"
- " \n"
- "The \002ALL\002 option displays all of the above statistics."));
- return true;
- }
-};
-
-class OSStats : public Module
-{
- CommandOSStats commandosstats;
- Serialize::Type stats_type;
- Stats stats_saver;
-
- public:
- OSStats(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandosstats(this), stats_type("Stats", Stats::Unserialize)
- {
-
- }
-
- void OnUserConnect(User *u, bool &exempt) anope_override
- {
- if (UserListByNick.size() == MaxUserCount && Anope::CurTime == MaxUserTime)
- stats_saver.QueueUpdate();
- }
-};
-
-MODULE_INIT(OSStats)
diff --git a/modules/commands/os_sxline.cpp b/modules/commands/os_sxline.cpp
deleted file mode 100644
index 0de21ce02..000000000
--- a/modules/commands/os_sxline.cpp
+++ /dev/null
@@ -1,732 +0,0 @@
-/* OperServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class SXLineDelCallback : public NumberList
-{
- XLineManager *xlm;
- Command *command;
- CommandSource &source;
- unsigned deleted;
- public:
- SXLineDelCallback(XLineManager *x, Command *c, CommandSource &_source, const Anope::string &numlist) : NumberList(numlist, true), xlm(x), command(c), source(_source), deleted(0)
- {
- }
-
- ~SXLineDelCallback()
- {
- if (!deleted)
- source.Reply(_("No matching entries on the %s list."), source.command.c_str());
- else if (deleted == 1)
- source.Reply(_("Deleted 1 entry from the %s list."), source.command.c_str());
- else
- source.Reply(_("Deleted %d entries from the %s list."), deleted, source.command.c_str());
- }
-
- void HandleNumber(unsigned number) anope_override
- {
- if (!number)
- return;
-
- XLine *x = this->xlm->GetEntry(number - 1);
-
- if (!x)
- return;
-
- Log(LOG_ADMIN, source, command) << "to remove " << x->mask << " from the list";
-
- ++deleted;
- DoDel(this->xlm, source, x);
- }
-
- static void DoDel(XLineManager *xlm, CommandSource &source, XLine *x)
- {
- xlm->DelXLine(x);
- }
-};
-
-class CommandOSSXLineBase : public Command
-{
- private:
- virtual XLineManager* xlm() = 0;
-
- virtual void OnAdd(CommandSource &source, const std::vector<Anope::string> &params) = 0;
-
- void OnDel(CommandSource &source, const std::vector<Anope::string> &params)
- {
-
- if (!this->xlm() || this->xlm()->GetList().empty())
- {
- source.Reply(_("%s list is empty."), source.command.c_str());
- return;
- }
-
- const Anope::string &mask = params.size() > 1 ? params[1] : "";
-
- if (mask.empty())
- {
- this->OnSyntaxError(source, "DEL");
- return;
- }
-
- if (isdigit(mask[0]) && mask.find_first_not_of("1234567890,-") == Anope::string::npos)
- {
- SXLineDelCallback list(this->xlm(), this, source, mask);
- list.Process();
- }
- else
- {
- XLine *x = this->xlm()->HasEntry(mask);
-
- if (!x)
- {
- source.Reply(_("\002%s\002 not found on the %s list."), mask.c_str(), source.command.c_str());
- return;
- }
-
- FOREACH_MOD(OnDelXLine, (source, x, this->xlm()));
-
- SXLineDelCallback::DoDel(this->xlm(), source, x);
- source.Reply(_("\002%s\002 deleted from the %s list."), mask.c_str(), source.command.c_str());
- Log(LOG_ADMIN, source, this) << "to remove " << mask << " from the list";
- }
-
- if (Anope::ReadOnly)
- source.Reply(READ_ONLY_MODE);
-
- return;
- }
-
- void ProcessList(CommandSource &source, const std::vector<Anope::string> &params, ListFormatter &list)
- {
- if (!this->xlm() || this->xlm()->GetList().empty())
- {
- source.Reply(_("%s list is empty."), source.command.c_str());
- return;
- }
-
- const Anope::string &mask = params.size() > 1 ? params[1] : "";
-
- if (!mask.empty() && isdigit(mask[0]) && mask.find_first_not_of("1234567890,-") == Anope::string::npos)
- {
- class SXLineListCallback : public NumberList
- {
- XLineManager *xlm;
- CommandSource &source;
- ListFormatter &list;
- public:
- SXLineListCallback(XLineManager *x, CommandSource &_source, ListFormatter &_list, const Anope::string &numlist) : NumberList(numlist, false), xlm(x), source(_source), list(_list)
- {
- }
-
- void HandleNumber(unsigned number) anope_override
- {
- if (!number)
- return;
-
- const XLine *x = this->xlm->GetEntry(number - 1);
-
- if (!x)
- return;
-
- ListFormatter::ListEntry entry;
- entry["Number"] = stringify(number);
- entry["Mask"] = x->mask;
- entry["By"] = x->by;
- entry["Created"] = Anope::strftime(x->created, NULL, true);
- entry["Expires"] = Anope::Expires(x->expires, source.nc);
- entry["ID"] = x->id;
- entry["Reason"] = x->reason;
- list.AddEntry(entry);
- }
- }
- sl_list(this->xlm(), source, list, mask);
- sl_list.Process();
- }
- else
- {
- for (unsigned i = 0, end = this->xlm()->GetCount(); i < end; ++i)
- {
- const XLine *x = this->xlm()->GetEntry(i);
-
- if (mask.empty() || mask.equals_ci(x->mask) || mask == x->id || Anope::Match(x->mask, mask, false, true))
- {
- ListFormatter::ListEntry entry;
- entry["Number"] = stringify(i + 1);
- entry["Mask"] = x->mask;
- entry["By"] = x->by;
- entry["Created"] = Anope::strftime(x->created, NULL, true);
- entry["Expires"] = Anope::Expires(x->expires, source.nc);
- entry["ID"] = x->id;
- entry["Reason"] = x->reason;
- list.AddEntry(entry);
- }
- }
- }
-
- if (list.IsEmpty())
- source.Reply(_("No matching entries on the %s list."), source.command.c_str());
- else
- {
- source.Reply(_("Current %s list:"), source.command.c_str());
-
- std::vector<Anope::string> replies;
- list.Process(replies);
-
- for (unsigned i = 0; i < replies.size(); ++i)
- source.Reply(replies[i]);
- }
- }
-
- void OnList(CommandSource &source, const std::vector<Anope::string> &params)
- {
- ListFormatter list(source.GetAccount());
- list.AddColumn(_("Number")).AddColumn(_("Mask")).AddColumn(_("Reason"));
-
- this->ProcessList(source, params, list);
- }
-
- void OnView(CommandSource &source, const std::vector<Anope::string> &params)
- {
- ListFormatter list(source.GetAccount());
- list.AddColumn(_("Number")).AddColumn(_("Mask")).AddColumn(_("By")).AddColumn(_("Created")).AddColumn(_("Expires"));
- if (Config->GetModule("operserv")->Get<bool>("akillids"))
- list.AddColumn(_("ID"));
- list.AddColumn(_("Reason"));
-
- this->ProcessList(source, params, list);
- }
-
- void OnClear(CommandSource &source)
- {
- FOREACH_MOD(OnDelXLine, (source, NULL, this->xlm()));
-
- for (unsigned i = this->xlm()->GetCount(); i > 0; --i)
- {
- XLine *x = this->xlm()->GetEntry(i - 1);
- this->xlm()->DelXLine(x);
- }
-
- Log(LOG_ADMIN, source, this) << "to CLEAR the list";
- source.Reply(_("The %s list has been cleared."), source.command.c_str());
- if (Anope::ReadOnly)
- source.Reply(READ_ONLY_MODE);
-
- return;
- }
- public:
- CommandOSSXLineBase(Module *creator, const Anope::string &cmd) : Command(creator, cmd, 1, 4)
- {
- }
-
- const Anope::string GetDesc(CommandSource &source) const anope_override
- {
- return Anope::printf(Language::Translate(source.GetAccount(), _("Manipulate the %s list")), source.command.upper().c_str());
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &cmd = params[0];
-
- if (cmd.equals_ci("ADD"))
- return this->OnAdd(source, params);
- else if (cmd.equals_ci("DEL"))
- return this->OnDel(source, params);
- else if (cmd.equals_ci("LIST"))
- return this->OnList(source, params);
- else if (cmd.equals_ci("VIEW"))
- return this->OnView(source, params);
- else if (cmd.equals_ci("CLEAR"))
- return this->OnClear(source);
- else
- this->OnSyntaxError(source, "");
-
- return;
- }
-
- virtual bool OnHelp(CommandSource &source, const Anope::string &subcommand) = 0;
-};
-
-class CommandOSSNLine : public CommandOSSXLineBase
-{
- XLineManager *xlm()
- {
- return this->snlines;
- }
-
- void OnAdd(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (!this->xlm())
- return;
-
- unsigned last_param = 2;
- Anope::string param, expiry;
-
- param = params.size() > 1 ? params[1] : "";
- if (!param.empty() && param[0] == '+')
- {
- expiry = param;
- param = params.size() > 2 ? params[2] : "";
- last_param = 3;
- }
-
- time_t expires = !expiry.empty() ? Anope::DoTime(expiry) : Config->GetModule("operserv")->Get<time_t>("snlineexpiry", "30d");
- /* If the expiry given does not contain a final letter, it's in days,
- * said the doc. Ah well.
- */
- if (!expiry.empty() && isdigit(expiry[expiry.length() - 1]))
- expires *= 86400;
- /* Do not allow less than a minute expiry time */
- if (expires && expires < 60)
- {
- source.Reply(BAD_EXPIRY_TIME);
- return;
- }
- else if (expires > 0)
- expires += Anope::CurTime;
-
- if (param.empty())
- {
- this->OnSyntaxError(source, "ADD");
- return;
- }
-
- Anope::string rest = param;
- if (params.size() > last_param)
- rest += " " + params[last_param];
-
- if (rest.find(':') == Anope::string::npos)
- {
- this->OnSyntaxError(source, "ADD");
- return;
- }
-
- sepstream sep(rest, ':');
- Anope::string mask;
- sep.GetToken(mask);
- Anope::string reason = sep.GetRemaining();
-
- if (mask.empty() || reason.empty())
- {
- this->OnSyntaxError(source, "ADD");
- return;
- }
-
- if (mask[0] == '/' && mask[mask.length() - 1] == '/')
- {
- const Anope::string &regexengine = Config->GetBlock("options")->Get<const Anope::string>("regexengine");
-
- if (regexengine.empty())
- {
- source.Reply(_("Regex is disabled."));
- return;
- }
-
- ServiceReference<RegexProvider> provider("Regex", regexengine);
- if (!provider)
- {
- source.Reply(_("Unable to find regex engine %s."), regexengine.c_str());
- return;
- }
-
- try
- {
- Anope::string stripped_mask = mask.substr(1, mask.length() - 2);
- delete provider->Compile(stripped_mask);
- }
- catch (const RegexException &ex)
- {
- source.Reply("%s", ex.GetReason().c_str());
- return;
- }
- }
-
- /* Clean up the last character of the mask if it is a space
- * See bug #761
- */
- unsigned masklen = mask.length();
- if (mask[masklen - 1] == ' ')
- mask.erase(masklen - 1);
-
- if (Config->GetModule("operserv")->Get<bool>("addakiller", "yes") && !source.GetNick().empty())
- reason = "[" + source.GetNick() + "] " + reason;
-
- if (mask.find_first_not_of("/.*?") == Anope::string::npos)
- {
- source.Reply(USERHOST_MASK_TOO_WIDE, mask.c_str());
- return;
- }
-
- XLine *x = new XLine(mask, source.GetNick(), expires, reason);
- if (Config->GetModule("operserv")->Get<bool>("akillids"))
- x->id = XLineManager::GenerateUID();
-
- unsigned int affected = 0;
- for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it)
- if (this->xlm()->Check(it->second, x))
- ++affected;
- float percent = static_cast<float>(affected) / static_cast<float>(UserListByNick.size()) * 100.0;
-
- if (percent > 95)
- {
- source.Reply(USERHOST_MASK_TOO_WIDE, mask.c_str());
- Log(LOG_ADMIN, source, this) << "tried to " << source.command << " " << percent << "% of the network (" << affected << " users)";
- delete x;
- return;
- }
-
- if (!this->xlm()->CanAdd(source, mask, expires, reason))
- return;
-
- EventReturn MOD_RESULT;
- FOREACH_RESULT(OnAddXLine, MOD_RESULT, (source, x, this->xlm()));
- if (MOD_RESULT == EVENT_STOP)
- {
- delete x;
- return;
- }
-
- this->xlm()->AddXLine(x);
-
- if (Config->GetModule("operserv")->Get<bool>("killonsnline", "yes"))
- {
- Anope::string rreason = "G-Lined: " + reason;
-
- for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it)
- {
- User *user = it->second;
-
- if (!user->HasMode("OPER") && user->server != Me && this->xlm()->Check(user, x))
- user->Kill(Me, rreason);
- }
-
- this->xlm()->Send(NULL, x);
- }
-
- source.Reply(_("\002%s\002 added to the %s list."), mask.c_str(), source.command.c_str());
- Log(LOG_ADMIN, source, this) << "on " << mask << " (" << reason << "), expires in " << (expires ? Anope::Duration(expires - Anope::CurTime) : "never") << " [affects " << affected << " user(s) (" << percent << "%)]";
- if (Anope::ReadOnly)
- source.Reply(READ_ONLY_MODE);
- }
-
- ServiceReference<XLineManager> snlines;
- public:
- CommandOSSNLine(Module *creator) : CommandOSSXLineBase(creator, "operserv/snline"), snlines("XLineManager", "xlinemanager/snline")
- {
- this->SetSyntax(_("ADD [+\037expiry\037] \037mask\037:\037reason\037"));
- this->SetSyntax(_("DEL {\037mask\037 | \037entry-num\037 | \037list\037 | \037id\037}"));
- this->SetSyntax(_("LIST [\037mask\037 | \037list\037 | \037id\037]"));
- this->SetSyntax(_("VIEW [\037mask\037 | \037list\037 | \037id\037]"));
- this->SetSyntax("CLEAR");
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Allows Services Operators to manipulate the SNLINE list. If\n"
- "a user with a realname matching an SNLINE mask attempts to\n"
- "connect, Services will not allow it to pursue his IRC\n"
- "session."));
- source.Reply(_(" \n"
- "\002SNLINE ADD\002 adds the given realname mask to the SNLINE\n"
- "list for the given reason (which \002must\002 be given).\n"
- "\037expiry\037 is specified as an integer followed by one of \037d\037\n"
- "(days), \037h\037 (hours), or \037m\037 (minutes). Combinations (such as\n"
- "\0371h30m\037) are not permitted. If a unit specifier is not\n"
- "included, the default is days (so \037+30\037 by itself means 30\n"
- "days). To add an SNLINE which does not expire, use \037+0\037. If the\n"
- "realname mask to be added starts with a \037+\037, an expiry time must\n"
- "be given, even if it is the same as the default. The\n"
- "current SNLINE default expiry time can be found with the\n"
- "\002STATS AKILL\002 command.\n"
- " \n"
- "\002Note\002: because the realname mask may contain spaces, the\n"
- "separator between it and the reason is a colon."));
- const Anope::string &regexengine = Config->GetBlock("options")->Get<const Anope::string>("regexengine");
- if (!regexengine.empty())
- {
- source.Reply(" ");
- source.Reply(_("Regex matches are also supported using the %s engine.\n"
- "Enclose your mask in // if this is desired."), regexengine.c_str());
- }
- source.Reply(_(" \n"
- "The \002SNLINE DEL\002 command removes the given mask from the\n"
- "SNLINE list if it is present. If a list of entry numbers is\n"
- "given, those entries are deleted. (See the example for LIST\n"
- "below.)\n"
- " \n"
- "The \002SNLINE LIST\002 command displays the SNLINE list.\n"
- "If a wildcard mask is given, only those entries matching the\n"
- "mask are displayed. If a list of entry numbers is given,\n"
- "only those entries are shown; for example:\n"
- " \002SNLINE LIST 2-5,7-9\002\n"
- " Lists SNLINE entries numbered 2 through 5 and 7\n"
- " through 9.\n"
- " \n"
- "\002SNLINE VIEW\002 is a more verbose version of \002SNLINE LIST\002, and\n"
- "will show who added an SNLINE, the date it was added, and when\n"
- "it expires, as well as the realname mask and reason.\n"
- " \n"
- "\002SNLINE CLEAR\002 clears all entries of the SNLINE list."));
- return true;
- }
-};
-
-class CommandOSSQLine : public CommandOSSXLineBase
-{
- XLineManager *xlm()
- {
- return this->sqlines;
- }
-
- void OnAdd(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (!this->xlm())
- return;
-
- unsigned last_param = 2;
- Anope::string expiry, mask;
-
- mask = params.size() > 1 ? params[1] : "";
- if (!mask.empty() && mask[0] == '+')
- {
- expiry = mask;
- mask = params.size() > 2 ? params[2] : "";
- last_param = 3;
- }
-
- time_t expires = !expiry.empty() ? Anope::DoTime(expiry) : Config->GetModule("operserv")->Get<time_t>("sqlineexpiry", "30d");
- /* If the expiry given does not contain a final letter, it's in days,
- * said the doc. Ah well.
- */
- if (!expiry.empty() && isdigit(expiry[expiry.length() - 1]))
- expires *= 86400;
- /* Do not allow less than a minute expiry time */
- if (expires && expires < 60)
- {
- source.Reply(BAD_EXPIRY_TIME);
- return;
- }
- else if (expires > 0)
- expires += Anope::CurTime;
-
- if (params.size() <= last_param)
- {
- this->OnSyntaxError(source, "ADD");
- return;
- }
-
- Anope::string reason = params[last_param];
- if (last_param == 2 && params.size() > 3)
- reason += " " + params[3];
-
- if (mask.empty() || reason.empty())
- {
- this->OnSyntaxError(source, "ADD");
- return;
- }
-
- if (mask[0] == '/' && mask[mask.length() - 1] == '/')
- {
- const Anope::string &regexengine = Config->GetBlock("options")->Get<const Anope::string>("regexengine");
-
- if (regexengine.empty())
- {
- source.Reply(_("Regex is disabled."));
- return;
- }
-
- ServiceReference<RegexProvider> provider("Regex", regexengine);
- if (!provider)
- {
- source.Reply(_("Unable to find regex engine %s."), regexengine.c_str());
- return;
- }
-
- try
- {
- Anope::string stripped_mask = mask.substr(1, mask.length() - 2);
- delete provider->Compile(stripped_mask);
- }
- catch (const RegexException &ex)
- {
- source.Reply("%s", ex.GetReason().c_str());
- return;
- }
- }
-
- if (Config->GetModule("operserv")->Get<bool>("addakiller", "yes") && !source.GetNick().empty())
- reason = "[" + source.GetNick() + "] " + reason;
-
- if (mask.find_first_not_of("./?*") == Anope::string::npos)
- {
- source.Reply(USERHOST_MASK_TOO_WIDE, mask.c_str());
- return;
- }
-
- XLine *x = new XLine(mask, source.GetNick(), expires, reason);
- if (Config->GetModule("operserv")->Get<bool>("akillids"))
- x->id = XLineManager::GenerateUID();
-
- unsigned int affected = 0;
- for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it)
- if (this->xlm()->Check(it->second, x))
- ++affected;
- float percent = static_cast<float>(affected) / static_cast<float>(UserListByNick.size()) * 100.0;
-
- if (percent > 95)
- {
- source.Reply(USERHOST_MASK_TOO_WIDE, mask.c_str());
- Log(LOG_ADMIN, source, this) << "tried to SQLine " << percent << "% of the network (" << affected << " users)";
- delete x;
- return;
- }
-
- if (!this->sqlines->CanAdd(source, mask, expires, reason))
- return;
-
- EventReturn MOD_RESULT;
- FOREACH_RESULT(OnAddXLine, MOD_RESULT, (source, x, this->xlm()));
- if (MOD_RESULT == EVENT_STOP)
- {
- delete x;
- return;
- }
-
- this->xlm()->AddXLine(x);
-
- if (Config->GetModule("operserv")->Get<bool>("killonsqline", "yes"))
- {
- Anope::string rreason = "Q-Lined: " + reason;
-
- if (mask[0] == '#')
- {
- for (channel_map::const_iterator cit = ChannelList.begin(), cit_end = ChannelList.end(); cit != cit_end; ++cit)
- {
- Channel *c = cit->second;
-
- if (!Anope::Match(c->name, mask, false, true))
- continue;
-
- std::vector<User *> users;
- for (Channel::ChanUserList::iterator it = c->users.begin(), it_end = c->users.end(); it != it_end; ++it)
- {
- ChanUserContainer *uc = it->second;
- User *user = uc->user;
-
- if (!user->HasMode("OPER") && user->server != Me)
- users.push_back(user);
- }
-
- for (unsigned i = 0; i < users.size(); ++i)
- c->Kick(NULL, users[i], "%s", reason.c_str());
- }
- }
- else
- {
- for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it)
- {
- User *user = it->second;
-
- if (!user->HasMode("OPER") && user->server != Me && this->xlm()->Check(user, x))
- user->Kill(Me, rreason);
- }
- }
-
- this->xlm()->Send(NULL, x);
- }
-
- source.Reply(_("\002%s\002 added to the %s list."), mask.c_str(), source.command.c_str());
- Log(LOG_ADMIN, source, this) << "on " << mask << " (" << reason << "), expires in " << (expires ? Anope::Duration(expires - Anope::CurTime) : "never") << " [affects " << affected << " user(s) (" << percent << "%)]";
- if (Anope::ReadOnly)
- source.Reply(READ_ONLY_MODE);
- }
-
- ServiceReference<XLineManager> sqlines;
- public:
- CommandOSSQLine(Module *creator) : CommandOSSXLineBase(creator, "operserv/sqline"), sqlines("XLineManager", "xlinemanager/sqline")
- {
- this->SetSyntax(_("ADD [+\037expiry\037] \037mask\037 \037reason\037"));
- this->SetSyntax(_("DEL {\037mask\037 | \037entry-num\037 | \037list\037 | \037id\037}"));
- this->SetSyntax(_("LIST [\037mask\037 | \037list\037 | \037id\037]"));
- this->SetSyntax(_("VIEW [\037mask\037 | \037list\037 | \037id\037]"));
- this->SetSyntax("CLEAR");
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Allows Services Operators to manipulate the SQLINE list. If\n"
- "a user with a nick matching an SQLINE mask attempts to\n"
- "connect, Services will not allow it to pursue his IRC\n"
- "session.\n"
- "If the first character of the mask is #, services will\n"
- "prevent the use of matching channels. If the mask is a\n"
- "regular expression, the expression will be matched against\n"
- "channels too."));
- source.Reply(_(" \n"
- "\002SQLINE ADD\002 adds the given (nick's) mask to the SQLINE\n"
- "list for the given reason (which \002must\002 be given).\n"
- "\037expiry\037 is specified as an integer followed by one of \037d\037\n"
- "(days), \037h\037 (hours), or \037m\037 (minutes). Combinations (such as\n"
- "\0371h30m\037) are not permitted. If a unit specifier is not\n"
- "included, the default is days (so \037+30\037 by itself means 30\n"
- "days). To add an SQLINE which does not expire, use \037+0\037.\n"
- "If the mask to be added starts with a \037+\037, an expiry time\n"
- "must be given, even if it is the same as the default. The\n"
- "current SQLINE default expiry time can be found with the\n"
- "\002STATS AKILL\002 command."));
- const Anope::string &regexengine = Config->GetBlock("options")->Get<const Anope::string>("regexengine");
- if (!regexengine.empty())
- {
- source.Reply(" ");
- source.Reply(_("Regex matches are also supported using the %s engine.\n"
- "Enclose your mask in // if this is desired."), regexengine.c_str());
- }
- source.Reply(_(" \n"
- "The \002SQLINE DEL\002 command removes the given mask from the\n"
- "SQLINE list if it is present. If a list of entry numbers is\n"
- "given, those entries are deleted. (See the example for LIST\n"
- "below.)\n"
- " \n"
- "The \002SQLINE LIST\002 command displays the SQLINE list.\n"
- "If a wildcard mask is given, only those entries matching the\n"
- "mask are displayed. If a list of entry numbers is given,\n"
- "only those entries are shown; for example:\n"
- " \002SQLINE LIST 2-5,7-9\002\n"
- " Lists SQLINE entries numbered 2 through 5 and 7\n"
- " through 9.\n"
- " \n"
- "\002SQLINE VIEW\002 is a more verbose version of \002SQLINE LIST\002, and\n"
- "will show who added an SQLINE, the date it was added, and when\n"
- "it expires, as well as the mask and reason.\n"
- " \n"
- "\002SQLINE CLEAR\002 clears all entries of the SQLINE list."));
- return true;
- }
-};
-
-class OSSXLine : public Module
-{
- CommandOSSNLine commandossnline;
- CommandOSSQLine commandossqline;
-
- public:
- OSSXLine(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandossnline(this), commandossqline(this)
- {
- }
-};
-
-MODULE_INIT(OSSXLine)
diff --git a/modules/commands/os_update.cpp b/modules/commands/os_update.cpp
deleted file mode 100644
index 5fac1b063..000000000
--- a/modules/commands/os_update.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-/* OperServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class CommandOSUpdate : public Command
-{
- public:
- CommandOSUpdate(Module *creator) : Command(creator, "operserv/update", 0, 0)
- {
- this->SetDesc(_("Force the Services databases to be updated immediately"));
- }
-
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- Log(LOG_ADMIN, source, this);
- source.Reply(_("Updating databases."));
- Anope::SaveDatabases();
- return;
- }
-
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
- {
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Causes Services to update all database files as soon as you\n"
- "send the command."));
- return true;
- }
-};
-
-class OSUpdate : public Module
-{
- CommandOSUpdate commandosupdate;
-
- public:
- OSUpdate(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandosupdate(this)
- {
-
- }
-};
-
-MODULE_INIT(OSUpdate)
diff --git a/modules/database/CMakeLists.txt b/modules/database/CMakeLists.txt
new file mode 100644
index 000000000..9a236d6d0
--- /dev/null
+++ b/modules/database/CMakeLists.txt
@@ -0,0 +1,2 @@
+build_modules(${CMAKE_CURRENT_SOURCE_DIR})
+build_modules_dependencies(${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/modules/database/db_flatfile.cpp b/modules/database/db_flatfile.cpp
deleted file mode 100644
index 171573d1c..000000000
--- a/modules/database/db_flatfile.cpp
+++ /dev/null
@@ -1,415 +0,0 @@
-/*
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-#ifndef _WIN32
-#include <sys/wait.h>
-#endif
-
-class SaveData : public Serialize::Data
-{
- public:
- Anope::string last;
- std::fstream *fs;
-
- SaveData() : fs(NULL) { }
-
- std::iostream& operator[](const Anope::string &key) anope_override
- {
- if (key != last)
- {
- *fs << "\nDATA " << key << " ";
- last = key;
- }
-
- return *fs;
- }
-};
-
-class LoadData : public Serialize::Data
-{
- public:
- std::fstream *fs;
- unsigned int id;
- std::map<Anope::string, Anope::string> data;
- std::stringstream ss;
- bool read;
-
- LoadData() : fs(NULL), id(0), read(false) { }
-
- std::iostream& operator[](const Anope::string &key) anope_override
- {
- if (!read)
- {
- for (Anope::string token; std::getline(*this->fs, token.str());)
- {
- if (token.find("ID ") == 0)
- {
- try
- {
- this->id = convertTo<unsigned int>(token.substr(3));
- }
- catch (const ConvertException &) { }
-
- continue;
- }
- else if (token.find("DATA ") != 0)
- break;
-
- size_t sp = token.find(' ', 5); // Skip DATA
- if (sp != Anope::string::npos)
- data[token.substr(5, sp - 5)] = token.substr(sp + 1);
- }
-
- read = true;
- }
-
- ss.clear();
- this->ss << this->data[key];
- return this->ss;
- }
-
- std::set<Anope::string> KeySet() const anope_override
- {
- std::set<Anope::string> keys;
- for (std::map<Anope::string, Anope::string>::const_iterator it = this->data.begin(), it_end = this->data.end(); it != it_end; ++it)
- keys.insert(it->first);
- return keys;
- }
-
- size_t Hash() const anope_override
- {
- size_t hash = 0;
- for (std::map<Anope::string, Anope::string>::const_iterator it = this->data.begin(), it_end = this->data.end(); it != it_end; ++it)
- if (!it->second.empty())
- hash ^= Anope::hash_cs()(it->second);
- return hash;
- }
-
- void Reset()
- {
- id = 0;
- read = false;
- data.clear();
- }
-};
-
-class DBFlatFile : public Module, public Pipe
-{
- /* Day the last backup was on */
- int last_day;
- /* Backup file names */
- std::map<Anope::string, std::list<Anope::string> > backups;
- bool loaded;
-
- int child_pid;
-
- void BackupDatabase()
- {
- tm *tm = localtime(&Anope::CurTime);
-
- if (tm->tm_mday != last_day)
- {
- last_day = tm->tm_mday;
-
- const std::vector<Anope::string> &type_order = Serialize::Type::GetTypeOrder();
-
- std::set<Anope::string> dbs;
- dbs.insert(Config->GetModule(this)->Get<const Anope::string>("database", "anope.db"));
-
- for (unsigned i = 0; i < type_order.size(); ++i)
- {
- Serialize::Type *stype = Serialize::Type::Find(type_order[i]);
-
- if (stype && stype->GetOwner())
- dbs.insert("module_" + stype->GetOwner()->name + ".db");
- }
-
-
- for (std::set<Anope::string>::const_iterator it = dbs.begin(), it_end = dbs.end(); it != it_end; ++it)
- {
- const Anope::string &oldname = Anope::DataDir + "/" + *it;
- Anope::string newname = Anope::DataDir + "/backups/" + *it + "-" + stringify(tm->tm_year + 1900) + Anope::printf("-%02i-", tm->tm_mon + 1) + Anope::printf("%02i", tm->tm_mday);
-
- /* Backup already exists or no database to backup */
- if (Anope::IsFile(newname) || !Anope::IsFile(oldname))
- continue;
-
- Log(LOG_DEBUG) << "db_flatfile: Attempting to rename " << *it << " to " << newname;
- if (rename(oldname.c_str(), newname.c_str()))
- {
- Anope::string err = Anope::LastError();
- Log(this) << "Unable to back up database " << *it << " (" << err << ")!";
-
- if (!Config->GetModule(this)->Get<bool>("nobackupokay"))
- {
- Anope::Quitting = true;
- Anope::QuitReason = "Unable to back up database " + *it + " (" + err + ")";
- }
-
- continue;
- }
-
- backups[*it].push_back(newname);
-
- unsigned keepbackups = Config->GetModule(this)->Get<unsigned>("keepbackups");
- if (keepbackups > 0 && backups[*it].size() > keepbackups)
- {
- unlink(backups[*it].front().c_str());
- backups[*it].pop_front();
- }
- }
- }
- }
-
- public:
- DBFlatFile(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, DATABASE | VENDOR), last_day(0), loaded(false), child_pid(-1)
- {
-
- }
-
-#ifndef _WIN32
- void OnRestart() anope_override
- {
- OnShutdown();
- }
-
- void OnShutdown() anope_override
- {
- if (child_pid > -1)
- {
- Log(this) << "Waiting for child to exit...";
-
- int status;
- waitpid(child_pid, &status, 0);
-
- Log(this) << "Done";
- }
- }
-#endif
-
- void OnNotify() anope_override
- {
- char buf[512];
- int i = this->Read(buf, sizeof(buf) - 1);
- if (i <= 0)
- return;
- buf[i] = 0;
-
- child_pid = -1;
-
- if (!*buf)
- {
- Log(this) << "Finished saving databases";
- return;
- }
-
- Log(this) << "Error saving databases: " << buf;
-
- if (!Config->GetModule(this)->Get<bool>("nobackupokay"))
- Anope::Quitting = true;
- }
-
- EventReturn OnLoadDatabase() anope_override
- {
- const std::vector<Anope::string> &type_order = Serialize::Type::GetTypeOrder();
- std::set<Anope::string> tried_dbs;
-
- const Anope::string &db_name = Anope::DataDir + "/" + Config->GetModule(this)->Get<const Anope::string>("database", "anope.db");
-
- std::fstream fd(db_name.c_str(), std::ios_base::in | std::ios_base::binary);
- if (!fd.is_open())
- {
- Log(this) << "Unable to open " << db_name << " for reading!";
- return EVENT_STOP;
- }
-
- std::map<Anope::string, std::vector<std::streampos> > positions;
-
- for (Anope::string buf; std::getline(fd, buf.str());)
- if (buf.find("OBJECT ") == 0)
- positions[buf.substr(7)].push_back(fd.tellg());
-
- LoadData ld;
- ld.fs = &fd;
-
- for (unsigned i = 0; i < type_order.size(); ++i)
- {
- Serialize::Type *stype = Serialize::Type::Find(type_order[i]);
- if (!stype || stype->GetOwner())
- continue;
-
- std::vector<std::streampos> &pos = positions[stype->GetName()];
-
- for (unsigned j = 0; j < pos.size(); ++j)
- {
- fd.clear();
- fd.seekg(pos[j]);
-
- Serializable *obj = stype->Unserialize(NULL, ld);
- if (obj != NULL)
- obj->id = ld.id;
- ld.Reset();
- }
- }
-
- fd.close();
-
- loaded = true;
- return EVENT_STOP;
- }
-
-
- void OnSaveDatabase() anope_override
- {
- if (child_pid > -1)
- {
- Log(this) << "Database save is already in progress!";
- return;
- }
-
- BackupDatabase();
-
- int i = -1;
-#ifndef _WIN32
- if (!Anope::Quitting && Config->GetModule(this)->Get<bool>("fork"))
- {
- i = fork();
- if (i > 0)
- {
- child_pid = i;
- return;
- }
- else if (i < 0)
- Log(this) << "Unable to fork for database save";
- }
-#endif
-
- try
- {
- std::map<Module *, std::fstream *> databases;
-
- /* First open the databases of all of the registered types. This way, if we have a type with 0 objects, that database will be properly cleared */
- for (std::map<Anope::string, Serialize::Type *>::const_iterator it = Serialize::Type::GetTypes().begin(), it_end = Serialize::Type::GetTypes().end(); it != it_end; ++it)
- {
- Serialize::Type *s_type = it->second;
-
- if (databases[s_type->GetOwner()])
- continue;
-
- Anope::string db_name;
- if (s_type->GetOwner())
- db_name = Anope::DataDir + "/module_" + s_type->GetOwner()->name + ".db";
- else
- db_name = Anope::DataDir + "/" + Config->GetModule(this)->Get<const Anope::string>("database", "anope.db");
-
- if (Anope::IsFile(db_name))
- rename(db_name.c_str(), (db_name + ".tmp").c_str());
-
- std::fstream *fs = databases[s_type->GetOwner()] = new std::fstream(db_name.c_str(), std::ios_base::out | std::ios_base::trunc | std::ios_base::binary);
-
- if (!fs->is_open())
- Log(this) << "Unable to open " << db_name << " for writing";
- }
-
- SaveData data;
- const std::list<Serializable *> &items = Serializable::GetItems();
- for (std::list<Serializable *>::const_iterator it = items.begin(), it_end = items.end(); it != it_end; ++it)
- {
- Serializable *base = *it;
- Serialize::Type *s_type = base->GetSerializableType();
-
- data.fs = databases[s_type->GetOwner()];
- if (!data.fs || !data.fs->is_open())
- continue;
-
- *data.fs << "OBJECT " << s_type->GetName();
- if (base->id)
- *data.fs << "\nID " << base->id;
- base->Serialize(data);
- *data.fs << "\nEND\n";
- }
-
- for (std::map<Module *, std::fstream *>::iterator it = databases.begin(), it_end = databases.end(); it != it_end; ++it)
- {
- std::fstream *f = it->second;
- const Anope::string &db_name = Anope::DataDir + "/" + (it->first ? (it->first->name + ".db") : Config->GetModule(this)->Get<const Anope::string>("database", "anope.db"));
-
- if (!f->is_open() || !f->good())
- {
- this->Write("Unable to write database " + db_name);
-
- f->close();
-
- if (Anope::IsFile((db_name + ".tmp").c_str()))
- rename((db_name + ".tmp").c_str(), db_name.c_str());
- }
- else
- {
- f->close();
- unlink((db_name + ".tmp").c_str());
- }
-
- delete f;
- }
- }
- catch (...)
- {
- if (i)
- throw;
- }
-
- if (!i)
- {
- this->Notify();
- exit(0);
- }
- }
-
- /* Load just one type. Done if a module is reloaded during runtime */
- void OnSerializeTypeCreate(Serialize::Type *stype) anope_override
- {
- if (!loaded)
- return;
-
- Anope::string db_name;
- if (stype->GetOwner())
- db_name = Anope::DataDir + "/module_" + stype->GetOwner()->name + ".db";
- else
- db_name = Anope::DataDir + "/" + Config->GetModule(this)->Get<const Anope::string>("database", "anope.db");
-
- std::fstream fd(db_name.c_str(), std::ios_base::in | std::ios_base::binary);
- if (!fd.is_open())
- {
- Log(this) << "Unable to open " << db_name << " for reading!";
- return;
- }
-
- LoadData ld;
- ld.fs = &fd;
-
- for (Anope::string buf; std::getline(fd, buf.str());)
- {
- if (buf == "OBJECT " + stype->GetName())
- {
- stype->Unserialize(NULL, ld);
- ld.Reset();
- }
- }
-
- fd.close();
- }
-};
-
-MODULE_INIT(DBFlatFile)
-
-
diff --git a/modules/database/db_redis.cpp b/modules/database/db_redis.cpp
deleted file mode 100644
index db12a2c5f..000000000
--- a/modules/database/db_redis.cpp
+++ /dev/null
@@ -1,644 +0,0 @@
-/*
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- */
-
-#include "module.h"
-#include "modules/redis.h"
-
-using namespace Redis;
-
-class DatabaseRedis;
-static DatabaseRedis *me;
-
-class Data : public Serialize::Data
-{
- public:
- std::map<Anope::string, std::stringstream *> data;
-
- ~Data()
- {
- for (std::map<Anope::string, std::stringstream *>::iterator it = data.begin(), it_end = data.end(); it != it_end; ++it)
- delete it->second;
- }
-
- std::iostream& operator[](const Anope::string &key) anope_override
- {
- std::stringstream* &stream = data[key];
- if (!stream)
- stream = new std::stringstream();
- return *stream;
- }
-
- std::set<Anope::string> KeySet() const anope_override
- {
- std::set<Anope::string> keys;
- for (std::map<Anope::string, std::stringstream *>::const_iterator it = this->data.begin(), it_end = this->data.end(); it != it_end; ++it)
- keys.insert(it->first);
- return keys;
- }
-
- size_t Hash() const anope_override
- {
- size_t hash = 0;
- for (std::map<Anope::string, std::stringstream *>::const_iterator it = this->data.begin(), it_end = this->data.end(); it != it_end; ++it)
- if (!it->second->str().empty())
- hash ^= Anope::hash_cs()(it->second->str());
- return hash;
- }
-};
-
-class TypeLoader : public Interface
-{
- Anope::string type;
- public:
- TypeLoader(Module *creator, const Anope::string &t) : Interface(creator), type(t) { }
-
- void OnResult(const Reply &r) anope_override;
-};
-
-class ObjectLoader : public Interface
-{
- Anope::string type;
- int64_t id;
-
- public:
- ObjectLoader(Module *creator, const Anope::string &t, int64_t i) : Interface(creator), type(t), id(i) { }
-
- void OnResult(const Reply &r) anope_override;
-};
-
-class IDInterface : public Interface
-{
- Reference<Serializable> o;
- public:
- IDInterface(Module *creator, Serializable *obj) : Interface(creator), o(obj) { }
-
- void OnResult(const Reply &r) anope_override;
-};
-
-class Deleter : public Interface
-{
- Anope::string type;
- int64_t id;
- public:
- Deleter(Module *creator, const Anope::string &t, int64_t i) : Interface(creator), type(t), id(i) { }
-
- void OnResult(const Reply &r) anope_override;
-};
-
-class Updater : public Interface
-{
- Anope::string type;
- int64_t id;
- public:
- Updater(Module *creator, const Anope::string &t, int64_t i) : Interface(creator), type(t), id(i) { }
-
- void OnResult(const Reply &r) anope_override;
-};
-
-class ModifiedObject : public Interface
-{
- Anope::string type;
- int64_t id;
- public:
- ModifiedObject(Module *creator, const Anope::string &t, int64_t i) : Interface(creator), type(t), id(i) { }
-
- void OnResult(const Reply &r) anope_override;
-};
-
-class SubscriptionListener : public Interface
-{
- public:
- SubscriptionListener(Module *creator) : Interface(creator) { }
-
- void OnResult(const Reply &r) anope_override;
-};
-
-class DatabaseRedis : public Module, public Pipe
-{
- SubscriptionListener sl;
- std::set<Serializable *> updated_items;
-
- public:
- ServiceReference<Provider> redis;
-
- DatabaseRedis(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, DATABASE | VENDOR), sl(this)
- {
- me = this;
-
- }
-
- /* Insert or update an object */
- void InsertObject(Serializable *obj)
- {
- Serialize::Type *t = obj->GetSerializableType();
-
- /* If there is no id yet for this object, get one */
- if (!obj->id)
- redis->SendCommand(new IDInterface(this, obj), "INCR id:" + t->GetName());
- else
- {
- Data data;
- obj->Serialize(data);
-
- if (obj->IsCached(data))
- return;
-
- obj->UpdateCache(data);
-
- std::vector<Anope::string> args;
- args.push_back("HGETALL");
- args.push_back("hash:" + t->GetName() + ":" + stringify(obj->id));
-
- /* Get object attrs to clear before updating */
- redis->SendCommand(new Updater(this, t->GetName(), obj->id), args);
- }
- }
-
- void OnNotify() anope_override
- {
- for (std::set<Serializable *>::iterator it = this->updated_items.begin(), it_end = this->updated_items.end(); it != it_end; ++it)
- {
- Serializable *s = *it;
-
- this->InsertObject(s);
- }
-
- this->updated_items.clear();
- }
-
- void OnReload(Configuration::Conf *conf) anope_override
- {
- Configuration::Block *block = conf->GetModule(this);
- this->redis = ServiceReference<Provider>("Redis::Provider", block->Get<const Anope::string>("engine", "redis/main"));
- }
-
- EventReturn OnLoadDatabase() anope_override
- {
- const std::vector<Anope::string> type_order = Serialize::Type::GetTypeOrder();
- for (unsigned i = 0; i < type_order.size(); ++i)
- {
- Serialize::Type *sb = Serialize::Type::Find(type_order[i]);
- this->OnSerializeTypeCreate(sb);
- }
-
- while (redis->BlockAndProcess());
-
- redis->Subscribe(&this->sl, "__keyspace@*__:hash:*");
-
- return EVENT_STOP;
- }
-
- void OnSerializeTypeCreate(Serialize::Type *sb) anope_override
- {
- if (!redis)
- return;
-
- std::vector<Anope::string> args;
- args.push_back("SMEMBERS");
- args.push_back("ids:" + sb->GetName());
-
- redis->SendCommand(new TypeLoader(this, sb->GetName()), args);
- }
-
- void OnSerializableConstruct(Serializable *obj) anope_override
- {
- this->updated_items.insert(obj);
- this->Notify();
- }
-
- void OnSerializableDestruct(Serializable *obj) anope_override
- {
- Serialize::Type *t = obj->GetSerializableType();
-
- std::vector<Anope::string> args;
- args.push_back("HGETALL");
- args.push_back("hash:" + t->GetName() + ":" + stringify(obj->id));
-
- /* Get all of the attributes for this object */
- redis->SendCommand(new Deleter(this, t->GetName(), obj->id), args);
-
- this->updated_items.erase(obj);
- t->objects.erase(obj->id);
- this->Notify();
- }
-
- void OnSerializableUpdate(Serializable *obj) anope_override
- {
- this->updated_items.insert(obj);
- this->Notify();
- }
-};
-
-void TypeLoader::OnResult(const Reply &r)
-{
- if (r.type != Reply::MULTI_BULK || !me->redis)
- {
- delete this;
- return;
- }
-
- for (unsigned i = 0; i < r.multi_bulk.size(); ++i)
- {
- const Reply *reply = r.multi_bulk[i];
-
- if (reply->type != Reply::BULK)
- continue;
-
- int64_t id;
- try
- {
- id = convertTo<int64_t>(reply->bulk);
- }
- catch (const ConvertException &)
- {
- continue;
- }
-
- std::vector<Anope::string> args;
- args.push_back("HGETALL");
- args.push_back("hash:" + this->type + ":" + stringify(id));
-
- me->redis->SendCommand(new ObjectLoader(me, this->type, id), args);
- }
-
- delete this;
-}
-
-void ObjectLoader::OnResult(const Reply &r)
-{
- Serialize::Type *st = Serialize::Type::Find(this->type);
-
- if (r.type != Reply::MULTI_BULK || r.multi_bulk.empty() || !me->redis || !st)
- {
- delete this;
- return;
- }
-
- Data data;
-
- for (unsigned i = 0; i + 1 < r.multi_bulk.size(); i += 2)
- {
- const Reply *key = r.multi_bulk[i],
- *value = r.multi_bulk[i + 1];
-
- data[key->bulk] << value->bulk;
- }
-
- Serializable* &obj = st->objects[this->id];
- obj = st->Unserialize(obj, data);
- if (obj)
- {
- obj->id = this->id;
- obj->UpdateCache(data);
- }
-
- delete this;
-}
-
-void IDInterface::OnResult(const Reply &r)
-{
- if (!o || r.type != Reply::INT || !r.i)
- {
- delete this;
- return;
- }
-
- Serializable* &obj = o->GetSerializableType()->objects[r.i];
- if (obj)
- /* This shouldn't be possible */
- obj->id = 0;
-
- o->id = r.i;
- obj = o;
-
- /* Now that we have the id, insert this object for real */
- anope_dynamic_static_cast<DatabaseRedis *>(this->owner)->InsertObject(o);
-
- delete this;
-}
-
-void Deleter::OnResult(const Reply &r)
-{
- if (r.type != Reply::MULTI_BULK || !me->redis || r.multi_bulk.empty())
- {
- delete this;
- return;
- }
-
- /* Transaction start */
- me->redis->StartTransaction();
-
- std::vector<Anope::string> args;
- args.push_back("DEL");
- args.push_back("hash:" + this->type + ":" + stringify(this->id));
-
- /* Delete hash object */
- me->redis->SendCommand(NULL, args);
-
- args.clear();
- args.push_back("SREM");
- args.push_back("ids:" + this->type);
- args.push_back(stringify(this->id));
-
- /* Delete id from ids set */
- me->redis->SendCommand(NULL, args);
-
- for (unsigned i = 0; i + 1 < r.multi_bulk.size(); i += 2)
- {
- const Reply *key = r.multi_bulk[i],
- *value = r.multi_bulk[i + 1];
-
- args.clear();
- args.push_back("SREM");
- args.push_back("value:" + this->type + ":" + key->bulk + ":" + value->bulk);
- args.push_back(stringify(this->id));
-
- /* Delete value -> object id */
- me->redis->SendCommand(NULL, args);
- }
-
- /* Transaction end */
- me->redis->CommitTransaction();
-
- delete this;
-}
-
-void Updater::OnResult(const Reply &r)
-{
- Serialize::Type *st = Serialize::Type::Find(this->type);
-
- if (!st)
- {
- delete this;
- return;
- }
-
- Serializable *obj = st->objects[this->id];
- if (!obj)
- {
- delete this;
- return;
- }
-
- Data data;
- obj->Serialize(data);
-
- /* Transaction start */
- me->redis->StartTransaction();
-
- for (unsigned i = 0; i + 1 < r.multi_bulk.size(); i += 2)
- {
- const Reply *key = r.multi_bulk[i],
- *value = r.multi_bulk[i + 1];
-
- std::vector<Anope::string> args;
- args.push_back("SREM");
- args.push_back("value:" + this->type + ":" + key->bulk + ":" + value->bulk);
- args.push_back(stringify(this->id));
-
- /* Delete value -> object id */
- me->redis->SendCommand(NULL, args);
- }
-
- /* Add object id to id set for this type */
- std::vector<Anope::string> args;
- args.push_back("SADD");
- args.push_back("ids:" + this->type);
- args.push_back(stringify(obj->id));
- me->redis->SendCommand(NULL, args);
-
- args.clear();
- args.push_back("HMSET");
- args.push_back("hash:" + this->type + ":" + stringify(obj->id));
-
- typedef std::map<Anope::string, std::stringstream *> items;
- for (items::iterator it = data.data.begin(), it_end = data.data.end(); it != it_end; ++it)
- {
- const Anope::string &key = it->first;
- std::stringstream *value = it->second;
-
- args.push_back(key);
- args.push_back(value->str());
-
- std::vector<Anope::string> args2;
-
- args2.push_back("SADD");
- args2.push_back("value:" + this->type + ":" + key + ":" + value->str());
- args2.push_back(stringify(obj->id));
-
- /* Add to value -> object id set */
- me->redis->SendCommand(NULL, args2);
- }
-
- ++obj->redis_ignore;
-
- /* Add object */
- me->redis->SendCommand(NULL, args);
-
- /* Transaction end */
- me->redis->CommitTransaction();
-
- delete this;
-}
-
-void SubscriptionListener::OnResult(const Reply &r)
-{
- /*
- * [May 15 13:59:35.645839 2013] Debug: pmessage
- * [May 15 13:59:35.645866 2013] Debug: __keyspace@*__:anope:hash:*
- * [May 15 13:59:35.645880 2013] Debug: __keyspace@0__:anope:hash:type:id
- * [May 15 13:59:35.645893 2013] Debug: hset
- */
- if (r.multi_bulk.size() != 4)
- return;
-
- size_t sz = r.multi_bulk[2]->bulk.find(':');
- if (sz == Anope::string::npos)
- return;
-
- const Anope::string &key = r.multi_bulk[2]->bulk.substr(sz + 1),
- &op = r.multi_bulk[3]->bulk;
-
- sz = key.rfind(':');
- if (sz == Anope::string::npos)
- return;
-
- const Anope::string &id = key.substr(sz + 1);
-
- size_t sz2 = key.rfind(':', sz - 1);
- if (sz2 == Anope::string::npos)
- return;
- const Anope::string &type = key.substr(sz2 + 1, sz - sz2 - 1);
-
- Serialize::Type *s_type = Serialize::Type::Find(type);
-
- if (s_type == NULL)
- return;
-
- uint64_t obj_id;
- try
- {
- obj_id = convertTo<uint64_t>(id);
- }
- catch (const ConvertException &)
- {
- return;
- }
-
- if (op == "hset" || op == "hdel")
- {
- Serializable *s = s_type->objects[obj_id];
-
- if (s && s->redis_ignore)
- {
- --s->redis_ignore;
- Log(LOG_DEBUG) << "redis: notify: got modify for object id " << obj_id << " of type " << type << ", but I am ignoring it";
- }
- else
- {
- Log(LOG_DEBUG) << "redis: notify: got modify for object id " << obj_id << " of type " << type;
-
- std::vector<Anope::string> args;
- args.push_back("HGETALL");
- args.push_back("hash:" + type + ":" + id);
-
- me->redis->SendCommand(new ModifiedObject(me, type, obj_id), args);
- }
- }
- else if (op == "del")
- {
- Serializable* &s = s_type->objects[obj_id];
- if (s == NULL)
- return;
-
- Log(LOG_DEBUG) << "redis: notify: deleting object id " << obj_id << " of type " << type;
-
- Data data;
-
- s->Serialize(data);
-
- /* Transaction start */
- me->redis->StartTransaction();
-
- typedef std::map<Anope::string, std::stringstream *> items;
- for (items::iterator it = data.data.begin(), it_end = data.data.end(); it != it_end; ++it)
- {
- const Anope::string &k = it->first;
- std::stringstream *value = it->second;
-
- std::vector<Anope::string> args;
- args.push_back("SREM");
- args.push_back("value:" + type + ":" + k + ":" + value->str());
- args.push_back(id);
-
- /* Delete value -> object id */
- me->redis->SendCommand(NULL, args);
- }
-
- std::vector<Anope::string> args;
- args.push_back("SREM");
- args.push_back("ids:" + type);
- args.push_back(stringify(s->id));
-
- /* Delete object from id set */
- me->redis->SendCommand(NULL, args);
-
- /* Transaction end */
- me->redis->CommitTransaction();
-
- delete s;
- s = NULL;
- }
-}
-
-void ModifiedObject::OnResult(const Reply &r)
-{
- Serialize::Type *st = Serialize::Type::Find(this->type);
-
- if (!st)
- {
- delete this;
- return;
- }
-
- Serializable* &obj = st->objects[this->id];
-
- /* Transaction start */
- me->redis->StartTransaction();
-
- /* Erase old object values */
- if (obj)
- {
- Data data;
-
- obj->Serialize(data);
-
- typedef std::map<Anope::string, std::stringstream *> items;
- for (items::iterator it = data.data.begin(), it_end = data.data.end(); it != it_end; ++it)
- {
- const Anope::string &key = it->first;
- std::stringstream *value = it->second;
-
- std::vector<Anope::string> args;
- args.push_back("SREM");
- args.push_back("value:" + st->GetName() + ":" + key + ":" + value->str());
- args.push_back(stringify(this->id));
-
- /* Delete value -> object id */
- me->redis->SendCommand(NULL, args);
- }
- }
-
- Data data;
-
- for (unsigned i = 0; i + 1 < r.multi_bulk.size(); i += 2)
- {
- const Reply *key = r.multi_bulk[i],
- *value = r.multi_bulk[i + 1];
-
- data[key->bulk] << value->bulk;
- }
-
- obj = st->Unserialize(obj, data);
- if (obj)
- {
- obj->id = this->id;
- obj->UpdateCache(data);
-
- /* Insert new object values */
- typedef std::map<Anope::string, std::stringstream *> items;
- for (items::iterator it = data.data.begin(), it_end = data.data.end(); it != it_end; ++it)
- {
- const Anope::string &key = it->first;
- std::stringstream *value = it->second;
-
- std::vector<Anope::string> args;
- args.push_back("SADD");
- args.push_back("value:" + st->GetName() + ":" + key + ":" + value->str());
- args.push_back(stringify(obj->id));
-
- /* Add to value -> object id set */
- me->redis->SendCommand(NULL, args);
- }
-
- std::vector<Anope::string> args;
- args.push_back("SADD");
- args.push_back("ids:" + st->GetName());
- args.push_back(stringify(obj->id));
-
- /* Add to type -> id set */
- me->redis->SendCommand(NULL, args);
- }
-
- /* Transaction end */
- me->redis->CommitTransaction();
-
- delete this;
-}
-
-MODULE_INIT(DatabaseRedis)
diff --git a/modules/database/db_sql.cpp b/modules/database/db_sql.cpp
deleted file mode 100644
index 15501bc5f..000000000
--- a/modules/database/db_sql.cpp
+++ /dev/null
@@ -1,261 +0,0 @@
-/*
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-#include "modules/sql.h"
-
-using namespace SQL;
-
-class SQLSQLInterface : public Interface
-{
- public:
- SQLSQLInterface(Module *o) : Interface(o) { }
-
- void OnResult(const Result &r) anope_override
- {
- Log(LOG_DEBUG) << "SQL successfully executed query: " << r.finished_query;
- }
-
- void OnError(const Result &r) anope_override
- {
- if (!r.GetQuery().query.empty())
- Log(LOG_DEBUG) << "Error executing query " << r.finished_query << ": " << r.GetError();
- else
- Log(LOG_DEBUG) << "Error executing query: " << r.GetError();
- }
-};
-
-class ResultSQLSQLInterface : public SQLSQLInterface
-{
- Reference<Serializable> obj;
-
-public:
- ResultSQLSQLInterface(Module *o, Serializable *ob) : SQLSQLInterface(o), obj(ob) { }
-
- void OnResult(const Result &r) anope_override
- {
- SQLSQLInterface::OnResult(r);
- if (r.GetID() > 0 && this->obj)
- this->obj->id = r.GetID();
- delete this;
- }
-
- void OnError(const Result &r) anope_override
- {
- SQLSQLInterface::OnError(r);
- delete this;
- }
-};
-
-class DBSQL : public Module, public Pipe
-{
- ServiceReference<Provider> sql;
- SQLSQLInterface sqlinterface;
- Anope::string prefix;
- bool import;
-
- std::set<Serializable *> updated_items;
- bool shutting_down;
- bool loading_databases;
- bool loaded;
- bool imported;
-
- void RunBackground(const Query &q, Interface *iface = NULL)
- {
- if (!this->sql)
- {
- static time_t last_warn = 0;
- if (last_warn + 300 < Anope::CurTime)
- {
- last_warn = Anope::CurTime;
- Log(this) << "db_sql: Unable to execute query, is SQL configured correctly?";
- }
- }
- else if (!Anope::Quitting)
- {
- if (iface == NULL)
- iface = &this->sqlinterface;
- this->sql->Run(iface, q);
- }
- else
- this->sql->RunQuery(q);
- }
-
- public:
- DBSQL(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, DATABASE | VENDOR), sql("", ""), sqlinterface(this), shutting_down(false), loading_databases(false), loaded(false), imported(false)
- {
-
-
- if (ModuleManager::FindModule("db_sql_live") != NULL)
- throw ModuleException("db_sql can not be loaded after db_sql_live");
- }
-
- void OnNotify() anope_override
- {
- for (std::set<Serializable *>::iterator it = this->updated_items.begin(), it_end = this->updated_items.end(); it != it_end; ++it)
- {
- Serializable *obj = *it;
-
- if (this->sql)
- {
- Data data;
- obj->Serialize(data);
-
- if (obj->IsCached(data))
- continue;
-
- obj->UpdateCache(data);
-
- /* If we didn't load these objects and we don't want to import just update the cache and continue */
- if (!this->loaded && !this->imported && !this->import)
- continue;
-
- Serialize::Type *s_type = obj->GetSerializableType();
- if (!s_type)
- continue;
-
- std::vector<Query> create = this->sql->CreateTable(this->prefix + s_type->GetName(), data);
- for (unsigned i = 0; i < create.size(); ++i)
- this->RunBackground(create[i]);
-
- Query insert = this->sql->BuildInsert(this->prefix + s_type->GetName(), obj->id, data);
- if (this->imported)
- this->RunBackground(insert, new ResultSQLSQLInterface(this, obj));
- else
- {
- /* We are importing objects from another database module, so don't do asynchronous
- * queries in case the core has to shut down, it will cut short the import
- */
- Result r = this->sql->RunQuery(insert);
- if (r.GetID() > 0)
- obj->id = r.GetID();
- }
- }
- }
-
- this->updated_items.clear();
- this->imported = true;
- }
-
- void OnReload(Configuration::Conf *conf) anope_override
- {
- Configuration::Block *block = conf->GetModule(this);
- this->sql = ServiceReference<Provider>("SQL::Provider", block->Get<const Anope::string>("engine"));
- this->prefix = block->Get<const Anope::string>("prefix", "anope_db_");
- this->import = block->Get<bool>("import");
- }
-
- void OnShutdown() anope_override
- {
- this->shutting_down = true;
- this->OnNotify();
- }
-
- void OnRestart() anope_override
- {
- this->OnShutdown();
- }
-
- EventReturn OnLoadDatabase() anope_override
- {
- if (!this->sql)
- {
- Log(this) << "Unable to load databases, is SQL configured correctly?";
- return EVENT_CONTINUE;
- }
-
- this->loading_databases = true;
-
- const std::vector<Anope::string> type_order = Serialize::Type::GetTypeOrder();
- for (unsigned i = 0; i < type_order.size(); ++i)
- {
- Serialize::Type *sb = Serialize::Type::Find(type_order[i]);
- this->OnSerializeTypeCreate(sb);
- }
-
- this->loading_databases = false;
- this->loaded = true;
-
- return EVENT_STOP;
- }
-
- void OnSerializableConstruct(Serializable *obj) anope_override
- {
- if (this->shutting_down || this->loading_databases)
- return;
- obj->UpdateTS();
- this->updated_items.insert(obj);
- this->Notify();
- }
-
- void OnSerializableDestruct(Serializable *obj) anope_override
- {
- if (this->shutting_down)
- return;
- Serialize::Type *s_type = obj->GetSerializableType();
- if (s_type && obj->id > 0)
- this->RunBackground("DELETE FROM `" + this->prefix + s_type->GetName() + "` WHERE `id` = " + stringify(obj->id));
- this->updated_items.erase(obj);
- }
-
- void OnSerializableUpdate(Serializable *obj) anope_override
- {
- if (this->shutting_down || obj->IsTSCached())
- return;
- obj->UpdateTS();
- this->updated_items.insert(obj);
- this->Notify();
- }
-
- void OnSerializeTypeCreate(Serialize::Type *sb) anope_override
- {
- if (!this->loading_databases && !this->loaded)
- return;
-
- Query query("SELECT * FROM `" + this->prefix + sb->GetName() + "`");
- Result res = this->sql->RunQuery(query);
-
- for (int j = 0; j < res.Rows(); ++j)
- {
- Data data;
-
- const std::map<Anope::string, Anope::string> &row = res.Row(j);
- for (std::map<Anope::string, Anope::string>::const_iterator rit = row.begin(), rit_end = row.end(); rit != rit_end; ++rit)
- data[rit->first] << rit->second;
-
- Serializable *obj = sb->Unserialize(NULL, data);
- try
- {
- if (obj)
- obj->id = convertTo<unsigned int>(res.Get(j, "id"));
- }
- catch (const ConvertException &)
- {
- Log(this) << "Unable to convert id for object #" << j << " of type " << sb->GetName();
- }
-
- if (obj)
- {
- /* The Unserialize operation is destructive so rebuild the data for UpdateCache.
- * Also the old data may contain columns that we don't use, so we reserialize the
- * object to know for sure our cache is consistent
- */
-
- Data data2;
- obj->Serialize(data2);
- obj->UpdateCache(data2); /* We know this is the most up to date copy */
- }
- }
- }
-};
-
-MODULE_INIT(DBSQL)
-
diff --git a/modules/database/db_sql_live.cpp b/modules/database/db_sql_live.cpp
deleted file mode 100644
index 7dfde3d02..000000000
--- a/modules/database/db_sql_live.cpp
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
- *
- * (C) 2012-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- */
-
-#include "module.h"
-#include "modules/sql.h"
-
-using namespace SQL;
-
-class DBMySQL : public Module, public Pipe
-{
- private:
- Anope::string prefix;
- ServiceReference<Provider> SQL;
- time_t lastwarn;
- bool ro;
- bool init;
- std::set<Serializable *> updated_items;
-
- bool CheckSQL()
- {
- if (SQL)
- {
- if (Anope::ReadOnly && this->ro)
- {
- Anope::ReadOnly = this->ro = false;
- Log() << "Found SQL again, going out of readonly mode...";
- }
-
- return true;
- }
- else
- {
- if (Anope::CurTime - Config->GetBlock("options")->Get<time_t>("updatetimeout", "5m") > lastwarn)
- {
- Log() << "Unable to locate SQL reference, going to readonly...";
- Anope::ReadOnly = this->ro = true;
- this->lastwarn = Anope::CurTime;
- }
-
- return false;
- }
- }
-
- bool CheckInit()
- {
- return init && SQL;
- }
-
- void RunQuery(const Query &query)
- {
- /* Can this be threaded? */
- this->RunQueryResult(query);
- }
-
- Result RunQueryResult(const Query &query)
- {
- if (this->CheckSQL())
- {
- Result res = SQL->RunQuery(query);
- if (!res.GetError().empty())
- Log(LOG_DEBUG) << "SQL-live got error " << res.GetError() << " for " + res.finished_query;
- else
- Log(LOG_DEBUG) << "SQL-live got " << res.Rows() << " rows for " << res.finished_query;
- return res;
- }
- throw SQL::Exception("No SQL!");
- }
-
- public:
- DBMySQL(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, DATABASE | VENDOR), SQL("", "")
- {
- this->lastwarn = 0;
- this->ro = false;
- this->init = false;
-
-
- if (ModuleManager::FindFirstOf(DATABASE) != this)
- throw ModuleException("If db_sql_live is loaded it must be the first database module loaded.");
- }
-
- void OnNotify() anope_override
- {
- if (!this->CheckInit())
- return;
-
- for (std::set<Serializable *>::iterator it = this->updated_items.begin(), it_end = this->updated_items.end(); it != it_end; ++it)
- {
- Serializable *obj = *it;
-
- if (obj && this->SQL)
- {
- Data data;
- obj->Serialize(data);
-
- if (obj->IsCached(data))
- continue;
-
- obj->UpdateCache(data);
-
- Serialize::Type *s_type = obj->GetSerializableType();
- if (!s_type)
- continue;
-
- std::vector<Query> create = this->SQL->CreateTable(this->prefix + s_type->GetName(), data);
- for (unsigned i = 0; i < create.size(); ++i)
- this->RunQueryResult(create[i]);
-
- Result res = this->RunQueryResult(this->SQL->BuildInsert(this->prefix + s_type->GetName(), obj->id, data));
- if (res.GetID() && obj->id != res.GetID())
- {
- /* In this case obj is new, so place it into the object map */
- obj->id = res.GetID();
- s_type->objects[obj->id] = obj;
- }
- }
- }
-
- this->updated_items.clear();
- }
-
- EventReturn OnLoadDatabase() anope_override
- {
- init = true;
- return EVENT_STOP;
- }
-
- void OnShutdown() anope_override
- {
- init = false;
- }
-
- void OnRestart() anope_override
- {
- init = false;
- }
-
- void OnReload(Configuration::Conf *conf) anope_override
- {
- Configuration::Block *block = conf->GetModule(this);
- this->SQL = ServiceReference<Provider>("SQL::Provider", block->Get<const Anope::string>("engine"));
- this->prefix = block->Get<const Anope::string>("prefix", "anope_db_");
- }
-
- void OnSerializableConstruct(Serializable *obj) anope_override
- {
- if (!this->CheckInit())
- return;
- obj->UpdateTS();
- this->updated_items.insert(obj);
- this->Notify();
- }
-
- void OnSerializableDestruct(Serializable *obj) anope_override
- {
- if (!this->CheckInit())
- return;
- Serialize::Type *s_type = obj->GetSerializableType();
- if (s_type)
- {
- if (obj->id > 0)
- this->RunQuery("DELETE FROM `" + this->prefix + s_type->GetName() + "` WHERE `id` = " + stringify(obj->id));
- s_type->objects.erase(obj->id);
- }
- this->updated_items.erase(obj);
- }
-
- void OnSerializeCheck(Serialize::Type *obj) anope_override
- {
- if (!this->CheckInit() || obj->GetTimestamp() == Anope::CurTime)
- return;
-
- Query query("SELECT * FROM `" + this->prefix + obj->GetName() + "` WHERE (`timestamp` >= " + this->SQL->FromUnixtime(obj->GetTimestamp()) + " OR `timestamp` IS NULL)");
-
- obj->UpdateTimestamp();
-
- Result res = this->RunQueryResult(query);
-
- bool clear_null = false;
- for (int i = 0; i < res.Rows(); ++i)
- {
- const std::map<Anope::string, Anope::string> &row = res.Row(i);
-
- unsigned int id;
- try
- {
- id = convertTo<unsigned int>(res.Get(i, "id"));
- }
- catch (const ConvertException &)
- {
- Log(LOG_DEBUG) << "Unable to convert id from " << obj->GetName();
- continue;
- }
-
- if (res.Get(i, "timestamp").empty())
- {
- clear_null = true;
- std::map<uint64_t, Serializable *>::iterator it = obj->objects.find(id);
- if (it != obj->objects.end())
- delete it->second; // This also removes this object from the map
- }
- else
- {
- Data data;
-
- for (std::map<Anope::string, Anope::string>::const_iterator it = row.begin(), it_end = row.end(); it != it_end; ++it)
- data[it->first] << it->second;
-
- Serializable *s = NULL;
- std::map<uint64_t, Serializable *>::iterator it = obj->objects.find(id);
- if (it != obj->objects.end())
- s = it->second;
-
- Serializable *new_s = obj->Unserialize(s, data);
- if (new_s)
- {
- // If s == new_s then s->id == new_s->id
- if (s != new_s)
- {
- new_s->id = id;
- obj->objects[id] = new_s;
-
- /* The Unserialize operation is destructive so rebuild the data for UpdateCache.
- * Also the old data may contain columns that we don't use, so we reserialize the
- * object to know for sure our cache is consistent
- */
-
- Data data2;
- new_s->Serialize(data2);
- new_s->UpdateCache(data2); /* We know this is the most up to date copy */
- }
- }
- else
- {
- if (!s)
- this->RunQuery("UPDATE `" + prefix + obj->GetName() + "` SET `timestamp` = " + this->SQL->FromUnixtime(obj->GetTimestamp()) + " WHERE `id` = " + stringify(id));
- else
- delete s;
- }
- }
- }
-
- if (clear_null)
- {
- query = "DELETE FROM `" + this->prefix + obj->GetName() + "` WHERE `timestamp` IS NULL";
- this->RunQuery(query);
- }
- }
-
- void OnSerializableUpdate(Serializable *obj) anope_override
- {
- if (!this->CheckInit() || obj->IsTSCached())
- return;
- obj->UpdateTS();
- this->updated_items.insert(obj);
- this->Notify();
- }
-};
-
-MODULE_INIT(DBMySQL)
-
diff --git a/modules/database/flatfile.cpp b/modules/database/flatfile.cpp
new file mode 100644
index 000000000..b45a89d44
--- /dev/null
+++ b/modules/database/flatfile.cpp
@@ -0,0 +1,218 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2011-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+
+class DBFlatFile : public Module
+ , public EventHook<Event::LoadDatabase>
+ , public EventHook<Event::SaveDatabase>
+{
+ /* Day the last backup was on */
+ int last_day;
+ /* Backup file names */
+ std::map<Anope::string, std::list<Anope::string> > backups;
+ bool loaded;
+
+ void BackupDatabase()
+ {
+ tm *tm = localtime(&Anope::CurTime);
+
+ if (tm->tm_mday != last_day)
+ {
+ last_day = tm->tm_mday;
+
+ const std::map<Anope::string, Serialize::TypeBase *> &types = Serialize::TypeBase::GetTypes();
+
+ std::set<Anope::string> dbs;
+ dbs.insert(Config->GetModule(this)->Get<Anope::string>("database", "anope.db"));
+
+ for (const std::pair<Anope::string, Serialize::TypeBase *> &p : types)
+ {
+ Serialize::TypeBase *stype = p.second;
+
+ if (stype->GetOwner())
+ dbs.insert("module_" + stype->GetOwner()->name + ".db");
+ }
+
+
+ for (std::set<Anope::string>::const_iterator it = dbs.begin(), it_end = dbs.end(); it != it_end; ++it)
+ {
+ const Anope::string &oldname = Anope::DataDir + "/" + *it;
+ Anope::string newname = Anope::DataDir + "/backups/" + *it + "-" + stringify(tm->tm_year + 1900) + Anope::printf("-%02i-", tm->tm_mon + 1) + Anope::printf("%02i", tm->tm_mday);
+
+ /* Backup already exists or no database to backup */
+ if (Anope::IsFile(newname) || !Anope::IsFile(oldname))
+ continue;
+
+ Log(LOG_DEBUG) << "db_flatfile: Attempting to rename " << *it << " to " << newname;
+ if (rename(oldname.c_str(), newname.c_str()))
+ {
+ Anope::string err = Anope::LastError();
+ Log(this) << "Unable to back up database " << *it << " (" << err << ")!";
+
+ if (!Config->GetModule(this)->Get<bool>("nobackupokay"))
+ {
+ Anope::Quitting = true;
+ Anope::QuitReason = "Unable to back up database " + *it + " (" + err + ")";
+ }
+
+ continue;
+ }
+
+ backups[*it].push_back(newname);
+
+ unsigned keepbackups = Config->GetModule(this)->Get<unsigned>("keepbackups");
+ if (keepbackups > 0 && backups[*it].size() > keepbackups)
+ {
+ unlink(backups[*it].front().c_str());
+ backups[*it].pop_front();
+ }
+ }
+ }
+ }
+
+ public:
+ DBFlatFile(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, DATABASE | VENDOR)
+ , EventHook<Event::LoadDatabase>(this)
+ , EventHook<Event::SaveDatabase>(this)
+ , last_day(0)
+ , loaded(false)
+ {
+
+ }
+
+ EventReturn OnLoadDatabase() override
+ {
+ const Anope::string &db_name = Anope::DataDir + "/" + Config->GetModule(this)->Get<Anope::string>("database", "anope.db");
+
+ std::fstream fd(db_name.c_str(), std::ios_base::in | std::ios_base::binary);
+ if (!fd.is_open())
+ {
+ Log(this) << "Unable to open " << db_name << " for reading!";
+ return EVENT_STOP;
+ }
+
+ Serialize::TypeBase *type = nullptr;
+ Serialize::Object *obj = nullptr;
+ for (Anope::string buf; std::getline(fd, buf.str());)
+ {
+ if (buf.find("OBJECT ") == 0)
+ {
+ Anope::string t = buf.substr(7);
+ if (obj)
+ Log(LOG_DEBUG) << "obj != null but got OBJECT";
+ if (type)
+ Log(LOG_DEBUG) << "type != null but got OBJECT";
+ type = Serialize::TypeBase::Find(t);
+ obj = nullptr;
+ }
+ else if (buf.find("ID ") == 0)
+ {
+ if (!type || obj)
+ continue;
+
+ try
+ {
+ Serialize::ID id = convertTo<Serialize::ID>(buf.substr(3));
+ obj = type->Require(id);
+ }
+ catch (const ConvertException &)
+ {
+ Log(LOG_DEBUG) << "Unable to parse object id " << buf.substr(3);
+ }
+ }
+ else if (buf.find("DATA ") == 0)
+ {
+ if (!type)
+ continue;
+
+ if (!obj)
+ obj = type->Create();
+
+ size_t sp = buf.find(' ', 5); // Skip DATA
+ if (sp == Anope::string::npos)
+ continue;
+
+ Anope::string key = buf.substr(5, sp - 5), value = buf.substr(sp + 1);
+
+ Serialize::FieldBase *field = type->GetField(key);
+ if (field)
+ field->UnserializeFromString(obj, value);
+ }
+ else if (buf.find("END") == 0)
+ {
+ type = nullptr;
+ obj = nullptr;
+ }
+ }
+
+ fd.close();
+
+ loaded = true;
+ return EVENT_STOP;
+ }
+
+
+ void OnSaveDatabase() override
+ {
+ BackupDatabase();
+
+ Anope::string db_name = Anope::DataDir + "/" + Config->GetModule(this)->Get<Anope::string>("database", "anope.db");
+
+ if (Anope::IsFile(db_name))
+ rename(db_name.c_str(), (db_name + ".tmp").c_str());
+
+ std::fstream f(db_name.c_str(), std::ios_base::out | std::ios_base::trunc | std::ios_base::binary);
+
+ if (!f.is_open())
+ {
+ Log(this) << "Unable to open " << db_name << " for writing";
+ }
+ else
+ {
+ for (std::pair<Serialize::ID, Serialize::Object *> p : Serialize::objects)
+ {
+ Serialize::Object *object = p.second;
+ Serialize::TypeBase *s_type = object->GetSerializableType();
+
+ f << "OBJECT " << s_type->GetName() << "\n";
+ f << "ID " << object->id << "\n";
+ for (Serialize::FieldBase *field : s_type->GetFields())
+ if (field->HasFieldS(object)) // for ext
+ f << "DATA " << field->serialize_name << " " << field->SerializeToString(object) << "\n";
+ f << "END\n";
+ }
+ }
+
+ if (!f.is_open() || !f.good())
+ {
+ f.close();
+ rename((db_name + ".tmp").c_str(), db_name.c_str());
+ }
+ else
+ {
+ f.close();
+ unlink((db_name + ".tmp").c_str());
+ }
+ }
+};
+
+MODULE_INIT(DBFlatFile)
+
+
diff --git a/modules/database/db_old.cpp b/modules/database/old.cpp
index 00524fdc3..640b45973 100644
--- a/modules/database/db_old.cpp
+++ b/modules/database/old.cpp
@@ -1,23 +1,36 @@
/*
+ * Anope IRC Services
*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
*
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
+/* Dependencies: anope_chanserv.access */
+
#include "module.h"
-#include "modules/os_session.h"
-#include "modules/bs_kick.h"
-#include "modules/cs_mode.h"
-#include "modules/bs_badwords.h"
-#include "modules/os_news.h"
-#include "modules/suspend.h"
-#include "modules/os_forbid.h"
-#include "modules/cs_entrymsg.h"
+#include "modules/operserv/session.h"
+#include "modules/botserv/kick.h"
+#include "modules/chanserv/mode.h"
+#include "modules/botserv/badwords.h"
+#include "modules/operserv/news.h"
+#include "modules/operserv/forbid.h"
+#include "modules/chanserv/entrymsg.h"
+#include "modules/nickserv/suspend.h"
+#include "modules/chanserv/suspend.h"
+#include "modules/chanserv/access.h"
+#include "modules/nickserv/access.h"
#define READ(x) \
if (true) \
@@ -94,6 +107,18 @@ else \
#define OLD_NEWS_OPER 1
#define OLD_NEWS_RANDOM 2
+enum
+{
+ TTB_BOLDS,
+ TTB_COLORS,
+ TTB_REVERSES,
+ TTB_UNDERLINES,
+ TTB_BADWORDS,
+ TTB_CAPS,
+ TTB_FLOOD,
+ TTB_REPEAT,
+};
+
static struct mlock_info
{
char c;
@@ -143,21 +168,25 @@ enum
LANG_PL /* Polish */
};
-static void process_mlock(ChannelInfo *ci, uint32_t lock, bool status, uint32_t *limit, Anope::string *key)
+static void process_mlock(ChanServ::Channel *ci, uint32_t lock, bool status, uint32_t *limit, Anope::string *key)
{
- ModeLocks *ml = ci->Require<ModeLocks>("modelocks");
+ ServiceReference<ModeLocks> mlocks;
+
+ if (!mlocks)
+ return;
+
for (unsigned i = 0; i < (sizeof(mlock_infos) / sizeof(mlock_info)); ++i)
if (lock & mlock_infos[i].m)
{
ChannelMode *cm = ModeManager::FindChannelModeByChar(mlock_infos[i].c);
- if (cm && ml)
+ if (cm)
{
if (limit && mlock_infos[i].c == 'l')
- ml->SetMLock(cm, status, stringify(*limit));
+ mlocks->SetMLock(ci, cm, status, stringify(*limit));
else if (key && mlock_infos[i].c == 'k')
- ml->SetMLock(cm, status, *key);
+ mlocks->SetMLock(ci, cm, status, *key);
else
- ml->SetMLock(cm, status);
+ mlocks->SetMLock(ci, cm, status);
}
}
}
@@ -435,7 +464,8 @@ int read_int32(int32_t *ret, dbFILE *f)
static void LoadNicks()
{
- ServiceReference<ForbidService> forbid("ForbidService", "forbid");
+ if (!NickServ::service)
+ return;
dbFILE *f = open_db_read("NickServ", "nick.db", 14);
if (f == NULL)
return;
@@ -445,27 +475,33 @@ static void LoadNicks()
Anope::string buffer;
READ(read_string(buffer, f));
- NickCore *nc = new NickCore(buffer);
+
+ NickServ::Account *nc = Serialize::New<NickServ::Account *>();
+ nc->SetDisplay(buffer);
const Anope::string settings[] = { "killprotect", "kill_quick", "ns_secure", "ns_private", "hide_email",
"hide_mask", "hide_quit", "memo_signon", "memo_receive", "autoop", "msg", "ns_keepmodes" };
for (unsigned j = 0; j < sizeof(settings) / sizeof(Anope::string); ++j)
- nc->Shrink<bool>(settings[j].upper());
+ nc->UnsetS<bool>(settings[j].upper());
char pwbuf[32];
READ(read_buffer(pwbuf, f));
if (hashm == "plain")
- my_b64_encode(pwbuf, nc->pass);
+ {
+ Anope::string p;
+ my_b64_encode(pwbuf, p);
+ nc->SetPassword(p);
+ }
else if (hashm == "md5" || hashm == "oldmd5")
- nc->pass = Hex(pwbuf, 16);
+ nc->SetPassword(Hex(pwbuf, 16));
else if (hashm == "sha1")
- nc->pass = Hex(pwbuf, 20);
+ nc->SetPassword(Hex(pwbuf, 20));
else
- nc->pass = Hex(pwbuf, strlen(pwbuf));
- nc->pass = hashm + ":" + nc->pass;
+ nc->SetPassword(Hex(pwbuf, strlen(pwbuf)));
+ nc->SetPassword(hashm + ":" + nc->GetPassword());
READ(read_string(buffer, f));
- nc->email = buffer;
+ nc->SetEmail(buffer);
READ(read_string(buffer, f));
if (!buffer.empty())
@@ -480,122 +516,134 @@ static void LoadNicks()
READ(read_uint32(&u32, f));
if (u32 & OLD_NI_KILLPROTECT)
- nc->Extend<bool>("KILLPROTECT");
+ nc->SetS<bool>("KILLPROTECT", true);
if (u32 & OLD_NI_SECURE)
- nc->Extend<bool>("NS_SECURE");
+ nc->SetS<bool>("NS_SECURE", true);
if (u32 & OLD_NI_MSG)
- nc->Extend<bool>("MSG");
+ nc->SetS<bool>("MSG", true);
if (u32 & OLD_NI_MEMO_HARDMAX)
- nc->Extend<bool>("MEMO_HARDMAX");
+ nc->SetS<bool>("MEMO_HARDMAX", true);
if (u32 & OLD_NI_MEMO_SIGNON)
- nc->Extend<bool>("MEMO_SIGNON");
+ nc->SetS<bool>("MEMO_SIGNON", true);
if (u32 & OLD_NI_MEMO_RECEIVE)
- nc->Extend<bool>("MEMO_RECEIVE");
+ nc->SetS<bool>("MEMO_RECEIVE", true);
if (u32 & OLD_NI_PRIVATE)
- nc->Extend<bool>("NS_PRIVATE");
+ nc->SetS<bool>("NS_PRIVATE", true);
if (u32 & OLD_NI_HIDE_EMAIL)
- nc->Extend<bool>("HIDE_EMAIL");
+ nc->SetS<bool>("HIDE_EMAIL", true);
if (u32 & OLD_NI_HIDE_MASK)
- nc->Extend<bool>("HIDE_MASK");
+ nc->SetS<bool>("HIDE_MASK", true);
if (u32 & OLD_NI_HIDE_QUIT)
- nc->Extend<bool>("HIDE_QUIT");
+ nc->SetS<bool>("HIDE_QUIT", true);
if (u32 & OLD_NI_KILL_QUICK)
- nc->Extend<bool>("KILL_QUICK");
+ nc->SetS<bool>("KILL_QUICK", true);
if (u32 & OLD_NI_KILL_IMMED)
- nc->Extend<bool>("KILL_IMMED");
+ nc->SetS<bool>("KILL_IMMED", true);
if (u32 & OLD_NI_MEMO_MAIL)
- nc->Extend<bool>("MEMO_MAIL");
+ nc->SetS<bool>("MEMO_MAIL", true);
if (u32 & OLD_NI_HIDE_STATUS)
- nc->Extend<bool>("HIDE_STATUS");
+ nc->SetS<bool>("HIDE_STATUS", true);
if (u32 & OLD_NI_SUSPENDED)
{
- SuspendInfo si;
- si.what = nc->display;
- si.when = si.expires = 0;
- nc->Extend("NS_SUSPENDED", si);
+ NSSuspendInfo *si = Serialize::New<NSSuspendInfo *>();
+ if (si)
+ {
+ si->SetAccount(nc);
+ }
}
if (!(u32 & OLD_NI_AUTOOP))
- nc->Extend<bool>("AUTOOP");
+ nc->SetS<bool>("AUTOOP", true);
uint16_t u16;
READ(read_uint16(&u16, f));
switch (u16)
{
case LANG_ES:
- nc->language = "es_ES";
+ nc->SetLanguage("es_ES");
break;
case LANG_PT:
- nc->language = "pt_PT";
+ nc->SetLanguage("pt_PT");
break;
case LANG_FR:
- nc->language = "fr_FR";
+ nc->SetLanguage("fr_FR");
break;
case LANG_TR:
- nc->language = "tr_TR";
+ nc->SetLanguage("tr_TR");
break;
case LANG_IT:
- nc->language = "it_IT";
+ nc->SetLanguage("it_IT");
break;
case LANG_DE:
- nc->language = "de_DE";
+ nc->SetLanguage("de_DE");
break;
case LANG_CAT:
- nc->language = "ca_ES"; // yes, iso639 defines catalan as CA
+ nc->SetLanguage("ca_ES"); // yes, iso639 defines catalan as CA
break;
case LANG_GR:
- nc->language = "el_GR";
+ nc->SetLanguage("el_GR");
break;
case LANG_NL:
- nc->language = "nl_NL";
+ nc->SetLanguage("nl_NL");
break;
case LANG_RU:
- nc->language = "ru_RU";
+ nc->SetLanguage("ru_RU");
break;
case LANG_HUN:
- nc->language = "hu_HU";
+ nc->SetLanguage("hu_HU");
break;
case LANG_PL:
- nc->language = "pl_PL";
+ nc->SetLanguage("pl_PL");
break;
case LANG_EN_US:
case LANG_JA_JIS:
case LANG_JA_EUC:
case LANG_JA_SJIS: // these seem to be unused
default:
- nc->language = "en";
+ nc->SetLanguage("en");
}
READ(read_uint16(&u16, f));
for (uint16_t j = 0; j < u16; ++j)
{
READ(read_string(buffer, f));
- nc->access.push_back(buffer);
+
+ NickAccess *a = Serialize::New<NickAccess *>();
+ if (a)
+ {
+ a->SetAccount(nc);
+ a->SetMask(buffer);
+ }
}
int16_t i16;
READ(read_int16(&i16, f));
- READ(read_int16(&nc->memos.memomax, f));
+ READ(read_int16(&i16, f));
+ MemoServ::MemoInfo *mi = nc->GetMemos();
+ if (mi)
+ mi->SetMemoMax(i16);
for (int16_t j = 0; j < i16; ++j)
{
- Memo *m = new Memo;
+ MemoServ::Memo *m = Serialize::New<MemoServ::Memo *>();
READ(read_uint32(&u32, f));
uint16_t flags;
READ(read_uint16(&flags, f));
int32_t tmp32;
READ(read_int32(&tmp32, f));
- m->time = tmp32;
+ if (m)
+ m->SetTime(tmp32);
char sbuf[32];
READ(read_buffer(sbuf, f));
- m->sender = sbuf;
- READ(read_string(m->text, f));
- m->owner = nc->display;
- nc->memos.memos->push_back(m);
- m->mi = &nc->memos;
+ if (m)
+ m->SetSender(sbuf);
+ Anope::string text;
+ READ(read_string(text, f));
+ if (m)
+ m->SetText(text);
}
READ(read_uint16(&u16, f));
READ(read_int16(&i16, f));
- Log(LOG_DEBUG) << "Loaded NickCore " << nc->display;
+ Log(LOG_DEBUG) << "Loaded NickServ::Account " << nc->GetDisplay();
}
for (int i = 0; i < 1024; ++i)
@@ -620,7 +668,7 @@ static void LoadNicks()
Anope::string core;
READ(read_string(core, f));
- NickCore *nc = NickCore::Find(core);
+ NickServ::Account *nc = NickServ::FindAccount(core);
if (nc == NULL)
{
Log() << "Skipping coreless nick " << nick << " with core " << core;
@@ -629,41 +677,38 @@ static void LoadNicks()
if (tmpu16 & OLD_NS_VERBOTEN)
{
- if (!forbid)
+ if (nc->GetDisplay().find_first_of("?*") != Anope::string::npos)
{
delete nc;
continue;
}
- if (nc->display.find_first_of("?*") != Anope::string::npos)
+ ForbidData *d = Serialize::New<ForbidData *>();
+ if (d)
{
- delete nc;
- continue;
+ d->SetMask(nc->GetDisplay());
+ d->SetCreator(last_usermask);
+ d->SetReason(last_realname);
+ d->SetType(FT_NICK);
}
-
- ForbidData *d = forbid->CreateForbid();
- d->mask = nc->display;
- d->creator = last_usermask;
- d->reason = last_realname;
- d->expires = 0;
- d->created = 0;
- d->type = FT_NICK;
+
delete nc;
- forbid->AddForbid(d);
continue;
}
- NickAlias *na = new NickAlias(nick, nc);
- na->last_usermask = last_usermask;
- na->last_realname = last_realname;
- na->last_quit = last_quit;
- na->time_registered = time_registered;
- na->last_seen = last_seen;
+ NickServ::Nick *na = Serialize::New<NickServ::Nick *>();
+ na->SetNick(nick);
+ na->SetAccount(nc);
+ na->SetLastUsermask(last_usermask);
+ na->SetLastRealname(last_realname);
+ na->SetLastQuit(last_quit);
+ na->SetTimeRegistered(time_registered);
+ na->SetLastSeen(last_seen);
if (tmpu16 & OLD_NS_NO_EXPIRE)
- na->Extend<bool>("NS_NO_EXPIRE");
+ na->SetS<bool>("NS_NO_EXPIRE", true);
- Log(LOG_DEBUG) << "Loaded NickAlias " << na->nick;
+ Log(LOG_DEBUG) << "Loaded NickServ::Nick " << na->GetNick();
}
close_db(f); /* End of section Ia */
@@ -686,7 +731,7 @@ static void LoadVHosts()
READ(read_string(creator, f));
READ(read_int32(&vtime, f));
- NickAlias *na = NickAlias::Find(nick);
+ NickServ::Nick *na = NickServ::FindNick(nick);
if (na == NULL)
{
Log() << "Removing vhost for non-existent nick " << nick;
@@ -695,7 +740,7 @@ static void LoadVHosts()
na->SetVhost(ident, host, creator, vtime);
- Log() << "Loaded vhost for " << na->nick;
+ Log() << "Loaded vhost for " << na->GetNick();
}
close_db(f);
@@ -721,13 +766,13 @@ static void LoadBots()
READ(read_int32(&created, f));
READ(read_int16(&chancount, f));
- BotInfo *bi = BotInfo::Find(nick, true);
+ ServiceBot *bi = ServiceBot::Find(nick, true);
if (!bi)
- bi = new BotInfo(nick, user, host, real);
- bi->created = created;
+ bi = new ServiceBot(nick, user, host, real);
+ bi->bi->SetCreated(created);
if (flags & OLD_BI_PRIVATE)
- bi->oper_only = true;
+ bi->bi->SetOperOnly(true);
Log(LOG_DEBUG) << "Loaded bot " << bi->nick;
}
@@ -737,7 +782,13 @@ static void LoadBots()
static void LoadChannels()
{
- ServiceReference<ForbidService> forbid("ForbidService", "forbid");
+ ServiceReference<BadWords> badwords;
+ ServiceReference<ChanServ::ChanServService> chanserv;
+
+ if (!chanserv)
+ return;
+
+ ServiceReference<ForbidService> forbid;
dbFILE *f = open_db_read("ChanServ", "chan.db", 16);
if (f == NULL)
return;
@@ -748,87 +799,92 @@ static void LoadChannels()
Anope::string buffer;
char namebuf[64];
READ(read_buffer(namebuf, f));
- ChannelInfo *ci = new ChannelInfo(namebuf);
+ ChanServ::Channel *ci = Serialize::New<ChanServ::Channel *>();
+ ci->SetName(namebuf);
const Anope::string settings[] = { "keeptopic", "peace", "cs_private", "restricted", "cs_secure", "secureops", "securefounder",
"signkick", "signkick_level", "topiclock", "persist", "noautoop", "cs_keepmodes" };
for (unsigned j = 0; j < sizeof(settings) / sizeof(Anope::string); ++j)
- ci->Shrink<bool>(settings[j].upper());
+ ci->UnsetS<bool>(settings[j].upper());
READ(read_string(buffer, f));
- ci->SetFounder(NickCore::Find(buffer));
+ ci->SetFounder(NickServ::FindAccount(buffer));
READ(read_string(buffer, f));
- ci->SetSuccessor(NickCore::Find(buffer));
+ ci->SetSuccessor(NickServ::FindAccount(buffer));
char pwbuf[32];
READ(read_buffer(pwbuf, f));
- READ(read_string(ci->desc, f));
+ Anope::string desc;
+ READ(read_string(desc, f));
+ ci->SetDesc(desc);
READ(read_string(buffer, f));
READ(read_string(buffer, f));
int32_t tmp32;
READ(read_int32(&tmp32, f));
- ci->time_registered = tmp32;
+ ci->SetTimeRegistered(tmp32);
READ(read_int32(&tmp32, f));
- ci->last_used = tmp32;
+ ci->SetLastUsed(tmp32);
- READ(read_string(ci->last_topic, f));
+ Anope::string last_topic;
+ READ(read_string(last_topic, f));
+ ci->SetLastTopic(last_topic);
READ(read_buffer(pwbuf, f));
- ci->last_topic_setter = pwbuf;
+ ci->SetLastTopicSetter(pwbuf);
READ(read_int32(&tmp32, f));
- ci->last_topic_time = tmp32;
+ ci->SetLastTopicTime(tmp32);
uint32_t tmpu32;
READ(read_uint32(&tmpu32, f));
// Temporary flags cleanup
tmpu32 &= ~0x80000000;
if (tmpu32 & OLD_CI_KEEPTOPIC)
- ci->Extend<bool>("KEEPTOPIC");
+ ci->SetS<bool>("KEEPTOPIC", true);
if (tmpu32 & OLD_CI_SECUREOPS)
- ci->Extend<bool>("SECUREOPS");
+ ci->SetS<bool>("SECUREOPS", true);
if (tmpu32 & OLD_CI_PRIVATE)
- ci->Extend<bool>("CS_PRIVATE");
+ ci->SetS<bool>("CS_PRIVATE", true);
if (tmpu32 & OLD_CI_TOPICLOCK)
- ci->Extend<bool>("TOPICLOCK");
+ ci->SetS<bool>("TOPICLOCK", true);
if (tmpu32 & OLD_CI_RESTRICTED)
- ci->Extend<bool>("RESTRICTED");
+ ci->SetS<bool>("RESTRICTED", true);
if (tmpu32 & OLD_CI_PEACE)
- ci->Extend<bool>("PEACE");
+ ci->SetS<bool>("PEACE", true);
if (tmpu32 & OLD_CI_SECURE)
- ci->Extend<bool>("CS_SECURE");
+ ci->SetS<bool>("CS_SECURE", true);
if (tmpu32 & OLD_CI_NO_EXPIRE)
- ci->Extend<bool>("CS_NO_EXPIRE");
+ ci->SetS<bool>("CS_NO_EXPIRE", true);
if (tmpu32 & OLD_CI_MEMO_HARDMAX)
- ci->Extend<bool>("MEMO_HARDMAX");
+ ci->SetS<bool>("MEMO_HARDMAX", true);
if (tmpu32 & OLD_CI_SECUREFOUNDER)
- ci->Extend<bool>("SECUREFOUNDER");
+ ci->SetS<bool>("SECUREFOUNDER", true);
if (tmpu32 & OLD_CI_SIGNKICK)
- ci->Extend<bool>("SIGNKICK");
+ ci->SetS<bool>("SIGNKICK", true);
if (tmpu32 & OLD_CI_SIGNKICK_LEVEL)
- ci->Extend<bool>("SIGNKICK_LEVEL");
+ ci->SetS<bool>("SIGNKICK_LEVEL", true);
Anope::string forbidby, forbidreason;
READ(read_string(forbidby, f));
READ(read_string(forbidreason, f));
if (tmpu32 & OLD_CI_SUSPENDED)
{
- SuspendInfo si;
- si.what = ci->name;
- si.by = forbidby;
- si.reason = forbidreason;
- si.when = si.expires = 0;
- ci->Extend("CS_SUSPENDED", si);
+ CSSuspendInfo *si = Serialize::New<CSSuspendInfo *>();
+ if (si)
+ {
+ si->SetChannel(ci);
+ si->SetBy(forbidby);
+ }
}
bool forbid_chan = tmpu32 & OLD_CI_VERBOTEN;
int16_t tmp16;
READ(read_int16(&tmp16, f));
- ci->bantype = tmp16;
+ ci->SetBanType(tmp16);
READ(read_int16(&tmp16, f));
if (tmp16 > 36)
@@ -838,17 +894,16 @@ static void LoadChannels()
int16_t level;
READ(read_int16(&level, f));
- if (level == ACCESS_INVALID)
- level = ACCESS_FOUNDER;
+ if (level == ChanServ::ACCESS_INVALID)
+ level = ChanServ::ACCESS_FOUNDER;
if (j == 10 && level < 0) // NOJOIN
- ci->Shrink<bool>("RESTRICTED"); // If CSDefRestricted was enabled this can happen
+ ci->UnsetS<bool>("RESTRICTED"); // If CSDefRestricted was enabled this can happen
ci->SetLevel(GetLevelName(j), level);
}
bool xop = tmpu32 & OLD_CI_XOP;
- ServiceReference<AccessProvider> provider_access("AccessProvider", "access/access"), provider_xop("AccessProvider", "access/xop");
uint16_t tmpu16;
READ(read_uint16(&tmpu16, f));
for (uint16_t j = 0; j < tmpu16; ++j)
@@ -857,19 +912,19 @@ static void LoadChannels()
READ(read_uint16(&in_use, f));
if (in_use)
{
- ChanAccess *access = NULL;
-
+ ChanServ::ChanAccess *access = NULL;
+
if (xop)
{
- if (provider_xop)
- access = provider_xop->Create();
+ access = Serialize::New<XOPChanAccess *>();
}
else
- if (provider_access)
- access = provider_access->Create();
+ {
+ access = Serialize::New<AccessChanAccess *>();
+ }
if (access)
- access->ci = ci;
+ access->SetChannel(ci);
int16_t level;
READ(read_int16(&level, f));
@@ -900,16 +955,19 @@ static void LoadChannels()
Anope::string mask;
READ(read_string(mask, f));
if (access)
- access->SetMask(mask, ci);
+ {
+ access->SetMask(mask);
+ NickServ::Nick *na = NickServ::FindNick(mask);
+ if (na)
+ na->SetAccount(na->GetAccount());
+ }
READ(read_int32(&tmp32, f));
if (access)
{
- access->last_seen = tmp32;
- access->creator = "Unknown";
- access->created = Anope::CurTime;
-
- ci->AddAccess(access);
+ access->SetLastSeen(tmp32);
+ access->SetCreator("Unknown");
+ access->SetCreated(Anope::CurTime);
}
}
}
@@ -943,74 +1001,67 @@ static void LoadChannels()
READ(read_string(buffer, f)); // +L
READ(read_int16(&tmp16, f));
- READ(read_int16(&ci->memos.memomax, f));
+ READ(read_int16(&tmp16, f));
+ MemoServ::MemoInfo *mi = ci->GetMemos();
+ if (mi)
+ mi->SetMemoMax(tmp16);
for (int16_t j = 0; j < tmp16; ++j)
{
READ(read_uint32(&tmpu32, f));
READ(read_uint16(&tmpu16, f));
- Memo *m = new Memo;
+ MemoServ::Memo *m = Serialize::New<MemoServ::Memo *>();
READ(read_int32(&tmp32, f));
- m->time = tmp32;
+ if (m)
+ m->SetTime(tmp32);
char sbuf[32];
READ(read_buffer(sbuf, f));
- m->sender = sbuf;
- READ(read_string(m->text, f));
- m->owner = ci->name;
- ci->memos.memos->push_back(m);
- m->mi = &ci->memos;
+ if (m)
+ m->SetSender(sbuf);
+ Anope::string text;
+ READ(read_string(text, f));
+ if (m)
+ m->SetText(text);
}
READ(read_string(buffer, f));
if (!buffer.empty())
{
- EntryMessageList *eml = ci->Require<EntryMessageList>("entrymsg");
- if (eml)
+ EntryMsg *e = Serialize::New<EntryMsg *>();
+ if (e)
{
- EntryMsg *e = eml->Create();
-
- e->chan = ci->name;
- e->creator = "Unknown";
- e->message = buffer;
- e->when = Anope::CurTime;
-
- (*eml)->push_back(e);
+ e->SetChannel(ci);
+ e->SetCreator("Unknown");
+ e->SetMessage(buffer);
+ e->SetWhen(Anope::CurTime);
}
}
READ(read_string(buffer, f));
- ci->bi = BotInfo::Find(buffer, true);
+ ci->SetBot(ServiceBot::Find(buffer, true));
READ(read_int32(&tmp32, f));
if (tmp32 & OLD_BS_DONTKICKOPS)
- ci->Extend<bool>("BS_DONTKICKOPS");
+ ci->SetS<bool>("BS_DONTKICKOPS", true);
if (tmp32 & OLD_BS_DONTKICKVOICES)
- ci->Extend<bool>("BS_DONTKICKVOICES");
+ ci->SetS<bool>("BS_DONTKICKVOICES", true);
if (tmp32 & OLD_BS_FANTASY)
- ci->Extend<bool>("BS_FANTASY");
+ ci->SetS<bool>("BS_FANTASY", true);
if (tmp32 & OLD_BS_GREET)
- ci->Extend<bool>("BS_GREET");
+ ci->SetS<bool>("BS_GREET", true);
if (tmp32 & OLD_BS_NOBOT)
- ci->Extend<bool>("BS_NOBOT");
+ ci->SetS<bool>("BS_NOBOT", true);
- KickerData *kd = ci->Require<KickerData>("kickerdata");
+ KickerData *kd = GetKickerData(ci);
if (kd)
{
- if (tmp32 & OLD_BS_KICK_BOLDS)
- kd->bolds = true;
- if (tmp32 & OLD_BS_KICK_COLORS)
- kd->colors = true;
- if (tmp32 & OLD_BS_KICK_REVERSES)
- kd->reverses = true;
- if (tmp32 & OLD_BS_KICK_UNDERLINES)
- kd->underlines = true;
- if (tmp32 & OLD_BS_KICK_BADWORDS)
- kd->badwords = true;
- if (tmp32 & OLD_BS_KICK_CAPS)
- kd->caps = true;
- if (tmp32 & OLD_BS_KICK_FLOOD)
- kd->flood = true;
- if (tmp32 & OLD_BS_KICK_REPEAT)
- kd->repeat = true;
+ kd->SetBolds(tmp32 & OLD_BS_KICK_BOLDS);
+ kd->SetColors(tmp32 & OLD_BS_KICK_COLORS);
+ kd->SetReverses(tmp32 & OLD_BS_KICK_REVERSES);
+ kd->SetUnderlines(tmp32 & OLD_BS_KICK_UNDERLINES);
+ kd->SetBadwords(tmp32 & OLD_BS_KICK_BADWORDS);
+ kd->SetCaps(tmp32 & OLD_BS_KICK_CAPS);
+ kd->SetFlood(tmp32 & OLD_BS_KICK_FLOOD);
+ kd->SetRepeat(tmp32 & OLD_BS_KICK_REPEAT);
}
READ(read_int16(&tmp16, f));
@@ -1018,27 +1069,51 @@ static void LoadChannels()
{
int16_t ttb;
READ(read_int16(&ttb, f));
- if (j < TTB_SIZE && kd)
- kd->ttb[j] = ttb;
+ switch (j)
+ {
+ case TTB_BOLDS:
+ kd->SetTTBBolds(ttb);
+ break;
+ case TTB_COLORS:
+ kd->SetTTBColors(ttb);
+ break;
+ case TTB_REVERSES:
+ kd->SetTTBReverses(ttb);
+ break;
+ case TTB_UNDERLINES:
+ kd->SetTTBUnderlines(ttb);
+ break;
+ case TTB_BADWORDS:
+ kd->SetTTBBadwords(ttb);
+ break;
+ case TTB_CAPS:
+ kd->SetTTBCaps(ttb);
+ break;
+ case TTB_FLOOD:
+ kd->SetTTBFlood(ttb);
+ break;
+ case TTB_REPEAT:
+ kd->SetTTBRepeat(ttb);
+ break;
+ }
}
READ(read_int16(&tmp16, f));
if (kd)
- kd->capsmin = tmp16;
+ kd->SetCapsMin(tmp16);
READ(read_int16(&tmp16, f));
if (kd)
- kd->capspercent = tmp16;
+ kd->SetCapsPercent(tmp16);
READ(read_int16(&tmp16, f));
if (kd)
- kd->floodlines = tmp16;
+ kd->SetFloodLines(tmp16);
READ(read_int16(&tmp16, f));
if (kd)
- kd->floodsecs = tmp16;
+ kd->SetFloodSecs(tmp16);
READ(read_int16(&tmp16, f));
if (kd)
- kd->repeattimes = tmp16;
+ kd->SetRepeatTimes(tmp16);
- BadWords *bw = ci->Require<BadWords>("badwords");
READ(read_uint16(&tmpu16, f));
for (uint16_t j = 0; j < tmpu16; ++j)
{
@@ -1058,38 +1133,33 @@ static void LoadChannels()
else if (type == 3)
bwtype = BW_END;
- if (bw)
- bw->AddBadWord(buffer, bwtype);
+ if (badwords)
+ badwords->AddBadWord(ci, buffer, bwtype);
}
}
if (forbid_chan)
{
- if (!forbid)
+ if (ci->GetName().find_first_of("?*") != Anope::string::npos)
{
delete ci;
continue;
}
- if (ci->name.find_first_of("?*") != Anope::string::npos)
+ ForbidData *d = Serialize::New<ForbidData *>();
+ if (d)
{
- delete ci;
- continue;
+ d->SetMask(ci->GetName());
+ d->SetCreator(forbidby);
+ d->SetReason(forbidreason);
+ d->SetType(FT_CHAN);
}
-
- ForbidData *d = forbid->CreateForbid();
- d->mask = ci->name;
- d->creator = forbidby;
- d->reason = forbidreason;
- d->expires = 0;
- d->created = 0;
- d->type = FT_CHAN;
+
delete ci;
- forbid->AddForbid(d);
continue;
}
- Log(LOG_DEBUG) << "Loaded channel " << ci->name;
+ Log(LOG_DEBUG) << "Loaded channel " << ci->GetName();
}
close_db(f);
@@ -1104,9 +1174,8 @@ static void LoadOper()
XLineManager *akill, *sqline, *snline, *szline;
akill = sqline = snline = szline = NULL;
- for (std::list<XLineManager *>::iterator it = XLineManager::XLineManagers.begin(), it_end = XLineManager::XLineManagers.end(); it != it_end; ++it)
+ for (XLineManager *xl : XLineManager::XLineManagers)
{
- XLineManager *xl = *it;
if (xl->Type() == 'G')
akill = xl;
else if (xl->Type() == 'Q')
@@ -1138,8 +1207,14 @@ static void LoadOper()
if (!akill)
continue;
- XLine *x = new XLine(user + "@" + host, by, expires, reason, XLineManager::GenerateUID());
- x->created = seton;
+ XLine *x = Serialize::New<XLine *>();
+ x->SetMask(user + "@" + host);
+ x->SetBy(by);
+ x->SetExpires(expires);
+ x->SetReason(reason);
+ x->SetID(XLineManager::GenerateUID());
+ x->SetCreated(seton);
+
akill->AddXLine(x);
}
@@ -1158,8 +1233,14 @@ static void LoadOper()
if (!snline)
continue;
- XLine *x = new XLine(mask, by, expires, reason, XLineManager::GenerateUID());
- x->created = seton;
+ XLine *x = Serialize::New<XLine *>();
+ x->SetMask(mask);
+ x->SetBy(by);
+ x->SetExpires(expires);
+ x->SetReason(reason);
+ x->SetID(XLineManager::GenerateUID());
+ x->SetCreated(seton);
+
snline->AddXLine(x);
}
@@ -1178,8 +1259,14 @@ static void LoadOper()
if (!sqline)
continue;
- XLine *x = new XLine(mask, by, expires, reason, XLineManager::GenerateUID());
- x->created = seton;
+ XLine *x = Serialize::New<XLine *>();
+ x->SetMask(mask);
+ x->SetBy(by);
+ x->SetExpires(expires);
+ x->SetReason(reason);
+ x->SetID(XLineManager::GenerateUID());
+ x->SetCreated(seton);
+
sqline->AddXLine(x);
}
@@ -1198,8 +1285,14 @@ static void LoadOper()
if (!szline)
continue;
- XLine *x = new XLine(mask, by, expires, reason, XLineManager::GenerateUID());
- x->created = seton;
+ XLine *x = Serialize::New<XLine *>();
+ x->SetMask(mask);
+ x->SetBy(by);
+ x->SetExpires(expires);
+ x->SetReason(reason);
+ x->SetID(XLineManager::GenerateUID());
+ x->SetCreated(seton);
+
szline->AddXLine(x);
}
@@ -1208,13 +1301,10 @@ static void LoadOper()
static void LoadExceptions()
{
- if (!session_service)
- return;
-
dbFILE *f = open_db_read("OperServ", "exception.db", 9);
if (f == NULL)
return;
-
+
int16_t num;
READ(read_int16(&num, f));
for (int i = 0; i < num; ++i)
@@ -1231,14 +1321,16 @@ static void LoadExceptions()
READ(read_int32(&time, f));
READ(read_int32(&expires, f));
- Exception *exception = session_service->CreateException();
- exception->mask = mask;
- exception->limit = limit;
- exception->who = who;
- exception->time = time;
- exception->expires = expires;
- exception->reason = reason;
- session_service->AddException(exception);
+ Exception *e = Serialize::New<Exception *>();
+ if (e)
+ {
+ e->SetMask(mask);
+ e->SetLimit(limit);
+ e->SetWho(who);
+ e->SetTime(time);
+ e->SetExpires(expires);
+ e->SetReason(reason);
+ }
}
close_db(f);
@@ -1246,9 +1338,6 @@ static void LoadExceptions()
static void LoadNews()
{
- if (!news_service)
- return;
-
dbFILE *f = open_db_read("OperServ", "news.db", 9);
if (f == NULL)
@@ -1260,60 +1349,68 @@ static void LoadNews()
for (int16_t i = 0; i < n; i++)
{
int16_t type;
- NewsItem *ni = news_service->CreateNewsItem();
+ NewsItem *ni = Serialize::New<NewsItem *>();
+
+ if (!ni)
+ break;
READ(read_int16(&type, f));
switch (type)
{
case OLD_NEWS_LOGON:
- ni->type = NEWS_LOGON;
+ ni->SetNewsType(NEWS_LOGON);
break;
case OLD_NEWS_OPER:
- ni->type = NEWS_OPER;
+ ni->SetNewsType(NEWS_OPER);
break;
case OLD_NEWS_RANDOM:
- ni->type = NEWS_RANDOM;
+ ni->SetNewsType(NEWS_RANDOM);
break;
}
int32_t unused;
READ(read_int32(&unused, f));
- READ(read_string(ni->text, f));
+ Anope::string text;
+ READ(read_string(text, f));
+ ni->SetText(text);
char who[32];
READ(read_buffer(who, f));
- ni->who = who;
+ ni->SetWho(who);
int32_t tmp;
READ(read_int32(&tmp, f));
- ni->time = tmp;
-
- news_service->AddNewsItem(ni);
+ ni->SetTime(tmp);
}
close_db(f);
}
class DBOld : public Module
+ , public EventHook<Event::LoadDatabase>
+ , public EventHook<Event::UplinkSync>
{
- PrimitiveExtensibleItem<uint32_t> mlock_on, mlock_off, mlock_limit;
- PrimitiveExtensibleItem<Anope::string> mlock_key;
+ ExtensibleItem<uint32_t> mlock_on, mlock_off, mlock_limit; // XXX these are no longer required because of confmodes
+ ExtensibleItem<Anope::string> mlock_key;
public:
- DBOld(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, DATABASE | VENDOR),
- mlock_on(this, "mlock_on"), mlock_off(this, "mlock_off"), mlock_limit(this, "mlock_limit"), mlock_key(this, "mlock_key")
+ DBOld(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, DATABASE | VENDOR)
+ , EventHook<Event::LoadDatabase>(this)
+ , EventHook<Event::UplinkSync>(this)
+ , mlock_on(this, "mlock_on")
+ , mlock_off(this, "mlock_off")
+ , mlock_limit(this, "mlock_limit")
+ , mlock_key(this, "mlock_key")
{
-
-
- hashm = Config->GetModule(this)->Get<const Anope::string>("hash");
+ hashm = Config->GetModule(this)->Get<Anope::string>("hash");
if (hashm != "md5" && hashm != "oldmd5" && hashm != "sha1" && hashm != "plain" && hashm != "sha256")
throw ModuleException("Invalid hash method");
}
- EventReturn OnLoadDatabase() anope_override
+ EventReturn OnLoadDatabase() override
{
LoadNicks();
LoadVHosts();
@@ -1326,11 +1423,13 @@ class DBOld : public Module
return EVENT_STOP;
}
- void OnUplinkSync(Server *s) anope_override
+ void OnUplinkSync(Server *s) override
{
- for (registered_channel_map::iterator it = RegisteredChannelList->begin(), it_end = RegisteredChannelList->end(); it != it_end; ++it)
+ if (!ChanServ::service)
+ return;
+ for (auto& it : ChanServ::service->GetChannels())
{
- ChannelInfo *ci = it->second;
+ ChanServ::Channel *ci = it.second;
uint32_t *limit = mlock_limit.Get(ci);
Anope::string *key = mlock_key.Get(ci);
@@ -1357,5 +1456,10 @@ class DBOld : public Module
}
};
+template<> void ModuleInfo<DBOld>(ModuleDef *def)
+{
+ def->Depends("chanserv.access");
+}
+
MODULE_INIT(DBOld)
diff --git a/modules/database/redis.cpp b/modules/database/redis.cpp
new file mode 100644
index 000000000..21f7ca960
--- /dev/null
+++ b/modules/database/redis.cpp
@@ -0,0 +1,445 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2013-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/redis.h"
+
+using namespace Redis;
+
+class DatabaseRedis;
+static DatabaseRedis *me;
+
+class TypeLoader : public Interface
+{
+ Serialize::TypeBase *type;
+
+ public:
+ TypeLoader(Module *creator, Serialize::TypeBase *t) : Interface(creator), type(t) { }
+
+ void OnResult(const Reply &r) override;
+};
+
+class ObjectLoader : public Interface
+{
+ Serialize::Object *obj;
+
+ public:
+ ObjectLoader(Module *creator, Serialize::Object *s) : Interface(creator), obj(s) { }
+
+ void OnResult(const Reply &r) override;
+};
+
+class FieldLoader : public Interface
+{
+ Serialize::Object *obj;
+ Serialize::FieldBase *field;
+
+ public:
+ FieldLoader(Module *creator, Serialize::Object *o, Serialize::FieldBase *f) : Interface(creator), obj(o), field(f) { }
+
+ void OnResult(const Reply &) override;
+};
+
+class SubscriptionListener : public Interface
+{
+ public:
+ SubscriptionListener(Module *creator) : Interface(creator) { }
+
+ void OnResult(const Reply &r) override;
+};
+
+class DatabaseRedis : public Module
+ , public EventHook<Event::LoadDatabase>
+ , public EventHook<Event::SerializeEvents>
+{
+ SubscriptionListener sl;
+
+ public:
+ ServiceReference<Provider> redis;
+
+ DatabaseRedis(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, DATABASE | VENDOR)
+ , EventHook<Event::LoadDatabase>(this)
+ , EventHook<Event::SerializeEvents>(this)
+ , sl(this)
+ {
+ me = this;
+ }
+
+ void OnReload(Configuration::Conf *conf) override
+ {
+ Configuration::Block *block = conf->GetModule(this);
+ this->redis = ServiceReference<Provider>(block->Get<Anope::string>("engine", "redis/main"));
+ }
+
+ EventReturn OnLoadDatabase() override
+ {
+ if (!redis)
+ return EVENT_STOP;
+
+ const std::map<Anope::string, Serialize::TypeBase *> &types = Serialize::TypeBase::GetTypes();
+ for (const std::pair<Anope::string, Serialize::TypeBase *> &p : types)
+ this->OnSerializeTypeCreate(p.second);
+
+ while (redis->BlockAndProcess());
+
+ redis->Subscribe(&this->sl, "anope");
+
+ return EVENT_STOP;
+ }
+
+ void OnSerializeTypeCreate(Serialize::TypeBase *sb)
+ {
+ std::vector<Anope::string> args = { "SMEMBERS", "ids:" + sb->GetName() };
+
+ redis->SendCommand(new TypeLoader(this, sb), args);
+ }
+
+ EventReturn OnSerializeList(Serialize::TypeBase *type, std::vector<Serialize::ID> &ids) override
+ {
+ return EVENT_CONTINUE;
+ }
+
+ EventReturn OnSerializeFind(Serialize::TypeBase *type, Serialize::FieldBase *field, const Anope::string &value, Serialize::ID &id) override
+ {
+ return EVENT_CONTINUE;
+ }
+
+ EventReturn OnSerializeGet(Serialize::Object *object, Serialize::FieldBase *field, Anope::string &value) override
+ {
+ return EVENT_CONTINUE;
+ }
+
+ EventReturn OnSerializeGetRefs(Serialize::Object *object, Serialize::TypeBase *type, std::vector<Serialize::Edge> &) override
+ {
+ return EVENT_CONTINUE;
+ }
+
+ EventReturn OnSerializeDeref(Serialize::ID id, Serialize::TypeBase *type) override
+ {
+ return EVENT_CONTINUE;
+ }
+
+ EventReturn OnSerializeGetSerializable(Serialize::Object *object, Serialize::FieldBase *field, Anope::string &type, Serialize::ID &value) override
+ {
+ return EVENT_CONTINUE;
+ }
+
+ EventReturn OnSerializeSet(Serialize::Object *object, Serialize::FieldBase *field, const Anope::string &value) override
+ {
+ std::vector<Anope::string> args;
+
+ redis->StartTransaction();
+
+ const Anope::string &old = field->SerializeToString(object);
+ args = { "SREM", "lookup:" + object->GetSerializableType()->GetName() + ":" + field->serialize_name + ":" + old, stringify(object->id) };
+ redis->SendCommand(nullptr, args);
+
+ // add object to type set
+ args = { "SADD", "ids:" + object->GetSerializableType()->GetName(), stringify(object->id) };
+ redis->SendCommand(nullptr, args);
+
+ // add key to key set
+ args = { "SADD", "keys:" + stringify(object->id), field->serialize_name };
+ redis->SendCommand(nullptr, args);
+
+ // set value
+ args = { "SET", "values:" + stringify(object->id) + ":" + field->serialize_name, value };
+ redis->SendCommand(nullptr, args);
+
+ // lookup
+ args = { "SADD", "lookup:" + object->GetSerializableType()->GetName() + ":" + field->serialize_name + ":" + value, stringify(object->id) };
+ redis->SendCommand(nullptr, args);
+
+ redis->CommitTransaction();
+
+ return EVENT_CONTINUE;
+ }
+
+ EventReturn OnSerializeSetSerializable(Serialize::Object *object, Serialize::FieldBase *field, Serialize::Object *value) override
+ {
+ return OnSerializeSet(object, field, stringify(value->id));
+ }
+
+ EventReturn OnSerializeUnset(Serialize::Object *object, Serialize::FieldBase *field) override
+ {
+ std::vector<Anope::string> args;
+
+ redis->StartTransaction();
+
+ const Anope::string &old = field->SerializeToString(object);
+ args = { "SREM", "lookup:" + object->GetSerializableType()->GetName() + ":" + field->serialize_name + ":" + old, stringify(object->id) };
+ redis->SendCommand(nullptr, args);
+
+ // remove field from set
+ args = { "SREM", "keys:" + stringify(object->id), field->serialize_name };
+ redis->SendCommand(nullptr, args);
+
+ redis->CommitTransaction();
+
+ return EVENT_CONTINUE;
+ }
+
+ EventReturn OnSerializeUnsetSerializable(Serialize::Object *object, Serialize::FieldBase *field) override
+ {
+ return OnSerializeUnset(object, field);
+ }
+
+ EventReturn OnSerializeHasField(Serialize::Object *object, Serialize::FieldBase *field) override
+ {
+ return EVENT_CONTINUE;
+ }
+
+ EventReturn OnSerializableGetId(Serialize::ID &id) override
+ {
+ std::vector<Anope::string> args = { "INCR", "id" };
+
+ auto f = [&](const Reply &r)
+ {
+ id = r.i;
+ };
+
+ FInterface inter(this, f);
+ redis->SendCommand(&inter, args);
+ while (redis->BlockAndProcess());
+ return EVENT_ALLOW;
+ }
+
+ void OnSerializableCreate(Serialize::Object *) override
+ {
+ }
+
+ void OnSerializableDelete(Serialize::Object *obj) override
+ {
+ std::vector<Anope::string> args;
+
+ redis->StartTransaction();
+
+ for (Serialize::FieldBase *field : obj->GetSerializableType()->GetFields())
+ {
+ Anope::string value = field->SerializeToString(obj);
+
+ args = { "SREM", "lookup:" + obj->GetSerializableType()->GetName() + ":" + field->serialize_name + ":" + value, stringify(obj->id) };
+ redis->SendCommand(nullptr, args);
+
+ args = { "DEL", "values:" + stringify(obj->id) + ":" + field->serialize_name };
+ redis->SendCommand(nullptr, args);
+
+ args = { "SREM", "keys:" + stringify(obj->id), field->serialize_name };
+ redis->SendCommand(nullptr, args);
+ }
+
+ args = { "SREM", "ids:" + obj->GetSerializableType()->GetName(), stringify(obj->id) };
+ redis->SendCommand(nullptr, args);
+
+ redis->CommitTransaction();
+ }
+};
+
+void TypeLoader::OnResult(const Reply &r)
+{
+ if (r.type != Reply::MULTI_BULK || !me->redis)
+ {
+ delete this;
+ return;
+ }
+
+ for (unsigned i = 0; i < r.multi_bulk.size(); ++i)
+ {
+ const Reply *reply = r.multi_bulk[i];
+
+ if (reply->type != Reply::BULK)
+ continue;
+
+ int64_t id;
+ try
+ {
+ id = convertTo<int64_t>(reply->bulk);
+ }
+ catch (const ConvertException &)
+ {
+ continue;
+ }
+
+ Serialize::Object *obj = type->Require(id);
+ if (obj == nullptr)
+ {
+ Log(LOG_DEBUG) << "redis: Unable to require object #" << id << " of type " << type->GetName();
+ continue;
+ }
+
+ std::vector<Anope::string> args = { "SMEMBERS", "keys:" + stringify(id) };
+
+ me->redis->SendCommand(new ObjectLoader(me, obj), args);
+ }
+
+ delete this;
+}
+
+void ObjectLoader::OnResult(const Reply &r)
+{
+ if (r.type != Reply::MULTI_BULK || r.multi_bulk.empty() || !me->redis)
+ {
+ delete this;
+ return;
+ }
+
+ Serialize::TypeBase *type = obj->GetSerializableType();
+
+ for (Reply *reply : r.multi_bulk)
+ {
+ const Anope::string &key = reply->bulk;
+ Serialize::FieldBase *field = type->GetField(key);
+
+ if (field == nullptr)
+ continue;
+
+ std::vector<Anope::string> args = { "GET", "values:" + stringify(obj->id) + ":" + key };
+
+ me->redis->SendCommand(new FieldLoader(me, obj, field), args);
+ }
+
+ delete this;
+}
+
+void FieldLoader::OnResult(const Reply &r)
+{
+ Log(LOG_DEBUG_2) << "redis: Setting field " << field->serialize_name << " of object #" << obj->id << " of type " << obj->GetSerializableType()->GetName() << " to " << r.bulk;
+ field->UnserializeFromString(obj, r.bulk);
+
+ delete this;
+}
+
+void SubscriptionListener::OnResult(const Reply &r)
+{
+ /*
+ * message
+ * anope
+ * message
+ *
+ * set 4 email adam@anope.org
+ * unset 4 email
+ * create 4 NickCore
+ * delete 4
+ */
+
+ const Anope::string &message = r.multi_bulk[2]->bulk;
+ Anope::string command;
+ spacesepstream sep(message);
+
+ sep.GetToken(command);
+
+ if (command == "set" || command == "unset")
+ {
+ Anope::string sid, key, value;
+
+ sep.GetToken(sid);
+ sep.GetToken(key);
+ value = sep.GetRemaining();
+
+ Serialize::ID id;
+ try
+ {
+ id = convertTo<Serialize::ID>(sid);
+ }
+ catch (const ConvertException &ex)
+ {
+ Log(LOG_DEBUG) << "redis: unable to get id for SL update key " << sid;
+ return;
+ }
+
+ Serialize::Object *obj = Serialize::GetID(id);
+ if (obj == nullptr)
+ {
+ Log(LOG_DEBUG) << "redis: pmessage for unknown object #" << id;
+ return;
+ }
+
+ Serialize::FieldBase *field = obj->GetSerializableType()->GetField(key);
+ if (field == nullptr)
+ {
+ Log(LOG_DEBUG) << "redis: pmessage for unknown field of object #" << id << ": " << key;
+ return;
+ }
+
+ Log(LOG_DEBUG_2) << "redis: Setting field " << field->serialize_name << " of object #" << obj->id << " of type " << obj->GetSerializableType()->GetName() << " to " << value;
+ field->UnserializeFromString(obj, value);
+ }
+ else if (command == "create")
+ {
+ Anope::string sid, stype;
+
+ sep.GetToken(sid);
+ sep.GetToken(stype);
+
+ Serialize::ID id;
+ try
+ {
+ id = convertTo<Serialize::ID>(sid);
+ }
+ catch (const ConvertException &ex)
+ {
+ Log(LOG_DEBUG) << "redis: unable to get id for SL update key " << sid;
+ return;
+ }
+
+ Serialize::TypeBase *type = Serialize::TypeBase::Find(stype);
+ if (type == nullptr)
+ {
+ Log(LOG_DEBUG) << "redis: pmessage create for nonexistant type " << stype;
+ return;
+ }
+
+ Serialize::Object *obj = type->Require(id);
+ if (obj == nullptr)
+ {
+ Log(LOG_DEBUG) << "redis: require for pmessage create type " << type->GetName() << " id #" << id << " returned nullptr";
+ return;
+ }
+ }
+ else if (command == "delete")
+ {
+ Anope::string sid;
+
+ sep.GetToken(sid);
+
+ Serialize::ID id;
+ try
+ {
+ id = convertTo<Serialize::ID>(sid);
+ }
+ catch (const ConvertException &ex)
+ {
+ Log(LOG_DEBUG) << "redis: unable to get id for SL update key " << sid;
+ return;
+ }
+
+ Serialize::Object *obj = Serialize::GetID(id);
+ if (obj == nullptr)
+ {
+ Log(LOG_DEBUG) << "redis: message for unknown object #" << id;
+ return;
+ }
+
+ obj->Delete();
+ }
+ else
+ Log(LOG_DEBUG) << "redis: unknown message: " << message;
+}
+
+MODULE_INIT(DatabaseRedis)
diff --git a/modules/database/sql.cpp b/modules/database/sql.cpp
new file mode 100644
index 000000000..d7ed1963a
--- /dev/null
+++ b/modules/database/sql.cpp
@@ -0,0 +1,445 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2011-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/sql.h"
+
+using namespace SQL;
+
+class DBMySQL : public Module, public Pipe
+ , public EventHook<Event::SerializeEvents>
+{
+ private:
+ bool transaction = false;
+ bool inited = false;
+ Anope::string prefix;
+ ServiceReference<Provider> SQL;
+ std::unordered_multimap<Serialize::Object *, Serialize::FieldBase *> cache;
+
+ void CacheMiss(Serialize::Object *object, Serialize::FieldBase *field)
+ {
+ cache.insert(std::make_pair(object, field));
+ }
+
+ bool IsCacheMiss(Serialize::Object *object, Serialize::FieldBase *field)
+ {
+ auto range = cache.equal_range(object);
+ for (auto it = range.first; it != range.second; ++it)
+ if (it->second == field)
+ return true;
+ return false;
+ }
+
+ Result Run(const Query &query)
+ {
+ if (!SQL)
+ return Result();
+
+ if (!inited)
+ {
+ inited = true;
+ for (const Query &q : SQL->InitSchema(prefix))
+ SQL->RunQuery(q);
+ }
+
+ Log(LOG_DEBUG_2) << query.Unsafe();
+
+ return SQL->RunQuery(query);
+ }
+
+ void StartTransaction()
+ {
+ if (!SQL || transaction)
+ return;
+
+ Run(SQL->BeginTransaction());
+
+ transaction = true;
+ Notify();
+ }
+
+ void Commit()
+ {
+ if (!SQL || !transaction)
+ return;
+
+ Run(SQL->Commit());
+
+ transaction = false;
+ }
+
+ public:
+ DBMySQL(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, DATABASE | VENDOR)
+ , EventHook<Event::SerializeEvents>(this)
+ {
+ }
+
+ void OnNotify() override
+ {
+ Commit();
+ Serialize::Clear();
+ cache.clear();
+ }
+
+ void OnReload(Configuration::Conf *conf) override
+ {
+ Configuration::Block *block = conf->GetModule(this);
+ this->SQL = ServiceReference<Provider>(block->Get<Anope::string>("engine"));
+ this->prefix = block->Get<Anope::string>("prefix", "anope_db_");
+ inited = false;
+ }
+
+ EventReturn OnSerializeList(Serialize::TypeBase *type, std::vector<Serialize::ID> &ids) override
+ {
+ StartTransaction();
+
+ ids.clear();
+
+ Query query = "SELECT `id` FROM `" + prefix + type->GetName() + "`";
+ Result res = Run(query);
+ for (int i = 0; i < res.Rows(); ++i)
+ {
+ Serialize::ID id = convertTo<Serialize::ID>(res.Get(i, "id"));
+ ids.push_back(id);
+ }
+
+ return EVENT_ALLOW;
+ }
+
+ EventReturn OnSerializeFind(Serialize::TypeBase *type, Serialize::FieldBase *field, const Anope::string &value, Serialize::ID &id) override
+ {
+ if (!SQL)
+ return EVENT_CONTINUE;
+
+ StartTransaction();
+
+ for (Query &q : SQL->CreateTable(prefix, type->GetName()))
+ Run(q);
+
+ for (Query &q : SQL->AlterTable(prefix, type->GetName(), field->serialize_name, false))
+ Run(q);
+
+ for (const Query &q : SQL->CreateIndex(prefix + type->GetName(), field->serialize_name))
+ Run(q);
+
+ Query query("SELECT `id` FROM `" + prefix + type->GetName() + "` WHERE `" + field->serialize_name + "` = @value@");
+ query.SetValue("value", value);
+ Result res = Run(query);
+ if (res.Rows())
+ try
+ {
+ id = convertTo<Serialize::ID>(res.Get(0, "id"));
+ return EVENT_ALLOW;
+ }
+ catch (const ConvertException &)
+ {
+ }
+ return EVENT_CONTINUE;
+ }
+
+ private:
+ bool GetValue(Serialize::Object *object, Serialize::FieldBase *field, SQL::Result::Value &v)
+ {
+ StartTransaction();
+
+ Query query = "SELECT `" + field->serialize_name + "` FROM `" + prefix + object->GetSerializableType()->GetName() + "` WHERE `id` = @id@";
+ query.SetValue("id", object->id);
+ Result res = Run(query);
+
+ if (res.Rows() == 0)
+ return false;
+
+ v = res.GetValue(0, field->serialize_name);
+ return true;
+ }
+
+ public:
+ EventReturn OnSerializeGet(Serialize::Object *object, Serialize::FieldBase *field, Anope::string &value) override
+ {
+ SQL::Result::Value v;
+
+ if (IsCacheMiss(object, field))
+ return EVENT_CONTINUE;
+
+ if (!GetValue(object, field, v))
+ {
+ CacheMiss(object, field);
+ return EVENT_CONTINUE;
+ }
+
+ value = v.value;
+ return EVENT_ALLOW;
+ }
+
+ EventReturn OnSerializeGetRefs(Serialize::Object *object, Serialize::TypeBase *type, std::vector<Serialize::Edge> &edges) override
+ {
+ StartTransaction();
+
+ edges.clear();
+
+ Query query;
+ if (type)
+ query = "SELECT field," + prefix + "edges.id,other_id,j1.type,j2.type AS other_type FROM `" + prefix + "edges` "
+ "JOIN `" + prefix + "objects` AS j1 ON " + prefix + "edges.id = j1.id "
+ "JOIN `" + prefix + "objects` AS j2 ON " + prefix + "edges.other_id = j2.id "
+ "WHERE "
+ " (" + prefix + "edges.id = @id@ AND j2.type = @other_type@) "
+ "OR"
+ " (other_id = @id@ AND j1.type = @other_type@)";
+ else
+ query = "SELECT field," + prefix + "edges.id,other_id,j1.type,j2.type AS other_type FROM `" + prefix + "edges` "
+ "JOIN `" + prefix + "objects` AS j1 ON " + prefix + "edges.id = j1.id "
+ "JOIN `" + prefix + "objects` AS j2 ON " + prefix + "edges.other_id = j2.id "
+ "WHERE " + prefix + "edges.id = @id@ OR other_id = @id@";
+
+ query.SetValue("type", object->GetSerializableType()->GetName());
+ query.SetValue("id", object->id);
+ if (type)
+ query.SetValue("other_type", type->GetName());
+
+ Result res = Run(query);
+ for (int i = 0; i < res.Rows(); ++i)
+ {
+ Serialize::ID id = convertTo<Serialize::ID>(res.Get(i, "id")); // object edge is on
+
+ if (id == object->id)
+ {
+ // we want other type, this is my edge
+ Anope::string t = res.Get(i, "other_type");
+ Anope::string f = res.Get(i, "field");
+ id = convertTo<Serialize::ID>(res.Get(i, "other_id"));
+
+ Serialize::FieldBase *obj_field = object->GetSerializableType()->GetField(f);
+ if (obj_field == nullptr)
+ {
+ Log(LOG_DEBUG) << "Unable to find field " << f << " on " << object->GetSerializableType()->GetName();
+ continue;
+ }
+
+ Serialize::TypeBase *obj_type = Serialize::TypeBase::Find(t);
+ if (obj_type == nullptr)
+ {
+ Log(LOG_DEBUG) << "Unable to find type " << t;
+ continue;
+ }
+
+ Serialize::Object *other = obj_type->Require(id);
+ if (other == nullptr)
+ {
+ Log(LOG_DEBUG) << "Unable to require id " << id << " type " << obj_type->GetName();
+ continue;
+ }
+
+ edges.emplace_back(other, obj_field, true);
+ }
+ else
+ {
+ // edge to me
+ Anope::string t = res.Get(i, "type");
+ Anope::string f = res.Get(i, "field");
+
+ Serialize::TypeBase *obj_type = Serialize::TypeBase::Find(t);
+ if (obj_type == nullptr)
+ {
+ Log(LOG_DEBUG) << "Unable to find type " << t;
+ continue;
+ }
+
+ Serialize::FieldBase *obj_field = obj_type->GetField(f);
+ if (obj_field == nullptr)
+ {
+ Log(LOG_DEBUG) << "Unable to find field " << f << " on " << obj_type->GetName();
+ continue;
+ }
+
+ Serialize::Object *other = obj_type->Require(id);
+ if (other == nullptr)
+ {
+ Log(LOG_DEBUG) << "Unable to require id " << id << " type " << obj_type->GetName();
+ continue;
+ }
+
+ // other type, other field,
+ edges.emplace_back(other, obj_field, false);
+ }
+ }
+
+ return EVENT_ALLOW;
+ }
+
+ EventReturn OnSerializeDeref(Serialize::ID id, Serialize::TypeBase *type) override
+ {
+ StartTransaction();
+
+ Query query = "SELECT `id` FROM `" + prefix + type->GetName() + "` WHERE `id` = @id@";
+ query.SetValue("id", id);
+ Result res = Run(query);
+ if (res.Rows() == 0)
+ return EVENT_CONTINUE;
+ return EVENT_ALLOW;
+ }
+
+ EventReturn OnSerializeGetSerializable(Serialize::Object *object, Serialize::FieldBase *field, Anope::string &type, Serialize::ID &value) override
+ {
+ StartTransaction();
+
+ Query query = "SELECT `" + field->serialize_name + "`,j1.type AS " + field->serialize_name + "_type FROM `" + prefix + object->GetSerializableType()->GetName() + "` "
+ "JOIN `" + prefix + "objects` AS j1 ON " + prefix + object->GetSerializableType()->GetName() + "." + field->serialize_name + " = j1.id "
+ "WHERE " + prefix + object->GetSerializableType()->GetName() + ".id = @id@";
+ query.SetValue("id", object->id);
+ Result res = Run(query);
+
+ if (res.Rows() == 0)
+ return EVENT_CONTINUE;
+
+ type = res.Get(0, field->serialize_name + "_type");
+ try
+ {
+ value = convertTo<Serialize::ID>(res.Get(0, field->serialize_name));
+ }
+ catch (const ConvertException &ex)
+ {
+ return EVENT_STOP;
+ }
+
+ return EVENT_ALLOW;
+ }
+
+ private:
+ void DoSet(Serialize::Object *object, Serialize::FieldBase *field, bool is_object, const Anope::string *value)
+ {
+ if (!SQL)
+ return;
+
+ StartTransaction();
+
+ for (Query &q : SQL->CreateTable(prefix, object->GetSerializableType()->GetName()))
+ Run(q);
+
+ for (Query &q : SQL->AlterTable(prefix, object->GetSerializableType()->GetName(), field->serialize_name, is_object))
+ Run(q);
+
+ Query q;
+ q.SetValue("id", object->id);
+ if (value)
+ q.SetValue(field->serialize_name, *value);
+ else
+ q.SetNull(field->serialize_name);
+
+ for (Query &q2 : SQL->Replace(prefix + object->GetSerializableType()->GetName(), q, { "id" }))
+ Run(q2);
+ }
+
+ public:
+ EventReturn OnSerializeSet(Serialize::Object *object, Serialize::FieldBase *field, const Anope::string &value) override
+ {
+ DoSet(object, field, false, &value);
+ return EVENT_STOP;
+ }
+
+ EventReturn OnSerializeSetSerializable(Serialize::Object *object, Serialize::FieldBase *field, Serialize::Object *value) override
+ {
+ if (!SQL)
+ return EVENT_CONTINUE;
+
+ StartTransaction();
+
+ if (value)
+ {
+ Anope::string v = stringify(value->id);
+ DoSet(object, field, true, &v);
+
+ Query query;
+ query.SetValue("field", field->serialize_name);
+ query.SetValue("id", object->id);
+ query.SetValue("other_id", value->id);
+
+ for (Query &q : SQL->Replace(prefix + "edges", query, { "id", "field" }))
+ Run(q);
+ }
+ else
+ {
+ DoSet(object, field, true, nullptr);
+
+ Query query("DELETE FROM `" + prefix + "edges` WHERE `id` = @id@ AND `field` = @field@");
+ query.SetValue("id", object->id);
+ query.SetValue("field", field->serialize_name);
+ Run(query);
+ }
+
+ return EVENT_STOP;
+ }
+
+ EventReturn OnSerializeUnset(Serialize::Object *object, Serialize::FieldBase *field) override
+ {
+ DoSet(object, field, false, nullptr);
+ return EVENT_STOP;
+ }
+
+ EventReturn OnSerializeUnsetSerializable(Serialize::Object *object, Serialize::FieldBase *field) override
+ {
+ DoSet(object, field, true, nullptr);
+
+ Query query("DELETE FROM `" + prefix + "edges` WHERE `id` = @id@ AND `field` = @field@");
+ query.SetValue("id", object->id);
+ query.SetValue("field", field->serialize_name);
+ Run(query);
+
+ return EVENT_STOP;
+ }
+
+ EventReturn OnSerializeHasField(Serialize::Object *object, Serialize::FieldBase *field) override
+ {
+ SQL::Result::Value v;
+
+ return GetValue(object, field, v) && !v.null ? EVENT_STOP : EVENT_CONTINUE;
+ }
+
+ EventReturn OnSerializableGetId(Serialize::ID &id) override
+ {
+ if (!SQL)
+ return EVENT_CONTINUE;
+
+ StartTransaction();
+
+ id = SQL->GetID(prefix);
+ return EVENT_ALLOW;
+ }
+
+ void OnSerializableCreate(Serialize::Object *object) override
+ {
+ StartTransaction();
+
+ Query q = Query("INSERT INTO `" + prefix + "objects` (`id`,`type`) VALUES (@id@, @type@)");
+ q.SetValue("id", object->id);
+ q.SetValue("type", object->GetSerializableType()->GetName());
+ Run(q);
+ }
+
+ void OnSerializableDelete(Serialize::Object *object) override
+ {
+ StartTransaction();
+
+ Query query("DELETE FROM `" + prefix + object->GetSerializableType()->GetName() + "` WHERE `id` = " + stringify(object->id));
+ Run(query);
+ }
+};
+
+MODULE_INIT(DBMySQL)
+
diff --git a/modules/m_dns.cpp b/modules/dns.cpp
index 93f09246e..523b4044d 100644
--- a/modules/m_dns.cpp
+++ b/modules/dns.cpp
@@ -1,12 +1,20 @@
/*
+ * Anope IRC Services
*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2013-2016 Anope Team <team@anope.org>
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
*
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "module.h"
@@ -109,7 +117,7 @@ class Packet : public Query
return name;
}
-
+
Question UnpackQuestion(const unsigned char *input, unsigned short input_size, unsigned short &pos)
{
Question question;
@@ -212,7 +220,7 @@ class Packet : public Query
unsigned short id;
/* Flags on the packet */
unsigned short flags;
-
+
Packet(Manager *m, sockaddrs *a) : manager(m), id(0), flags(0)
{
if (a)
@@ -248,7 +256,7 @@ class Packet : public Query
for (unsigned i = 0; i < qdcount; ++i)
this->questions.push_back(this->UnpackQuestion(input, len, packet_pos));
-
+
for (unsigned i = 0; i < ancount; ++i)
this->answers.push_back(this->UnpackResourceRecord(input, len, packet_pos));
@@ -270,7 +278,7 @@ class Packet : public Query
{
if (output_size < HEADER_LENGTH)
throw SocketException("Unable to pack packet");
-
+
unsigned short pos = 0;
output[pos++] = this->id >> 8;
@@ -433,7 +441,7 @@ class Packet : public Query
l = htonl(manager->GetSerial());
memcpy(&output[pos], &l, 4);
pos += 4;
-
+
l = htonl(refresh); // Refresh
memcpy(&output[pos], &l, 4);
pos += 4;
@@ -459,7 +467,7 @@ class Packet : public Query
break;
}
}
-
+
return pos;
}
};
@@ -478,7 +486,7 @@ namespace DNS
class TCPSocket : public ListenSocket
{
Manager *manager;
-
+
public:
/* A TCP client */
class Client : public ClientSocket, public Timer, public ReplySocket
@@ -487,7 +495,7 @@ class TCPSocket : public ListenSocket
Packet *packet;
unsigned char packet_buffer[524];
int length;
-
+
public:
Client(Manager *m, TCPSocket *l, int fd, const sockaddrs &addr) : Socket(fd, l->IsIPv6()), ClientSocket(l, addr), Timer(5),
manager(m), packet(NULL), length(0)
@@ -500,18 +508,18 @@ class TCPSocket : public ListenSocket
Log(LOG_DEBUG_2) << "Resolver: Exiting client from " << clientaddr.addr();
delete packet;
}
-
+
/* Times out after a few seconds */
- void Tick(time_t) anope_override { }
+ void Tick(time_t) override { }
- void Reply(Packet *p) anope_override
+ void Reply(Packet *p) override
{
delete packet;
packet = p;
SocketEngine::Change(this, true, SF_WRITABLE);
}
- bool ProcessRead() anope_override
+ bool ProcessRead() override
{
Log(LOG_DEBUG_2) << "Resolver: Reading from DNS TCP socket";
@@ -531,7 +539,7 @@ class TCPSocket : public ListenSocket
return true;
}
- bool ProcessWrite() anope_override
+ bool ProcessWrite() override
{
Log(LOG_DEBUG_2) << "Resolver: Writing to DNS TCP socket";
@@ -560,13 +568,13 @@ class TCPSocket : public ListenSocket
};
TCPSocket(Manager *m, const Anope::string &ip, int port) : Socket(-1, ip.find(':') != Anope::string::npos), ListenSocket(ip, port, ip.find(':') != Anope::string::npos), manager(m) { }
-
- ClientSocket *OnAccept(int fd, const sockaddrs &addr) anope_override
+
+ ClientSocket *OnAccept(int fd, const sockaddrs &addr) override
{
return new Client(this->manager, this, fd, addr);
}
};
-
+
/* Listens for UDP requests */
class UDPSocket : public ReplySocket
{
@@ -581,16 +589,16 @@ class UDPSocket : public ReplySocket
for (unsigned i = 0; i < packets.size(); ++i)
delete packets[i];
}
-
- void Reply(Packet *p) anope_override
+
+ void Reply(Packet *p) override
{
packets.push_back(p);
SocketEngine::Change(this, true, SF_WRITABLE);
}
-
+
std::deque<Packet *>& GetPackets() { return packets; }
-
- bool ProcessRead() anope_override
+
+ bool ProcessRead() override
{
Log(LOG_DEBUG_2) << "Resolver: Reading from DNS UDP socket";
@@ -600,8 +608,8 @@ class UDPSocket : public ReplySocket
int length = recvfrom(this->GetFD(), reinterpret_cast<char *>(&packet_buffer), sizeof(packet_buffer), 0, &from_server.sa, &x);
return this->manager->HandlePacket(this, packet_buffer, length, &from_server);
}
-
- bool ProcessWrite() anope_override
+
+ bool ProcessWrite() override
{
Log(LOG_DEBUG_2) << "Resolver: Writing to DNS UDP socket";
@@ -623,7 +631,7 @@ class UDPSocket : public ReplySocket
if (packets.empty())
SocketEngine::Change(this, false, SF_WRITABLE);
-
+
return true;
}
};
@@ -638,7 +646,7 @@ class NotifySocket : public Socket
SocketEngine::Change(this, true, SF_WRITABLE);
}
- bool ProcessWrite() anope_override
+ bool ProcessWrite() override
{
if (!packet)
return false;
@@ -665,7 +673,7 @@ class MyManager : public Manager, public Timer
{
uint32_t serial;
- typedef TR1NS::unordered_map<Question, Query, Question::hash> cache_map;
+ typedef std::unordered_map<Question, Query, Question::hash> cache_map;
cache_map cache;
TCPSocket *tcpsock;
@@ -689,7 +697,7 @@ class MyManager : public Manager, public Timer
delete tcpsock;
for (std::map<unsigned short, Request *>::iterator it = this->requests.begin(), it_end = this->requests.end(); it != it_end;)
- {
+ {
Request *request = it->second;
++it;
@@ -749,7 +757,7 @@ class MyManager : public Manager, public Timer
}
public:
- void Process(Request *req) anope_override
+ void Process(Request *req) override
{
Log(LOG_DEBUG_2) << "Resolver: Processing request to lookup " << req->name << ", of type " << req->type;
@@ -767,7 +775,7 @@ class MyManager : public Manager, public Timer
this->requests[req->id] = req;
req->SetSecs(timeout);
-
+
Packet *p = new Packet(this, &this->addrs);
p->flags = QUERYFLAGS_RD;
p->id = req->id;
@@ -776,12 +784,12 @@ class MyManager : public Manager, public Timer
this->udpsock->Reply(p);
}
- void RemoveRequest(Request *req) anope_override
+ void RemoveRequest(Request *req) override
{
this->requests.erase(req->id);
}
- bool HandlePacket(ReplySocket *s, const unsigned char *const packet_buffer, int length, sockaddrs *from) anope_override
+ bool HandlePacket(ReplySocket *s, const unsigned char *const packet_buffer, int length, sockaddrs *from) override
{
if (length < Packet::HEADER_LENGTH)
return true;
@@ -840,7 +848,7 @@ class MyManager : public Manager, public Timer
}
}
- FOREACH_MOD(OnDnsRequest, (recv_packet, packet));
+ EventManager::Get()->Dispatch(&Event::DnsRequest::OnDnsRequest, recv_packet, packet);
for (unsigned i = 0; i < recv_packet.questions.size(); ++i)
{
@@ -928,17 +936,17 @@ class MyManager : public Manager, public Timer
request->OnLookupComplete(&recv_packet);
this->AddCache(recv_packet);
}
-
+
delete request;
return true;
}
- void UpdateSerial() anope_override
+ void UpdateSerial() override
{
serial = Anope::CurTime;
}
- void Notify(const Anope::string &zone) anope_override
+ void Notify(const Anope::string &zone) override
{
/* notify slaves of the update */
for (unsigned i = 0; i < notify.size(); ++i)
@@ -969,12 +977,12 @@ class MyManager : public Manager, public Timer
}
}
- uint32_t GetSerial() const anope_override
+ uint32_t GetSerial() const override
{
return serial;
}
- void Tick(time_t now) anope_override
+ void Tick(time_t now) override
{
Log(LOG_DEBUG_2) << "Resolver: Purging DNS cache";
@@ -989,7 +997,7 @@ class MyManager : public Manager, public Timer
this->cache.erase(it);
}
}
-
+
private:
/** Add a record to the dns cache
* @param r The record
@@ -1017,10 +1025,11 @@ class MyManager : public Manager, public Timer
return false;
}
-
+
};
class ModuleDNS : public Module
+ , public EventHook<Event::ModuleUnload>
{
MyManager manager;
@@ -1031,7 +1040,9 @@ class ModuleDNS : public Module
std::vector<std::pair<Anope::string, short> > notify;
public:
- ModuleDNS(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, EXTRA | VENDOR), manager(this)
+ ModuleDNS(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, EXTRA | VENDOR)
+ , EventHook<Event::ModuleUnload>(this)
+ , manager(this)
{
}
@@ -1048,16 +1059,16 @@ class ModuleDNS : public Module
}
}
- void OnReload(Configuration::Conf *conf) anope_override
+ void OnReload(Configuration::Conf *conf) override
{
Configuration::Block *block = conf->GetModule(this);
- nameserver = block->Get<const Anope::string>("nameserver", "127.0.0.1");
+ nameserver = block->Get<Anope::string>("nameserver", "127.0.0.1");
timeout = block->Get<time_t>("timeout", "5");
- ip = block->Get<const Anope::string>("ip", "0.0.0.0");
+ ip = block->Get<Anope::string>("ip", "0.0.0.0");
port = block->Get<int>("port", "53");
- admin = block->Get<const Anope::string>("admin", "admin@example.com");
- nameservers = block->Get<const Anope::string>("nameservers", "ns1.example.com");
+ admin = block->Get<Anope::string>("admin", "admin@example.com");
+ nameservers = block->Get<Anope::string>("nameservers", "ns1.example.com");
refresh = block->Get<int>("refresh", "3600");
for (int i = 0; i < block->CountBlock("notify"); ++i)
@@ -1114,7 +1125,7 @@ class ModuleDNS : public Module
}
}
- void OnModuleUnload(User *u, Module *m) anope_override
+ void OnModuleUnload(User *u, Module *m) override
{
for (std::map<unsigned short, Request *>::iterator it = this->manager.requests.begin(), it_end = this->manager.requests.end(); it != it_end;)
{
diff --git a/modules/m_dnsbl.cpp b/modules/dnsbl.cpp
index aa24e5ba6..a2c8a4380 100644
--- a/modules/m_dnsbl.cpp
+++ b/modules/dnsbl.cpp
@@ -1,9 +1,20 @@
/*
+ * Anope IRC Services
*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2013-2016 Anope Team <team@anope.org>
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "module.h"
@@ -11,9 +22,6 @@
using namespace DNS;
-static ServiceReference<XLineManager> akills("XLineManager", "xlinemanager/sgline");
-static ServiceReference<Manager> dnsmanager("DNS::Manager", "dns/manager");
-
struct Blacklist
{
struct Reply
@@ -43,14 +51,15 @@ struct Blacklist
class DNSBLResolver : public Request
{
+ ServiceReference<XLineManager> &akills;
Reference<User> user;
Blacklist blacklist;
bool add_to_akill;
public:
- DNSBLResolver(Module *c, User *u, const Blacklist &b, const Anope::string &host, bool add_akill) : Request(dnsmanager, c, host, QUERY_A, true), user(u), blacklist(b), add_to_akill(add_akill) { }
+ DNSBLResolver(ServiceReference<XLineManager> &a, Module *c, DNS::Manager *manager, User *u, const Blacklist &b, const Anope::string &host, bool add_akill) : Request(manager, c, host, QUERY_A, true), akills(a), user(u), blacklist(b), add_to_akill(add_akill) { }
- void OnLookupComplete(const Query *record) anope_override
+ void OnLookupComplete(const Query *record) override
{
if (!user || user->Quitting())
return;
@@ -78,11 +87,19 @@ class DNSBLResolver : public Request
reason = reason.replace_all_cs("%h", user->host);
reason = reason.replace_all_cs("%i", addr);
reason = reason.replace_all_cs("%r", reply ? reply->reason : "");
- reason = reason.replace_all_cs("%N", Config->GetBlock("networkinfo")->Get<const Anope::string>("networkname"));
+ reason = reason.replace_all_cs("%N", Config->GetBlock("networkinfo")->Get<Anope::string>("networkname"));
- BotInfo *OperServ = Config->GetClient("OperServ");
+ ServiceBot *OperServ = Config->GetClient("OperServ");
Log(creator, "dnsbl", OperServ) << user->GetMask() << " (" << addr << ") appears in " << this->blacklist.name;
- XLine *x = new XLine("*@" + addr, OperServ ? OperServ->nick : "m_dnsbl", Anope::CurTime + this->blacklist.bantime, reason, XLineManager::GenerateUID());
+
+ XLine *x = Serialize::New<XLine *>();
+ x->SetMask("*@" + addr);
+ x->SetBy(OperServ ? OperServ->nick : "m_dnsbl");
+ x->SetCreated(Anope::CurTime);
+ x->SetExpires(Anope::CurTime + this->blacklist.bantime);
+ x->SetReason(reason);
+ x->SetID(XLineManager::GenerateUID());
+
if (this->add_to_akill && akills)
{
akills->AddXLine(x);
@@ -97,20 +114,25 @@ class DNSBLResolver : public Request
};
class ModuleDNSBL : public Module
+ , public EventHook<Event::UserConnect>
{
+ ServiceReference<DNS::Manager> manager;
std::vector<Blacklist> blacklists;
std::set<Anope::string> exempts;
bool check_on_connect;
bool check_on_netburst;
bool add_to_akill;
+ ServiceReference<XLineManager> akill;
public:
ModuleDNSBL(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR | EXTRA)
+ , EventHook<Event::UserConnect>(this)
+ , akill("sgline")
{
}
- void OnReload(Configuration::Conf *conf) anope_override
+ void OnReload(Configuration::Conf *conf) override
{
Configuration::Block *block = conf->GetModule(this);
this->check_on_connect = block->Get<bool>("check_on_connect");
@@ -149,9 +171,9 @@ class ModuleDNSBL : public Module
this->exempts.insert(block->Get<Anope::string>("ip"));
}
- void OnUserConnect(User *user, bool &exempt) anope_override
+ void OnUserConnect(User *user, bool &exempt) override
{
- if (exempt || user->Quitting() || (!this->check_on_connect && !Me->IsSynced()) || !dnsmanager)
+ if (exempt || user->Quitting() || (!this->check_on_connect && !Me->IsSynced()) || !manager)
return;
if (!this->check_on_netburst && !user->server->IsSynced())
@@ -179,8 +201,8 @@ class ModuleDNSBL : public Module
DNSBLResolver *res = NULL;
try
{
- res = new DNSBLResolver(this, user, b, dnsbl_host, this->add_to_akill);
- dnsmanager->Process(res);
+ res = new DNSBLResolver(akill, this, manager, user, b, dnsbl_host, this->add_to_akill);
+ manager->Process(res);
}
catch (const SocketException &ex)
{
diff --git a/modules/encryption/CMakeLists.txt b/modules/encryption/CMakeLists.txt
new file mode 100644
index 000000000..cd225a94d
--- /dev/null
+++ b/modules/encryption/CMakeLists.txt
@@ -0,0 +1 @@
+build_modules(${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/modules/encryption/enc_bcrypt.cpp b/modules/encryption/bcrypt.cpp
index a30b925b0..622fef745 100644
--- a/modules/encryption/enc_bcrypt.cpp
+++ b/modules/encryption/bcrypt.cpp
@@ -1,11 +1,23 @@
-/* Module for providing bcrypt hashing
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2014-2016 Anope Team <team@anope.org>
*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
*
- * This program is free but copyrighted software; see the file COPYING for
- * details.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+/*
* Most of the code in this file is taken from
* http://openwall.com/crypt/crypt_blowfish-1.2.tar.gz
*/
@@ -839,8 +851,11 @@ char *_crypt_gensalt_blowfish_rn(const char *prefix, unsigned long count,
#include "module.h"
#include "modules/encryption.h"
+#include "modules/nickserv.h"
class EBCRYPT : public Module
+ , public EventHook<Event::Encrypt>
+ , public EventHook<Event::CheckAuthentication>
{
unsigned int rounds;
@@ -873,8 +888,10 @@ class EBCRYPT : public Module
}
public:
- EBCRYPT(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, ENCRYPTION | VENDOR),
- rounds(10)
+ EBCRYPT(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, ENCRYPTION | VENDOR)
+ , EventHook<Event::Encrypt>(this)
+ , EventHook<Event::CheckAuthentication>(this)
+ , rounds(10)
{
// Test a pre-calculated hash
bool test = Compare("Test!", "$2a$10$x9AQFAQScY0v9KF2suqkEOepsHFrG.CXHbIXI.1F28SfSUb56A/7K");
@@ -886,28 +903,28 @@ class EBCRYPT : public Module
throw ModuleException("BCrypt could not load!");
}
- EventReturn OnEncrypt(const Anope::string &src, Anope::string &dest) anope_override
+ EventReturn OnEncrypt(const Anope::string &src, Anope::string &dest) override
{
dest = "bcrypt:" + Generate(src, Salt());
Log(LOG_DEBUG_2) << "(enc_bcrypt) hashed password from [" << src << "] to [" << dest << "]";
return EVENT_ALLOW;
}
- void OnCheckAuthentication(User *, IdentifyRequest *req) anope_override
+ void OnCheckAuthentication(User *, NickServ::IdentifyRequest *req) override
{
- const NickAlias *na = NickAlias::Find(req->GetAccount());
+ NickServ::Nick *na = NickServ::FindNick(req->GetAccount());
if (na == NULL)
return;
- NickCore *nc = na->nc;
+ NickServ::Account *nc = na->GetAccount();
- size_t pos = nc->pass.find(':');
+ size_t pos = nc->GetPassword().find(':');
if (pos == Anope::string::npos)
return;
- Anope::string hash_method(nc->pass.begin(), nc->pass.begin() + pos);
+ Anope::string hash_method(nc->GetPassword().begin(), nc->GetPassword().begin() + pos);
if (hash_method != "bcrypt")
return;
- if (Compare(req->GetPassword(), nc->pass.substr(7)))
+ if (Compare(req->GetPassword(), nc->GetPassword().substr(7)))
{
/* if we are NOT the first module in the list,
* we want to re-encrypt the pass with the new encryption
@@ -916,24 +933,28 @@ class EBCRYPT : public Module
unsigned int hashrounds = 0;
try
{
- size_t roundspos = nc->pass.find('$', 11);
+ size_t roundspos = nc->GetPassword().find('$', 11);
if (roundspos == Anope::string::npos)
throw ConvertException("Could not find hashrounds");
- hashrounds = convertTo<unsigned int>(nc->pass.substr(11, roundspos - 11));
+ hashrounds = convertTo<unsigned int>(nc->GetPassword().substr(11, roundspos - 11));
}
catch (const ConvertException &)
{
- Log(this) << "Could not get the round size of a hash. This is probably a bug. Hash: " << nc->pass;
+ Log(this) << "Could not get the round size of a hash. This is probably a bug. Hash: " << nc->GetPassword();
}
if (ModuleManager::FindFirstOf(ENCRYPTION) != this || (hashrounds && hashrounds != rounds))
- Anope::Encrypt(req->GetPassword(), nc->pass);
+ {
+ Anope::string p;
+ Anope::Encrypt(req->GetPassword(), p);
+ nc->SetPassword(p);
+ }
req->Success(this);
}
}
- void OnReload(Configuration::Conf *conf) anope_override
+ void OnReload(Configuration::Conf *conf) override
{
Configuration::Block *block = conf->GetModule(this);
rounds = block->Get<unsigned int>("rounds", "10");
diff --git a/modules/encryption/enc_none.cpp b/modules/encryption/enc_none.cpp
deleted file mode 100644
index 0302316f7..000000000
--- a/modules/encryption/enc_none.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-/* Module for plain text encryption.
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * This program is free but copyrighted software; see the file COPYING for
- * details.
- */
-
-#include "module.h"
-
-class ENone : public Module
-{
- public:
- ENone(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, ENCRYPTION | VENDOR)
- {
-
- }
-
- EventReturn OnEncrypt(const Anope::string &src, Anope::string &dest) anope_override
- {
- Anope::string buf = "plain:";
- Anope::string cpass;
- Anope::B64Encode(src, cpass);
- buf += cpass;
- Log(LOG_DEBUG_2) << "(enc_none) hashed password from [" << src << "] to [" << buf << "]";
- dest = buf;
- return EVENT_ALLOW;
- }
-
- EventReturn OnDecrypt(const Anope::string &hashm, const Anope::string &src, Anope::string &dest) anope_override
- {
- if (!hashm.equals_cs("plain"))
- return EVENT_CONTINUE;
- size_t pos = src.find(':');
- Anope::string buf = src.substr(pos + 1);
- Anope::B64Decode(buf, dest);
- return EVENT_ALLOW;
- }
-
- void OnCheckAuthentication(User *, IdentifyRequest *req) anope_override
- {
- const NickAlias *na = NickAlias::Find(req->GetAccount());
- if (na == NULL)
- return;
- NickCore *nc = na->nc;
-
- size_t pos = nc->pass.find(':');
- if (pos == Anope::string::npos)
- return;
- Anope::string hash_method(nc->pass.begin(), nc->pass.begin() + pos);
- if (!hash_method.equals_cs("plain"))
- return;
-
- Anope::string buf;
- this->OnEncrypt(req->GetPassword(), buf);
- if (nc->pass.equals_cs(buf))
- {
- /* if we are NOT the first module in the list,
- * we want to re-encrypt the pass with the new encryption
- */
- if (ModuleManager::FindFirstOf(ENCRYPTION) != this)
- Anope::Encrypt(req->GetPassword(), nc->pass);
- req->Success(this);
- }
- }
-};
-
-MODULE_INIT(ENone)
diff --git a/modules/encryption/enc_md5.cpp b/modules/encryption/md5.cpp
index 1dab95f0b..ca3bfcc9a 100644
--- a/modules/encryption/enc_md5.cpp
+++ b/modules/encryption/md5.cpp
@@ -1,14 +1,26 @@
-/* Module for encryption using MD5.
+/*
+ * Anope IRC Services
*
- * Modified for Anope.
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2006-2016 Anope Team <team@anope.org>
*
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+/*
* Taken from IRC Services and is copyright (c) 1996-2002 Andrew Church.
* E-mail: <achurch@achurch.org>
* Parts written by Andrew Kempe and others.
- * This program is free but copyrighted software; see the file COPYING for
- * details.
*/
#include "module.h"
@@ -250,7 +262,7 @@ class MD5Context : public Encryption::Context
* operation, processing another message block, and updating the
* context.
*/
- void Update(const unsigned char *input, size_t len) anope_override
+ void Update(const unsigned char *input, size_t len) override
{
unsigned i, index, partLen;
@@ -284,8 +296,8 @@ class MD5Context : public Encryption::Context
/* MD5 finalization. Ends an MD5 message-digest opera
* the message digest and zeroizing the context.
- */
- void Finalize() anope_override
+ */
+ void Finalize() override
{
unsigned char bits[8];
unsigned index, padLen;
@@ -309,7 +321,7 @@ class MD5Context : public Encryption::Context
memset(this->buffer, 0, sizeof(this->buffer));
}
- Encryption::Hash GetFinalizedHash() anope_override
+ Encryption::Hash GetFinalizedHash() override
{
Encryption::Hash hash;
hash.first = this->digest;
@@ -323,12 +335,12 @@ class MD5Provider : public Encryption::Provider
public:
MD5Provider(Module *creator) : Encryption::Provider(creator, "md5") { }
- Encryption::Context *CreateContext(Encryption::IV *iv) anope_override
+ Encryption::Context *CreateContext(Encryption::IV *iv) override
{
return new MD5Context(iv);
}
- Encryption::IV GetDefaultIV() anope_override
+ Encryption::IV GetDefaultIV() override
{
Encryption::IV iv;
iv.first = md5_iv;
@@ -338,17 +350,22 @@ class MD5Provider : public Encryption::Provider
};
class EMD5 : public Module
+ , public EventHook<Event::Encrypt>
+ , public EventHook<Event::CheckAuthentication>
{
MD5Provider md5provider;
public:
- EMD5(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, ENCRYPTION | VENDOR),
- md5provider(this)
+ EMD5(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, ENCRYPTION | VENDOR)
+ , EventHook<Event::Encrypt>(this)
+ , EventHook<Event::CheckAuthentication>(this)
+ , md5provider(this)
{
-
+ if (ModuleManager::FindFirstOf(ENCRYPTION) == this)
+ throw ModuleException("enc_md5 is deprecated and can not be used as a primary encryption method");
}
- EventReturn OnEncrypt(const Anope::string &src, Anope::string &dest) anope_override
+ EventReturn OnEncrypt(const Anope::string &src, Anope::string &dest) override
{
MD5Context context;
@@ -364,29 +381,33 @@ class EMD5 : public Module
return EVENT_ALLOW;
}
- void OnCheckAuthentication(User *, IdentifyRequest *req) anope_override
+ void OnCheckAuthentication(User *, NickServ::IdentifyRequest *req) override
{
- const NickAlias *na = NickAlias::Find(req->GetAccount());
+ NickServ::Nick *na = NickServ::FindNick(req->GetAccount());
if (na == NULL)
return;
- NickCore *nc = na->nc;
+ NickServ::Account *nc = na->GetAccount();
- size_t pos = nc->pass.find(':');
+ size_t pos = nc->GetPassword().find(':');
if (pos == Anope::string::npos)
return;
- Anope::string hash_method(nc->pass.begin(), nc->pass.begin() + pos);
+ Anope::string hash_method(nc->GetPassword().begin(), nc->GetPassword().begin() + pos);
if (!hash_method.equals_cs("md5"))
return;
Anope::string buf;
this->OnEncrypt(req->GetPassword(), buf);
- if (nc->pass.equals_cs(buf))
+ if (nc->GetPassword().equals_cs(buf))
{
/* if we are NOT the first module in the list,
* we want to re-encrypt the pass with the new encryption
*/
if (ModuleManager::FindFirstOf(ENCRYPTION) != this)
- Anope::Encrypt(req->GetPassword(), nc->pass);
+ {
+ Anope::string p;
+ Anope::Encrypt(req->GetPassword(), p);
+ nc->SetPassword(p);
+ }
req->Success(this);
}
}
diff --git a/modules/encryption/none.cpp b/modules/encryption/none.cpp
new file mode 100644
index 000000000..3b1f49d33
--- /dev/null
+++ b/modules/encryption/none.cpp
@@ -0,0 +1,78 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2006-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+
+class ENone : public Module
+ , public EventHook<Event::Encrypt>
+ , public EventHook<Event::CheckAuthentication>
+{
+ public:
+ ENone(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, ENCRYPTION | VENDOR)
+ , EventHook<Event::Encrypt>(this)
+ , EventHook<Event::CheckAuthentication>(this)
+ {
+ if (ModuleManager::FindFirstOf(ENCRYPTION) == this)
+ throw ModuleException("enc_none is deprecated and can not be used as a primary encryption method");
+ }
+
+ EventReturn OnEncrypt(const Anope::string &src, Anope::string &dest) override
+ {
+ Anope::string buf = "plain:";
+ Anope::string cpass;
+ Anope::B64Encode(src, cpass);
+ buf += cpass;
+ Log(LOG_DEBUG_2) << "(enc_none) hashed password from [" << src << "] to [" << buf << "]";
+ dest = buf;
+ return EVENT_ALLOW;
+ }
+
+ void OnCheckAuthentication(User *, NickServ::IdentifyRequest *req) override
+ {
+ NickServ::Nick *na = NickServ::FindNick(req->GetAccount());
+ if (na == NULL)
+ return;
+ NickServ::Account *nc = na->GetAccount();
+
+ size_t pos = nc->GetPassword().find(':');
+ if (pos == Anope::string::npos)
+ return;
+ Anope::string hash_method(nc->GetPassword().begin(), nc->GetPassword().begin() + pos);
+ if (!hash_method.equals_cs("plain"))
+ return;
+
+ Anope::string buf;
+ this->OnEncrypt(req->GetPassword(), buf);
+ if (nc->GetPassword().equals_cs(buf))
+ {
+ /* if we are NOT the first module in the list,
+ * we want to re-encrypt the pass with the new encryption
+ */
+ if (ModuleManager::FindFirstOf(ENCRYPTION) != this)
+ {
+ Anope::string p;
+ Anope::Encrypt(req->GetPassword(), p);
+ nc->SetPassword(p);
+ }
+ req->Success(this);
+ }
+ }
+};
+
+MODULE_INIT(ENone)
diff --git a/modules/encryption/enc_old.cpp b/modules/encryption/old.cpp
index 7c751df93..bfe7131bc 100644
--- a/modules/encryption/enc_old.cpp
+++ b/modules/encryption/old.cpp
@@ -1,32 +1,43 @@
-/* Include file for high-level encryption routines.
+/*
+ * Anope IRC Services
*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
*
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "module.h"
#include "modules/encryption.h"
-static ServiceReference<Encryption::Provider> md5("Encryption::Provider", "md5");
-
class OldMD5Provider : public Encryption::Provider
{
+ ServiceReference<Encryption::Provider> md5;
+
public:
- OldMD5Provider(Module *creator) : Encryption::Provider(creator, "oldmd5") { }
+ OldMD5Provider(Module *creator) : Encryption::Provider(creator, "oldmd5")
+ , md5("md5")
+ {
+ }
- Encryption::Context *CreateContext(Encryption::IV *iv) anope_override
+ Encryption::Context *CreateContext(Encryption::IV *iv) override
{
if (md5)
return md5->CreateContext(iv);
return NULL;
}
- Encryption::IV GetDefaultIV() anope_override
+ Encryption::IV GetDefaultIV() override
{
if (md5)
return md5->GetDefaultIV();
@@ -35,15 +46,23 @@ class OldMD5Provider : public Encryption::Provider
};
class EOld : public Module
+ , public EventHook<Event::Encrypt>
+ , public EventHook<Event::CheckAuthentication>
{
OldMD5Provider oldmd5provider;
+ ServiceReference<Encryption::Provider> md5;
inline static char XTOI(char c) { return c > 9 ? c - 'A' + 10 : c - '0'; }
public:
- EOld(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, ENCRYPTION | VENDOR),
- oldmd5provider(this)
+ EOld(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, ENCRYPTION | VENDOR)
+ , EventHook<Event::Encrypt>(this)
+ , EventHook<Event::CheckAuthentication>(this)
+ , oldmd5provider(this)
+ , md5("md5")
{
+ if (ModuleManager::FindFirstOf(ENCRYPTION) == this)
+ throw ModuleException("enc_old is deprecated and can not be used as a primary encryption method");
ModuleManager::LoadModule("enc_md5", User::Find(creator));
if (!md5)
@@ -51,7 +70,7 @@ class EOld : public Module
}
- EventReturn OnEncrypt(const Anope::string &src, Anope::string &dest) anope_override
+ EventReturn OnEncrypt(const Anope::string &src, Anope::string &dest) override
{
if (!md5)
return EVENT_CONTINUE;
@@ -79,29 +98,33 @@ class EOld : public Module
return EVENT_ALLOW;
}
- void OnCheckAuthentication(User *, IdentifyRequest *req) anope_override
+ void OnCheckAuthentication(User *, NickServ::IdentifyRequest *req) override
{
- const NickAlias *na = NickAlias::Find(req->GetAccount());
+ NickServ::Nick *na = NickServ::FindNick(req->GetAccount());
if (na == NULL)
return;
- NickCore *nc = na->nc;
+ NickServ::Account *nc = na->GetAccount();
- size_t pos = nc->pass.find(':');
+ size_t pos = nc->GetPassword().find(':');
if (pos == Anope::string::npos)
return;
- Anope::string hash_method(nc->pass.begin(), nc->pass.begin() + pos);
+ Anope::string hash_method(nc->GetPassword().begin(), nc->GetPassword().begin() + pos);
if (!hash_method.equals_cs("oldmd5"))
return;
Anope::string buf;
this->OnEncrypt(req->GetPassword(), buf);
- if (nc->pass.equals_cs(buf))
+ if (nc->GetPassword().equals_cs(buf))
{
/* if we are NOT the first module in the list,
* we want to re-encrypt the pass with the new encryption
*/
if (ModuleManager::FindFirstOf(ENCRYPTION) != this)
- Anope::Encrypt(req->GetPassword(), nc->pass);
+ {
+ Anope::string p;
+ Anope::Encrypt(req->GetPassword(), p);
+ nc->SetPassword(p);
+ }
req->Success(this);
}
}
diff --git a/modules/encryption/enc_sha1.cpp b/modules/encryption/sha1.cpp
index b9782bc34..ea108f49c 100644
--- a/modules/encryption/enc_sha1.cpp
+++ b/modules/encryption/sha1.cpp
@@ -1,9 +1,23 @@
/*
+ * Anope IRC Services
*
- * Modified for Anope.
- * (C) 2006-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2006-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+/*
SHA-1 in C
By Steve Reid <steve@edmweb.com>
100% Public Domain
@@ -125,7 +139,7 @@ class SHA1Context : public Encryption::Context
memset(this->digest, 0, sizeof(this->digest));
}
- void Update(const unsigned char *data, size_t len) anope_override
+ void Update(const unsigned char *data, size_t len) override
{
uint32_t i, j;
@@ -146,7 +160,7 @@ class SHA1Context : public Encryption::Context
memcpy(&this->buffer[j], &data[i], len - i);
}
- void Finalize() anope_override
+ void Finalize() override
{
uint32_t i;
unsigned char finalcount[8];
@@ -169,7 +183,7 @@ class SHA1Context : public Encryption::Context
this->Transform(this->buffer);
}
- Encryption::Hash GetFinalizedHash() anope_override
+ Encryption::Hash GetFinalizedHash() override
{
Encryption::Hash hash;
hash.first = this->digest;
@@ -183,12 +197,12 @@ class SHA1Provider : public Encryption::Provider
public:
SHA1Provider(Module *creator) : Encryption::Provider(creator, "sha1") { }
- Encryption::Context *CreateContext(Encryption::IV *iv) anope_override
+ Encryption::Context *CreateContext(Encryption::IV *iv) override
{
return new SHA1Context(iv);
}
- Encryption::IV GetDefaultIV() anope_override
+ Encryption::IV GetDefaultIV() override
{
Encryption::IV iv;
iv.first = sha1_iv;
@@ -198,17 +212,22 @@ class SHA1Provider : public Encryption::Provider
};
class ESHA1 : public Module
+ , public EventHook<Event::Encrypt>
+ , public EventHook<Event::CheckAuthentication>
{
SHA1Provider sha1provider;
public:
- ESHA1(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, ENCRYPTION | VENDOR),
- sha1provider(this)
+ ESHA1(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, ENCRYPTION | VENDOR)
+ , EventHook<Event::Encrypt>(this)
+ , EventHook<Event::CheckAuthentication>(this)
+ , sha1provider(this)
{
-
+ if (ModuleManager::FindFirstOf(ENCRYPTION) == this)
+ throw ModuleException("enc_sha1 is deprecated and can not be used as a primary encryption method");
}
- EventReturn OnEncrypt(const Anope::string &src, Anope::string &dest) anope_override
+ EventReturn OnEncrypt(const Anope::string &src, Anope::string &dest) override
{
SHA1Context context;
@@ -224,26 +243,30 @@ class ESHA1 : public Module
return EVENT_ALLOW;
}
- void OnCheckAuthentication(User *, IdentifyRequest *req) anope_override
+ void OnCheckAuthentication(User *, NickServ::IdentifyRequest *req) override
{
- const NickAlias *na = NickAlias::Find(req->GetAccount());
+ NickServ::Nick *na = NickServ::FindNick(req->GetAccount());
if (na == NULL)
return;
- NickCore *nc = na->nc;
+ NickServ::Account *nc = na->GetAccount();
- size_t pos = nc->pass.find(':');
+ size_t pos = nc->GetPassword().find(':');
if (pos == Anope::string::npos)
return;
- Anope::string hash_method(nc->pass.begin(), nc->pass.begin() + pos);
+ Anope::string hash_method(nc->GetPassword().begin(), nc->GetPassword().begin() + pos);
if (!hash_method.equals_cs("sha1"))
return;
Anope::string buf;
this->OnEncrypt(req->GetPassword(), buf);
- if (nc->pass.equals_cs(buf))
+ if (nc->GetPassword().equals_cs(buf))
{
if (ModuleManager::FindFirstOf(ENCRYPTION) != this)
- Anope::Encrypt(req->GetPassword(), nc->pass);
+ {
+ Anope::string p;
+ Anope::Encrypt(req->GetPassword(), p);
+ nc->SetPassword(p);
+ }
req->Success(this);
}
}
diff --git a/modules/encryption/enc_sha256.cpp b/modules/encryption/sha256.cpp
index 4f111da34..e05629a50 100644
--- a/modules/encryption/enc_sha256.cpp
+++ b/modules/encryption/sha256.cpp
@@ -1,18 +1,20 @@
-/* This module generates and compares password hashes using SHA256 algorithms.
+/*
+ * Anope IRC Services
*
- * If an intruder gets access to your system or uses a brute force attack,
- * salt will not provide much value.
- * IMPORTANT: DATA HASHES CANNOT BE "DECRYPTED" BACK TO PLAIN TEXT.
+ * Copyright (C) 2010-2016 Anope Team <team@anope.org>
*
- * Modified for Anope.
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
*
- * Taken from InspIRCd ( www.inspircd.org )
- * see http://wiki.inspircd.org/Credits
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * This program is free but copyrighted software; see
- * the file COPYING for details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
/* FIPS 180-2 SHA-224/256/384/512 implementation
@@ -55,7 +57,6 @@ static const unsigned SHA256_BLOCK_SIZE = 512 / 8;
inline static uint32_t SHFR(uint32_t x, uint32_t n) { return x >> n; }
inline static uint32_t ROTR(uint32_t x, uint32_t n) { return (x >> n) | (x << ((sizeof(x) << 3) - n)); }
-inline static uint32_t ROTL(uint32_t x, uint32_t n) { return (x << n) | (x >> ((sizeof(x) << 3) - n)); }
inline static uint32_t CH(uint32_t x, uint32_t y, uint32_t z) { return (x & y) ^ (~x & z); }
inline static uint32_t MAJ(uint32_t x, uint32_t y, uint32_t z) { return (x & y) ^ (x & z) ^ (y & z); }
@@ -173,7 +174,7 @@ class SHA256Context : public Encryption::Context
memset(this->digest, 0, sizeof(this->digest));
}
- void Update(const unsigned char *message, size_t mlen) anope_override
+ void Update(const unsigned char *message, size_t mlen) override
{
unsigned tmp_len = SHA256_BLOCK_SIZE - this->len, rem_len = mlen < tmp_len ? mlen : tmp_len;
@@ -195,7 +196,7 @@ class SHA256Context : public Encryption::Context
this->tot_len += (block_nb + 1) << 6;
}
- void Finalize() anope_override
+ void Finalize() override
{
unsigned block_nb = 1 + ((SHA256_BLOCK_SIZE - 9) < (this->len % SHA256_BLOCK_SIZE));
unsigned len_b = (this->tot_len + this->len) << 3;
@@ -208,7 +209,7 @@ class SHA256Context : public Encryption::Context
UNPACK32(this->h[i], &this->digest[i << 2]);
}
- Encryption::Hash GetFinalizedHash() anope_override
+ Encryption::Hash GetFinalizedHash() override
{
Encryption::Hash hash;
hash.first = this->digest;
@@ -222,12 +223,12 @@ class SHA256Provider : public Encryption::Provider
public:
SHA256Provider(Module *creator) : Encryption::Provider(creator, "sha256") { }
- Encryption::Context *CreateContext(Encryption::IV *iv) anope_override
+ Encryption::Context *CreateContext(Encryption::IV *iv) override
{
return new SHA256Context(iv);
}
- Encryption::IV GetDefaultIV() anope_override
+ Encryption::IV GetDefaultIV() override
{
Encryption::IV iv;
iv.first = sha256_h0;
@@ -237,6 +238,8 @@ class SHA256Provider : public Encryption::Provider
};
class ESHA256 : public Module
+ , public EventHook<Event::Encrypt>
+ , public EventHook<Event::CheckAuthentication>
{
SHA256Provider sha256provider;
@@ -273,15 +276,17 @@ class ESHA256 : public Module
}
public:
- ESHA256(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, ENCRYPTION | VENDOR),
- sha256provider(this)
+ ESHA256(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, ENCRYPTION | VENDOR)
+ , EventHook<Event::Encrypt>(this)
+ , EventHook<Event::CheckAuthentication>(this)
+ , sha256provider(this)
{
use_iv = false;
}
- EventReturn OnEncrypt(const Anope::string &src, Anope::string &dest) anope_override
+ EventReturn OnEncrypt(const Anope::string &src, Anope::string &dest) override
{
if (!use_iv)
NewRandomIV();
@@ -302,31 +307,35 @@ class ESHA256 : public Module
return EVENT_ALLOW;
}
- void OnCheckAuthentication(User *, IdentifyRequest *req) anope_override
+ void OnCheckAuthentication(User *, NickServ::IdentifyRequest *req) override
{
- const NickAlias *na = NickAlias::Find(req->GetAccount());
+ NickServ::Nick *na = NickServ::FindNick(req->GetAccount());
if (na == NULL)
return;
- NickCore *nc = na->nc;
+ NickServ::Account *nc = na->GetAccount();
- size_t pos = nc->pass.find(':');
+ size_t pos = nc->GetPassword().find(':');
if (pos == Anope::string::npos)
return;
- Anope::string hash_method(nc->pass.begin(), nc->pass.begin() + pos);
+ Anope::string hash_method(nc->GetPassword().substr(0, pos));
if (!hash_method.equals_cs("sha256"))
return;
- GetIVFromPass(nc->pass);
+ GetIVFromPass(nc->GetPassword());
use_iv = true;
Anope::string buf;
this->OnEncrypt(req->GetPassword(), buf);
- if (nc->pass.equals_cs(buf))
+ if (nc->GetPassword().equals_cs(buf))
{
/* if we are NOT the first module in the list,
* we want to re-encrypt the pass with the new encryption
*/
if (ModuleManager::FindFirstOf(ENCRYPTION) != this)
- Anope::Encrypt(req->GetPassword(), nc->pass);
+ {
+ Anope::string p;
+ Anope::Encrypt(req->GetPassword(), p);
+ nc->SetPassword(p);
+ }
req->Success(this);
}
}
diff --git a/modules/extra/m_ldap.cpp b/modules/extra/ldap.cpp
index 27bb3ef50..6d4c0bd62 100644
--- a/modules/extra/m_ldap.cpp
+++ b/modules/extra/ldap.cpp
@@ -1,12 +1,20 @@
/*
+ * Anope IRC Services
*
- * (C) 2011-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2011-2016 Anope Team <team@anope.org>
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
*
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
/* RequiredLibraries: ldap_r,lber */
@@ -270,18 +278,18 @@ class LDAPService : public LDAPProvider, public Thread, public Condition
ldap_unbind_ext(this->con, NULL, NULL);
}
- void BindAsAdmin(LDAPInterface *i) anope_override
+ void BindAsAdmin(LDAPInterface *i) override
{
this->Bind(i, this->admin_binddn, this->admin_pass);
}
- void Bind(LDAPInterface *i, const Anope::string &who, const Anope::string &pass) anope_override
+ void Bind(LDAPInterface *i, const Anope::string &who, const Anope::string &pass) override
{
LDAPBind *b = new LDAPBind(this, i, who, pass);
QueueRequest(b);
}
- void Search(LDAPInterface *i, const Anope::string &base, const Anope::string &filter) anope_override
+ void Search(LDAPInterface *i, const Anope::string &base, const Anope::string &filter) override
{
if (i == NULL)
throw LDAPException("No interface");
@@ -290,7 +298,7 @@ class LDAPService : public LDAPProvider, public Thread, public Condition
QueueRequest(s);
}
- void Add(LDAPInterface *i, const Anope::string &dn, LDAPMods &attributes) anope_override
+ void Add(LDAPInterface *i, const Anope::string &dn, LDAPMods &attributes) override
{
LDAPAdd *add = new LDAPAdd(this, i, dn, attributes);
QueueRequest(add);
@@ -430,6 +438,7 @@ class LDAPService : public LDAPProvider, public Thread, public Condition
};
class ModuleLDAP : public Module, public Pipe
+ , public EventHook<Event::ModuleUnload>
{
std::map<Anope::string, LDAPService *> LDAPServices;
@@ -452,7 +461,7 @@ class ModuleLDAP : public Module, public Pipe
LDAPServices.clear();
}
- void OnReload(Configuration::Conf *config) anope_override
+ void OnReload(Configuration::Conf *config) override
{
Configuration::Block *conf = config->GetModule(this);
@@ -465,7 +474,7 @@ class ModuleLDAP : public Module, public Pipe
++it;
for (i = 0; i < conf->CountBlock("ldap"); ++i)
- if (conf->GetBlock("ldap", i)->Get<const Anope::string>("name", "ldap/main") == cname)
+ if (conf->GetBlock("ldap", i)->Get<Anope::string>("name", "ldap/main") == cname)
break;
if (i == conf->CountBlock("ldap"))
@@ -484,13 +493,13 @@ class ModuleLDAP : public Module, public Pipe
{
Configuration::Block *ldap = conf->GetBlock("ldap", i);
- const Anope::string &connname = ldap->Get<const Anope::string>("name", "ldap/main");
+ const Anope::string &connname = ldap->Get<Anope::string>("name", "ldap/main");
if (this->LDAPServices.find(connname) == this->LDAPServices.end())
{
- const Anope::string &server = ldap->Get<const Anope::string>("server", "127.0.0.1");
- const Anope::string &admin_binddn = ldap->Get<const Anope::string>("admin_binddn");
- const Anope::string &admin_password = ldap->Get<const Anope::string>("admin_password");
+ const Anope::string &server = ldap->Get<Anope::string>("server", "127.0.0.1");
+ const Anope::string &admin_binddn = ldap->Get<Anope::string>("admin_binddn");
+ const Anope::string &admin_password = ldap->GetAnope::string>("admin_password");
try
{
@@ -508,7 +517,7 @@ class ModuleLDAP : public Module, public Pipe
}
}
- void OnModuleUnload(User *, Module *m) anope_override
+ void OnModuleUnload(User *, Module *m) override
{
for (std::map<Anope::string, LDAPService *>::iterator it = this->LDAPServices.begin(); it != this->LDAPServices.end(); ++it)
{
@@ -545,7 +554,7 @@ class ModuleLDAP : public Module, public Pipe
}
}
- void OnNotify() anope_override
+ void OnNotify() override
{
for (std::map<Anope::string, LDAPService *>::iterator it = this->LDAPServices.begin(); it != this->LDAPServices.end(); ++it)
{
@@ -575,7 +584,7 @@ class ModuleLDAP : public Module, public Pipe
delete req;
}
- }
+ }
}
};
diff --git a/modules/extra/m_ldap_authentication.cpp b/modules/extra/ldap_authentication.cpp
index 0f18916b6..ef61b6940 100644
--- a/modules/extra/m_ldap_authentication.cpp
+++ b/modules/extra/ldap_authentication.cpp
@@ -1,13 +1,25 @@
/*
+ * Anope IRC Services
*
- * (C) 2011-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2011-2016 Anope Team <team@anope.org>
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "module.h"
#include "modules/ldap.h"
+#include "modules/nickserv.h"
static Module *me;
@@ -29,7 +41,7 @@ struct IdentifyInfo
{
req->Hold(me);
}
-
+
~IdentifyInfo()
{
req->Release(me);
@@ -53,7 +65,7 @@ class IdentifyInterface : public LDAPInterface
delete this;
}
- void OnResult(const LDAPResult &r) anope_override
+ void OnResult(const LDAPResult &r) override
{
if (!ii->lprov)
return;
@@ -99,20 +111,20 @@ class IdentifyInterface : public LDAPInterface
}
else
{
- NickAlias *na = NickAlias::Find(ii->req->GetAccount());
+ NickServ::Nick *na = NickServ::FindNick(ii->req->GetAccount());
if (na == NULL)
{
- na = new NickAlias(ii->req->GetAccount(), new NickCore(ii->req->GetAccount()));
- na->last_realname = ii->user ? ii->user->realname : ii->req->GetAccount();
- FOREACH_MOD(OnNickRegister, (ii->user, na, ii->req->GetPassword()));
- BotInfo *NickServ = Config->GetClient("NickServ");
+ na = new NickServ::Nick(ii->req->GetAccount(), new NickServ::Account(ii->req->GetAccount()));
+ na->SetLastRealname(ii->user ? ii->user->realname : ii->req->GetAccount());
+ NickServ::EventManager::Get()->Dispatch(&NickServ::Event::NickRegister::OnNickRegister, ii->user, na, ii->req->GetPassword());;
+ ServiceBot *NickServ = Config->GetClient("NickServ");
if (ii->user && NickServ)
- ii->user->SendMessage(NickServ, _("Your account \002%s\002 has been successfully created."), na->nick.c_str());
+ ii->user->SendMessage(NickServ, _("Your account \002%s\002 has been successfully created."), na->GetNick().c_str());
}
// encrypt and store the password in the nickcore
- Anope::Encrypt(ii->req->GetPassword(), na->nc->pass);
+ Anope::Encrypt(ii->req->GetPassword(), na->GetAccount()->pass);
- na->nc->Extend<Anope::string>("m_ldap_authentication_dn", ii->dn);
+ na->GetAccount()->Extend<Anope::string>("m_ldap_authentication_dn", ii->dn);
ii->req->Success(me);
}
break;
@@ -122,7 +134,7 @@ class IdentifyInterface : public LDAPInterface
}
}
- void OnError(const LDAPResult &r) anope_override
+ void OnError(const LDAPResult &r) override
{
}
};
@@ -139,7 +151,7 @@ class OnIdentifyInterface : public LDAPInterface
delete this;
}
- void OnResult(const LDAPResult &r) anope_override
+ void OnResult(const LDAPResult &r) override
{
User *u = User::Find(uid);
@@ -151,13 +163,13 @@ class OnIdentifyInterface : public LDAPInterface
const LDAPAttributes &attr = r.get(0);
Anope::string email = attr.get(email_attribute);
- if (!email.equals_ci(u->Account()->email))
+ if (!email.equals_ci(u->Account()->GetEmail()))
{
- u->Account()->email = email;
- BotInfo *NickServ = Config->GetClient("NickServ");
+ u->Account()->GetEmail() = email;
+ ServiceBot *NickServ = Config->GetClient("NickServ");
if (NickServ)
u->SendMessage(NickServ, _("Your email has been updated to \002%s\002"), email.c_str());
- Log(this->owner) << "Updated email address for " << u->nick << " (" << u->Account()->display << ") to " << email;
+ Log(this->owner) << "Updated email address for " << u->nick << " (" << u->Account()->GetDisplay() << ") to " << email;
}
}
catch (const LDAPException &ex)
@@ -166,7 +178,7 @@ class OnIdentifyInterface : public LDAPInterface
}
}
- void OnError(const LDAPResult &r) anope_override
+ void OnError(const LDAPResult &r) override
{
Log(this->owner) << r.error;
}
@@ -177,18 +189,22 @@ class OnRegisterInterface : public LDAPInterface
public:
OnRegisterInterface(Module *m) : LDAPInterface(m) { }
- void OnResult(const LDAPResult &r) anope_override
+ void OnResult(const LDAPResult &r) override
{
Log(this->owner) << "Successfully added newly created account to LDAP";
}
- void OnError(const LDAPResult &r) anope_override
+ void OnError(const LDAPResult &r) override
{
Log(this->owner) << "Error adding newly created account to LDAP: " << r.getError();
}
};
class ModuleLDAPAuthentication : public Module
+ , public EventHook<Event::PreCommand>
+ , public EventHook<Event::CheckAuthentication>
+ , public EventHook<Event::NickIdentify>
+ , public EventHook<NickServ::Event::NickRegister>
{
ServiceReference<LDAPProvider> ldap;
OnRegisterInterface orinterface;
@@ -198,38 +214,39 @@ class ModuleLDAPAuthentication : public Module
Anope::string password_attribute;
Anope::string disable_register_reason;
Anope::string disable_email_reason;
+
public:
- ModuleLDAPAuthentication(const Anope::string &modname, const Anope::string &creator) :
- Module(modname, creator, EXTRA | VENDOR), ldap("LDAPProvider", "ldap/main"), orinterface(this),
- dn(this, "m_ldap_authentication_dn")
+ ModuleLDAPAuthentication(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, EXTRA | VENDOR)
+ , EventHook<Event::PreCommand>("OnPreCommand", EventHook<Event::PreCommand>::Priority::FIRST)
+ , EventHook<Event::CheckAuthentication>("OnCheckAuthentication", EventHook<Event::CheckAuthentication>::Priority::FIRST)
+ , EventHook<Event::NickIdentify>("OnNickIdentify", EventHook<Event::NickIdentify>::Priority::FIRST)
+ , EventHook<NickServ::Event::NickRegister>("OnNickRegister", EventHook<NickServ::Event::NickRegister>::Priority::FIRST)
+ , ldap("LDAPProvider", "ldap/main")
+ , orinterface(this)
+ , dn(this, "m_ldap_authentication_dn")
{
me = this;
}
- void Prioritize() anope_override
- {
- ModuleManager::SetPriority(this, PRIORITY_FIRST);
- }
-
- void OnReload(Configuration::Conf *config) anope_override
+ void OnReload(Configuration::Conf *config) override
{
Configuration::Block *conf = Config->GetModule(this);
- basedn = conf->Get<const Anope::string>("basedn");
- search_filter = conf->Get<const Anope::string>("search_filter");
- object_class = conf->Get<const Anope::string>("object_class");
- username_attribute = conf->Get<const Anope::string>("username_attribute");
- this->password_attribute = conf->Get<const Anope::string>("password_attribute");
- email_attribute = conf->Get<const Anope::string>("email_attribute");
- this->disable_register_reason = conf->Get<const Anope::string>("disable_register_reason");
- this->disable_email_reason = conf->Get<const Anope::string>("disable_email_reason");
+ basedn = conf->Get<Anope::string>("basedn");
+ search_filter = conf->Get<Anope::string>("search_filter");
+ object_class = conf->Get<Anope::string>("object_class");
+ username_attribute = conf->Get<Anope::string>("username_attribute");
+ this->password_attribute = conf->Get<Anope::string>("password_attribute");
+ email_attribute = conf->Get<Anope::string>("email_attribute");
+ this->disable_register_reason = conf->Get<Anope::string>("disable_register_reason");
+ this->disable_email_reason = conf->Get<Anope::string>("disable_email_reason");
if (!email_attribute.empty())
/* Don't complain to users about how they need to update their email, we will do it for them */
config->GetModule("nickserv")->Set("forceemail", "false");
}
- EventReturn OnPreCommand(CommandSource &source, Command *command, std::vector<Anope::string> &params) anope_override
+ EventReturn OnPreCommand(CommandSource &source, Command *command, std::vector<Anope::string> &params) override
{
if (!this->disable_register_reason.empty())
{
@@ -249,7 +266,7 @@ class ModuleLDAPAuthentication : public Module
return EVENT_CONTINUE;
}
- void OnCheckAuthentication(User *u, IdentifyRequest *req) anope_override
+ void OnCheckAuthentication(User *u, IdentifyRequest *req) override
{
if (!this->ldap)
return;
@@ -258,7 +275,7 @@ class ModuleLDAPAuthentication : public Module
this->ldap->BindAsAdmin(new IdentifyInterface(this, ii));
}
- void OnNickIdentify(User *u) anope_override
+ void OnNickIdentify(User *u) override
{
if (email_attribute.empty() || !this->ldap)
return;
@@ -270,7 +287,7 @@ class ModuleLDAPAuthentication : public Module
this->ldap->Search(new OnIdentifyInterface(this, u->GetUID()), *d, "(" + email_attribute + "=*)");
}
- void OnNickRegister(User *, NickAlias *na, const Anope::string &pass) anope_override
+ void OnNickRegister(User *, NickServ::Nick *na, const Anope::string &pass) override
{
if (!this->disable_register_reason.empty() || !this->ldap)
return;
@@ -285,18 +302,18 @@ class ModuleLDAPAuthentication : public Module
attributes[0].values.push_back(object_class);
attributes[1].name = username_attribute;
- attributes[1].values.push_back(na->nick);
+ attributes[1].values.push_back(na->GetNick());
- if (!na->nc->email.empty())
+ if (!na->GetAccount()->GetEmail().empty())
{
attributes[2].name = email_attribute;
- attributes[2].values.push_back(na->nc->email);
+ attributes[2].values.push_back(na->GetAccount()->GetEmail());
}
attributes[3].name = this->password_attribute;
attributes[3].values.push_back(pass);
- Anope::string new_dn = username_attribute + "=" + na->nick + "," + basedn;
+ Anope::string new_dn = username_attribute + "=" + na->GetNick() + "," + basedn;
this->ldap->Add(&this->orinterface, new_dn, attributes);
}
};
diff --git a/modules/extra/m_ldap_oper.cpp b/modules/extra/ldap_oper.cpp
index 23ebf1a6b..5157a6f10 100644
--- a/modules/extra/m_ldap_oper.cpp
+++ b/modules/extra/ldap_oper.cpp
@@ -1,9 +1,20 @@
/*
+ * Anope IRC Services
*
- * (C) 2011-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2011-2016 Anope Team <team@anope.org>
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "module.h"
@@ -21,12 +32,12 @@ class IdentifyInterface : public LDAPInterface
{
}
- void OnResult(const LDAPResult &r) anope_override
+ void OnResult(const LDAPResult &r) override
{
if (!u || !u->Account())
return;
- NickCore *nc = u->Account();
+ NickServ::Account *nc = u->Account();
try
{
@@ -46,7 +57,8 @@ class IdentifyInterface : public LDAPInterface
o = new Oper(u->nick, ot);
my_opers.insert(o);
nc->o = o;
- Log(this->owner) << "Tied " << u->nick << " (" << nc->display << ") to opertype " << ot->GetName();
+
+ Log(this->owner) << "Tied " << u->nick << " (" << nc->GetDisplay() << ") to opertype " << ot->GetName();
}
}
catch (const LDAPException &ex)
@@ -60,12 +72,12 @@ class IdentifyInterface : public LDAPInterface
}
nc->o = NULL;
- Log(this->owner) << "Removed services operator from " << u->nick << " (" << nc->display << ")";
+ Log(this->owner) << "Removed services operator from " << u->nick << " (" << nc->GetDisplay() << ")";
}
}
}
- void OnError(const LDAPResult &r) anope_override
+ void OnError(const LDAPResult &r) override
{
}
@@ -76,6 +88,8 @@ class IdentifyInterface : public LDAPInterface
};
class LDAPOper : public Module
+ , public EventHook<Event::NickIdentify>
+ , public EventHook<Event::DelCore>
{
ServiceReference<LDAPProvider> ldap;
@@ -84,28 +98,29 @@ class LDAPOper : public Module
Anope::string basedn;
Anope::string filter;
public:
- LDAPOper(const Anope::string &modname, const Anope::string &creator) :
- Module(modname, creator, EXTRA | VENDOR), ldap("LDAPProvider", "ldap/main")
+ LDAPOper(const Anope::string &modname, const Anope::string &creator)
+ : Module(modname, creator, EXTRA | VENDOR)
+ , ldap("LDAPProvider", "ldap/main")
{
}
- void OnReload(Configuration::Conf *conf) anope_override
+ void OnReload(Configuration::Conf *conf) override
{
Configuration::Block *config = Config->GetModule(this);
- this->binddn = config->Get<const Anope::string>("binddn");
- this->password = config->Get<const Anope::string>("password");
- this->basedn = config->Get<const Anope::string>("basedn");
- this->filter = config->Get<const Anope::string>("filter");
- opertype_attribute = config->Get<const Anope::string>("opertype_attribute");
+ this->binddn = config->Get<Anope::string>("binddn");
+ this->password = config->Get<Anope::string>("password");
+ this->basedn = config->Get<Anope::string>("basedn");
+ this->filter = config->Get<Anope::string>("filter");
+ opertype_attribute = config->Get<Anope::string>("opertype_attribute");
for (std::set<Oper *>::iterator it = my_opers.begin(), it_end = my_opers.end(); it != it_end; ++it)
delete *it;
my_opers.clear();
}
- void OnNickIdentify(User *u) anope_override
+ void OnNickIdentify(User *u) override
{
try
{
@@ -115,8 +130,8 @@ class LDAPOper : public Module
throw LDAPException("Could not search LDAP for opertype settings, invalid configuration.");
if (!this->binddn.empty())
- this->ldap->Bind(NULL, this->binddn.replace_all_cs("%a", u->Account()->display), this->password.c_str());
- this->ldap->Search(new IdentifyInterface(this, u), this->basedn, this->filter.replace_all_cs("%a", u->Account()->display));
+ this->ldap->Bind(NULL, this->binddn.replace_all_cs("%a", u->Account()->GetDisplay()), this->password.c_str());
+ this->ldap->Search(new IdentifyInterface(this, u), this->basedn, this->filter.replace_all_cs("%a", u->Account()->GetDisplay()));
}
catch (const LDAPException &ex)
{
@@ -124,7 +139,7 @@ class LDAPOper : public Module
}
}
- void OnDelCore(NickCore *nc) anope_override
+ void OnDelCore(NickServ::Account *nc) override
{
if (nc->o != NULL && my_opers.count(nc->o) > 0)
{
diff --git a/modules/extra/m_regex_pcre.cpp b/modules/extra/m_regex_pcre.cpp
deleted file mode 100644
index fa804c14e..000000000
--- a/modules/extra/m_regex_pcre.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- *
- * (C) 2012-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- */
-
-/* RequiredLibraries: pcre */
-/* RequiredWindowsLibraries: libpcre */
-
-#include "module.h"
-#include <pcre.h>
-
-class PCRERegex : public Regex
-{
- pcre *regex;
-
- public:
- PCRERegex(const Anope::string &expr) : Regex(expr)
- {
- const char *error;
- int erroffset;
- this->regex = pcre_compile(expr.c_str(), PCRE_CASELESS, &error, &erroffset, NULL);
- if (!this->regex)
- throw RegexException("Error in regex " + expr + " at offset " + stringify(erroffset) + ": " + error);
- }
-
- ~PCRERegex()
- {
- pcre_free(this->regex);
- }
-
- bool Matches(const Anope::string &str)
- {
- return pcre_exec(this->regex, NULL, str.c_str(), str.length(), 0, 0, NULL, 0) > -1;
- }
-};
-
-class PCRERegexProvider : public RegexProvider
-{
- public:
- PCRERegexProvider(Module *creator) : RegexProvider(creator, "regex/pcre") { }
-
- Regex *Compile(const Anope::string &expression) anope_override
- {
- return new PCRERegex(expression);
- }
-};
-
-class ModuleRegexPCRE : public Module
-{
- PCRERegexProvider pcre_regex_provider;
-
- public:
- ModuleRegexPCRE(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, EXTRA | VENDOR),
- pcre_regex_provider(this)
- {
- this->SetPermanent(true);
- }
-
- ~ModuleRegexPCRE()
- {
- for (std::list<XLineManager *>::iterator it = XLineManager::XLineManagers.begin(); it != XLineManager::XLineManagers.end(); ++it)
- {
- XLineManager *xlm = *it;
- const std::vector<XLine *> &xlines = xlm->GetList();
-
- for (unsigned int i = 0; i < xlines.size(); ++i)
- {
- XLine *x = xlines[i];
-
- if (x->regex && dynamic_cast<PCRERegex *>(x->regex))
- {
- delete x->regex;
- x->regex = NULL;
- }
- }
- }
- }
-};
-
-MODULE_INIT(ModuleRegexPCRE)
diff --git a/modules/extra/m_regex_posix.cpp b/modules/extra/m_regex_posix.cpp
deleted file mode 100644
index 2486ddd70..000000000
--- a/modules/extra/m_regex_posix.cpp
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- *
- * (C) 2012-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- */
-
-#include "module.h"
-#include <sys/types.h>
-#include <regex.h>
-
-class POSIXRegex : public Regex
-{
- regex_t regbuf;
-
- public:
- POSIXRegex(const Anope::string &expr) : Regex(expr)
- {
- int err = regcomp(&this->regbuf, expr.c_str(), REG_EXTENDED | REG_NOSUB);
- if (err)
- {
- char buf[BUFSIZE];
- regerror(err, &this->regbuf, buf, sizeof(buf));
- regfree(&this->regbuf);
- throw RegexException("Error in regex " + expr + ": " + buf);
- }
- }
-
- ~POSIXRegex()
- {
- regfree(&this->regbuf);
- }
-
- bool Matches(const Anope::string &str)
- {
- return regexec(&this->regbuf, str.c_str(), 0, NULL, 0) == 0;
- }
-};
-
-class POSIXRegexProvider : public RegexProvider
-{
- public:
- POSIXRegexProvider(Module *creator) : RegexProvider(creator, "regex/posix") { }
-
- Regex *Compile(const Anope::string &expression) anope_override
- {
- return new POSIXRegex(expression);
- }
-};
-
-class ModuleRegexPOSIX : public Module
-{
- POSIXRegexProvider posix_regex_provider;
-
- public:
- ModuleRegexPOSIX(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, EXTRA | VENDOR),
- posix_regex_provider(this)
- {
- this->SetPermanent(true);
- }
-
- ~ModuleRegexPOSIX()
- {
- for (std::list<XLineManager *>::iterator it = XLineManager::XLineManagers.begin(); it != XLineManager::XLineManagers.end(); ++it)
- {
- XLineManager *xlm = *it;
- const std::vector<XLine *> &xlines = xlm->GetList();
-
- for (unsigned int i = 0; i < xlines.size(); ++i)
- {
- XLine *x = xlines[i];
-
- if (x->regex && dynamic_cast<POSIXRegex *>(x->regex))
- {
- delete x->regex;
- x->regex = NULL;
- }
- }
- }
- }
-};
-
-MODULE_INIT(ModuleRegexPOSIX)
diff --git a/modules/extra/m_regex_tre.cpp b/modules/extra/m_regex_tre.cpp
deleted file mode 100644
index 66411280c..000000000
--- a/modules/extra/m_regex_tre.cpp
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- *
- * (C) 2012-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- */
-
-/* RequiredLibraries: tre */
-
-#include "module.h"
-#include <tre/regex.h>
-
-class TRERegex : public Regex
-{
- regex_t regbuf;
-
- public:
- TRERegex(const Anope::string &expr) : Regex(expr)
- {
- int err = regcomp(&this->regbuf, expr.c_str(), REG_EXTENDED | REG_NOSUB);
- if (err)
- {
- char buf[BUFSIZE];
- regerror(err, &this->regbuf, buf, sizeof(buf));
- regfree(&this->regbuf);
- throw RegexException("Error in regex " + expr + ": " + buf);
- }
- }
-
- ~TRERegex()
- {
- regfree(&this->regbuf);
- }
-
- bool Matches(const Anope::string &str)
- {
- return regexec(&this->regbuf, str.c_str(), 0, NULL, 0) == 0;
- }
-};
-
-class TRERegexProvider : public RegexProvider
-{
- public:
- TRERegexProvider(Module *creator) : RegexProvider(creator, "regex/tre") { }
-
- Regex *Compile(const Anope::string &expression) anope_override
- {
- return new TRERegex(expression);
- }
-};
-
-class ModuleRegexTRE : public Module
-{
- TRERegexProvider tre_regex_provider;
-
- public:
- ModuleRegexTRE(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, EXTRA | VENDOR),
- tre_regex_provider(this)
- {
- this->SetPermanent(true);
- }
-
- ~ModuleRegexTRE()
- {
- for (std::list<XLineManager *>::iterator it = XLineManager::XLineManagers.begin(); it != XLineManager::XLineManagers.end(); ++it)
- {
- XLineManager *xlm = *it;
- const std::vector<XLine *> &xlines = xlm->GetList();
-
- for (unsigned int i = 0; i < xlines.size(); ++i)
- {
- XLine *x = xlines[i];
-
- if (x->regex && dynamic_cast<TRERegex *>(x->regex))
- {
- delete x->regex;
- x->regex = NULL;
- }
- }
- }
- }
-};
-
-MODULE_INIT(ModuleRegexTRE)
diff --git a/modules/extra/m_sql_oper.cpp b/modules/extra/m_sql_oper.cpp
deleted file mode 100644
index b2b2f2d1e..000000000
--- a/modules/extra/m_sql_oper.cpp
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- *
- * (C) 2012-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- */
-
-#include "module.h"
-#include "modules/sql.h"
-
-struct SQLOper : Oper
-{
- SQLOper(const Anope::string &n, OperType *o) : Oper(n, o) { }
-};
-
-class SQLOperResult : public SQL::Interface
-{
- Reference<User> user;
-
- struct SQLOperResultDeleter
- {
- SQLOperResult *res;
- SQLOperResultDeleter(SQLOperResult *r) : res(r) { }
- ~SQLOperResultDeleter() { delete res; }
- };
-
- void Deoper()
- {
- if (user->Account() && user->Account()->o && dynamic_cast<SQLOper *>(user->Account()->o))
- {
- delete user->Account()->o;
- user->Account()->o = NULL;
-
- Log(this->owner) << "m_sql_oper: Removed services operator from " << user->nick << " (" << user->Account()->display << ")";
-
- BotInfo *OperServ = Config->GetClient("OperServ");
- user->RemoveMode(OperServ, "OPER"); // Probably not set, just incase
- }
- }
-
- public:
- SQLOperResult(Module *m, User *u) : SQL::Interface(m), user(u) { }
-
- void OnResult(const SQL::Result &r) anope_override
- {
- SQLOperResultDeleter d(this);
-
- if (!user || !user->Account())
- return;
-
- if (r.Rows() == 0)
- {
- Log(LOG_DEBUG) << "m_sql_oper: Got 0 rows for " << user->nick;
- Deoper();
- return;
- }
-
- Anope::string opertype;
- try
- {
- opertype = r.Get(0, "opertype");
- }
- catch (const SQL::Exception &)
- {
- Log(this->owner) << "Expected column named \"opertype\" but one was not found";
- return;
- }
-
- Log(LOG_DEBUG) << "m_sql_oper: Got result for " << user->nick << ", opertype " << opertype;
-
- Anope::string modes;
- try
- {
- modes = r.Get(0, "modes");
- }
- catch (const SQL::Exception &)
- {
- // Common case here is an exception, but this probably doesn't get this far often
- }
-
- BotInfo *OperServ = Config->GetClient("OperServ");
- if (opertype.empty())
- {
- Deoper();
- return;
- }
-
- OperType *ot = OperType::Find(opertype);
- if (ot == NULL)
- {
- Log(this->owner) << "m_sql_oper: Oper " << user->nick << " has type " << opertype << ", but this opertype does not exist?";
- return;
- }
-
- if (user->Account()->o && !dynamic_cast<SQLOper *>(user->Account()->o))
- {
- Log(this->owner) << "Oper " << user->Account()->display << " has type " << opertype << ", but is already configured as an oper of type " << user->Account()->o->ot->GetName();
- return;
- }
-
- if (!user->Account()->o || user->Account()->o->ot != ot)
- {
- Log(this->owner) << "m_sql_oper: Tieing oper " << user->nick << " to type " << opertype;
-
- delete user->Account()->o;
- user->Account()->o = new SQLOper(user->Account()->display, ot);
- }
-
- if (!user->HasMode("OPER"))
- {
- IRCD->SendOper(user);
-
- if (!modes.empty())
- user->SetModes(OperServ, "%s", modes.c_str());
- }
- }
-
- void OnError(const SQL::Result &r) anope_override
- {
- SQLOperResultDeleter d(this);
- Log(this->owner) << "m_sql_oper: Error executing query " << r.GetQuery().query << ": " << r.GetError();
- }
-};
-
-class ModuleSQLOper : public Module
-{
- Anope::string engine;
- Anope::string query;
-
- ServiceReference<SQL::Provider> SQL;
-
- public:
- ModuleSQLOper(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, EXTRA | VENDOR)
- {
- }
-
- ~ModuleSQLOper()
- {
- for (nickcore_map::const_iterator it = NickCoreList->begin(), it_end = NickCoreList->end(); it != it_end; ++it)
- {
- NickCore *nc = it->second;
-
- if (nc->o && dynamic_cast<SQLOper *>(nc->o))
- {
- delete nc->o;
- nc->o = NULL;
- }
- }
- }
-
- void OnReload(Configuration::Conf *conf) anope_override
- {
- Configuration::Block *config = conf->GetModule(this);
-
- this->engine = config->Get<const Anope::string>("engine");
- this->query = config->Get<const Anope::string>("query");
-
- this->SQL = ServiceReference<SQL::Provider>("SQL::Provider", this->engine);
- }
-
- void OnNickIdentify(User *u) anope_override
- {
- if (!this->SQL)
- {
- Log() << "Unable to find SQL engine";
- return;
- }
-
- SQL::Query q(this->query);
- q.SetValue("a", u->Account()->display);
- q.SetValue("i", u->ip.addr());
-
- this->SQL->Run(new SQLOperResult(this, u), q);
-
- Log(LOG_DEBUG) << "m_sql_oper: Checking authentication for " << u->Account()->display;
- }
-};
-
-MODULE_INIT(ModuleSQLOper)
diff --git a/modules/extra/m_sqlite.cpp b/modules/extra/m_sqlite.cpp
deleted file mode 100644
index d8b76011a..000000000
--- a/modules/extra/m_sqlite.cpp
+++ /dev/null
@@ -1,326 +0,0 @@
-/*
- *
- * (C) 2011-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- */
-
-/* RequiredLibraries: sqlite3 */
-/* RequiredWindowsLibraries: sqlite3 */
-
-#include "module.h"
-#include "modules/sql.h"
-#include <sqlite3.h>
-
-using namespace SQL;
-
-/* SQLite3 API, based from InspiRCd */
-
-/** A SQLite result
- */
-class SQLiteResult : public Result
-{
- public:
- SQLiteResult(unsigned int i, const Query &q, const Anope::string &fq) : Result(i, q, fq)
- {
- }
-
- SQLiteResult(const Query &q, const Anope::string &fq, const Anope::string &err) : Result(0, q, fq, err)
- {
- }
-
- void AddRow(const std::map<Anope::string, Anope::string> &data)
- {
- this->entries.push_back(data);
- }
-};
-
-/** A SQLite database, there can be multiple
- */
-class SQLiteService : public Provider
-{
- std::map<Anope::string, std::set<Anope::string> > active_schema;
-
- Anope::string database;
-
- sqlite3 *sql;
-
- Anope::string Escape(const Anope::string &query);
-
- public:
- SQLiteService(Module *o, const Anope::string &n, const Anope::string &d);
-
- ~SQLiteService();
-
- void Run(Interface *i, const Query &query) anope_override;
-
- Result RunQuery(const Query &query);
-
- std::vector<Query> CreateTable(const Anope::string &table, const Data &data) anope_override;
-
- Query BuildInsert(const Anope::string &table, unsigned int id, Data &data);
-
- Query GetTables(const Anope::string &prefix);
-
- Anope::string BuildQuery(const Query &q);
-
- Anope::string FromUnixtime(time_t);
-};
-
-class ModuleSQLite : public Module
-{
- /* SQL connections */
- std::map<Anope::string, SQLiteService *> SQLiteServices;
- public:
- ModuleSQLite(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, EXTRA | VENDOR)
- {
- }
-
- ~ModuleSQLite()
- {
- for (std::map<Anope::string, SQLiteService *>::iterator it = this->SQLiteServices.begin(); it != this->SQLiteServices.end(); ++it)
- delete it->second;
- SQLiteServices.clear();
- }
-
- void OnReload(Configuration::Conf *conf) anope_override
- {
- Configuration::Block *config = conf->GetModule(this);
-
- for (std::map<Anope::string, SQLiteService *>::iterator it = this->SQLiteServices.begin(); it != this->SQLiteServices.end();)
- {
- const Anope::string &cname = it->first;
- SQLiteService *s = it->second;
- int i, num;
- ++it;
-
- for (i = 0, num = config->CountBlock("sqlite"); i < num; ++i)
- if (config->GetBlock("sqlite", i)->Get<const Anope::string>("name", "sqlite/main") == cname)
- break;
-
- if (i == num)
- {
- Log(LOG_NORMAL, "sqlite") << "SQLite: Removing server connection " << cname;
-
- delete s;
- this->SQLiteServices.erase(cname);
- }
- }
-
- for (int i = 0; i < config->CountBlock("sqlite"); ++i)
- {
- Configuration::Block *block = config->GetBlock("sqlite", i);
- Anope::string connname = block->Get<const Anope::string>("name", "sqlite/main");
-
- if (this->SQLiteServices.find(connname) == this->SQLiteServices.end())
- {
- Anope::string database = Anope::DataDir + "/" + block->Get<const Anope::string>("database", "anope");
-
- try
- {
- SQLiteService *ss = new SQLiteService(this, connname, database);
- this->SQLiteServices[connname] = ss;
-
- Log(LOG_NORMAL, "sqlite") << "SQLite: Successfully added database " << database;
- }
- catch (const SQL::Exception &ex)
- {
- Log(LOG_NORMAL, "sqlite") << "SQLite: " << ex.GetReason();
- }
- }
- }
- }
-};
-
-SQLiteService::SQLiteService(Module *o, const Anope::string &n, const Anope::string &d)
-: Provider(o, n), database(d), sql(NULL)
-{
- int db = sqlite3_open_v2(database.c_str(), &this->sql, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0);
- if (db != SQLITE_OK)
- throw SQL::Exception("Unable to open SQLite database " + database + ": " + sqlite3_errmsg(this->sql));
-}
-
-SQLiteService::~SQLiteService()
-{
- sqlite3_interrupt(this->sql);
- sqlite3_close(this->sql);
-}
-
-void SQLiteService::Run(Interface *i, const Query &query)
-{
- Result res = this->RunQuery(query);
- if (!res.GetError().empty())
- i->OnError(res);
- else
- i->OnResult(res);
-}
-
-Result SQLiteService::RunQuery(const Query &query)
-{
- Anope::string real_query = this->BuildQuery(query);
- sqlite3_stmt *stmt;
- int err = sqlite3_prepare_v2(this->sql, real_query.c_str(), real_query.length(), &stmt, NULL);
- if (err != SQLITE_OK)
- return SQLiteResult(query, real_query, sqlite3_errmsg(this->sql));
-
- std::vector<Anope::string> columns;
- int cols = sqlite3_column_count(stmt);
- columns.resize(cols);
- for (int i = 0; i < cols; ++i)
- columns[i] = sqlite3_column_name(stmt, i);
-
- SQLiteResult result(0, query, real_query);
-
- while ((err = sqlite3_step(stmt)) == SQLITE_ROW)
- {
- std::map<Anope::string, Anope::string> items;
- for (int i = 0; i < cols; ++i)
- {
- const char *data = reinterpret_cast<const char *>(sqlite3_column_text(stmt, i));
- if (data && *data)
- items[columns[i]] = data;
- }
- result.AddRow(items);
- }
-
- result.id = sqlite3_last_insert_rowid(this->sql);
-
- sqlite3_finalize(stmt);
-
- if (err != SQLITE_DONE)
- return SQLiteResult(query, real_query, sqlite3_errmsg(this->sql));
-
- return result;
-}
-
-std::vector<Query> SQLiteService::CreateTable(const Anope::string &table, const Data &data)
-{
- std::vector<Query> queries;
- std::set<Anope::string> &known_cols = this->active_schema[table];
-
- if (known_cols.empty())
- {
- Log(LOG_DEBUG) << "m_sqlite: Fetching columns for " << table;
-
- Result columns = this->RunQuery("PRAGMA table_info(" + table + ")");
- for (int i = 0; i < columns.Rows(); ++i)
- {
- const Anope::string &column = columns.Get(i, "name");
-
- Log(LOG_DEBUG) << "m_sqlite: Column #" << i << " for " << table << ": " << column;
- known_cols.insert(column);
- }
- }
-
- if (known_cols.empty())
- {
- Anope::string query_text = "CREATE TABLE `" + table + "` (`id` INTEGER PRIMARY KEY, `timestamp` timestamp DEFAULT CURRENT_TIMESTAMP";
-
- for (Data::Map::const_iterator it = data.data.begin(), it_end = data.data.end(); it != it_end; ++it)
- {
- known_cols.insert(it->first);
-
- query_text += ", `" + it->first + "` ";
- if (data.GetType(it->first) == Serialize::Data::DT_INT)
- query_text += "int(11)";
- else
- query_text += "text";
- }
-
- query_text += ")";
-
- queries.push_back(query_text);
-
- query_text = "CREATE UNIQUE INDEX `" + table + "_id_idx` ON `" + table + "` (`id`)";
- queries.push_back(query_text);
-
- query_text = "CREATE INDEX `" + table + "_timestamp_idx` ON `" + table + "` (`timestamp`)";
- queries.push_back(query_text);
-
- query_text = "CREATE TRIGGER `" + table + "_trigger` AFTER UPDATE ON `" + table + "` FOR EACH ROW BEGIN UPDATE `" + table + "` SET `timestamp` = CURRENT_TIMESTAMP WHERE `id` = `old.id`; end;";
- queries.push_back(query_text);
- }
- else
- for (Data::Map::const_iterator it = data.data.begin(), it_end = data.data.end(); it != it_end; ++it)
- {
- if (known_cols.count(it->first) > 0)
- continue;
-
- known_cols.insert(it->first);
-
- Anope::string query_text = "ALTER TABLE `" + table + "` ADD `" + it->first + "` ";
- if (data.GetType(it->first) == Serialize::Data::DT_INT)
- query_text += "int(11)";
- else
- query_text += "text";
-
- queries.push_back(query_text);
- }
-
- return queries;
-}
-
-Query SQLiteService::BuildInsert(const Anope::string &table, unsigned int id, Data &data)
-{
- /* Empty columns not present in the data set */
- const std::set<Anope::string> &known_cols = this->active_schema[table];
- for (std::set<Anope::string>::iterator it = known_cols.begin(), it_end = known_cols.end(); it != it_end; ++it)
- if (*it != "id" && *it != "timestamp" && data.data.count(*it) == 0)
- data[*it] << "";
-
- Anope::string query_text = "REPLACE INTO `" + table + "` (";
- if (id > 0)
- query_text += "`id`,";
- for (Data::Map::const_iterator it = data.data.begin(), it_end = data.data.end(); it != it_end; ++it)
- query_text += "`" + it->first + "`,";
- query_text.erase(query_text.length() - 1);
- query_text += ") VALUES (";
- if (id > 0)
- query_text += stringify(id) + ",";
- for (Data::Map::const_iterator it = data.data.begin(), it_end = data.data.end(); it != it_end; ++it)
- query_text += "@" + it->first + "@,";
- query_text.erase(query_text.length() - 1);
- query_text += ")";
-
- Query query(query_text);
- for (Data::Map::const_iterator it = data.data.begin(), it_end = data.data.end(); it != it_end; ++it)
- {
- Anope::string buf;
- *it->second >> buf;
- query.SetValue(it->first, buf);
- }
-
- return query;
-}
-
-Query SQLiteService::GetTables(const Anope::string &prefix)
-{
- return Query("SELECT name FROM sqlite_master WHERE type='table' AND name LIKE '" + prefix + "%';");
-}
-
-Anope::string SQLiteService::Escape(const Anope::string &query)
-{
- char *e = sqlite3_mprintf("%q", query.c_str());
- Anope::string buffer = e;
- sqlite3_free(e);
- return buffer;
-}
-
-Anope::string SQLiteService::BuildQuery(const Query &q)
-{
- Anope::string real_query = q.query;
-
- for (std::map<Anope::string, QueryData>::const_iterator it = q.parameters.begin(), it_end = q.parameters.end(); it != it_end; ++it)
- real_query = real_query.replace_all_cs("@" + it->first + "@", (it->second.escape ? ("'" + this->Escape(it->second.data) + "'") : it->second.data));
-
- return real_query;
-}
-
-Anope::string SQLiteService::FromUnixtime(time_t t)
-{
- return "datetime('" + stringify(t) + "', 'unixepoch')";
-}
-
-MODULE_INIT(ModuleSQLite)
-
diff --git a/modules/extra/m_mysql.cpp b/modules/extra/mysql.cpp
index 06cce3143..b0aec1f6d 100644
--- a/modules/extra/m_mysql.cpp
+++ b/modules/extra/mysql.cpp
@@ -1,9 +1,20 @@
/*
+ * Anope IRC Services
*
- * (C) 2010-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2010-2016 Anope Team <team@anope.org>
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
/* RequiredLibraries: mysqlclient */
@@ -66,31 +77,35 @@ class MySQLResult : public Result
public:
MySQLResult(unsigned int i, const Query &q, const Anope::string &fq, MYSQL_RES *r) : Result(i, q, fq), res(r)
{
- unsigned num_fields = res ? mysql_num_fields(res) : 0;
+ if (!res)
+ return;
+
+ unsigned num_fields = mysql_num_fields(res);
+ MYSQL_FIELD *fields = mysql_fetch_fields(res);
/* It is not thread safe to log anything here using Log(this->owner) now :( */
- if (!num_fields)
+ if (!num_fields || !fields)
return;
+ for (unsigned field_count = 0; field_count < num_fields; ++field_count)
+ columns.push_back(fields[field_count].name ? fields[field_count].name : "");
+
for (MYSQL_ROW row; (row = mysql_fetch_row(res));)
{
- MYSQL_FIELD *fields = mysql_fetch_fields(res);
+ std::vector<Value> values;
- if (fields)
+ for (unsigned field_count = 0; field_count < num_fields; ++field_count)
{
- std::map<Anope::string, Anope::string> items;
+ const char *data = row[field_count];
- for (unsigned field_count = 0; field_count < num_fields; ++field_count)
- {
- Anope::string column = (fields[field_count].name ? fields[field_count].name : "");
- Anope::string data = (row[field_count] ? row[field_count] : "");
-
- items[column] = data;
- }
-
- this->entries.push_back(items);
+ Value v;
+ v.null = !data;
+ v.value = data ? data : "";
+ values.push_back(v);
}
+
+ this->values.push_back(values);
}
}
@@ -109,7 +124,7 @@ class MySQLResult : public Result
*/
class MySQLService : public Provider
{
- std::map<Anope::string, std::set<Anope::string> > active_schema;
+ std::map<Anope::string, std::set<Anope::string> > active_schema, indexes;
Anope::string database;
Anope::string server;
@@ -135,23 +150,28 @@ class MySQLService : public Provider
~MySQLService();
- void Run(Interface *i, const Query &query) anope_override;
+ void Run(Interface *i, const Query &query) override;
- Result RunQuery(const Query &query) anope_override;
+ Result RunQuery(const Query &query) override;
- std::vector<Query> CreateTable(const Anope::string &table, const Data &data) anope_override;
+ std::vector<Query> InitSchema(const Anope::string &prefix) override;
+ std::vector<Query> Replace(const Anope::string &table, const Query &, const std::set<Anope::string> &) override;
+ std::vector<Query> CreateTable(const Anope::string &prefix, const Anope::string &table) override;
+ std::vector<Query> AlterTable(const Anope::string &, const Anope::string &table, const Anope::string &field, bool) override;
+ std::vector<Query> CreateIndex(const Anope::string &table, const Anope::string &field) override;
- Query BuildInsert(const Anope::string &table, unsigned int id, Data &data) anope_override;
+ Query BeginTransaction() override;
+ Query Commit() override;
- Query GetTables(const Anope::string &prefix) anope_override;
+ Serialize::ID GetID(const Anope::string &) override;
+
+ Query GetTables(const Anope::string &prefix) override;
void Connect();
bool CheckConnection();
Anope::string BuildQuery(const Query &q);
-
- Anope::string FromUnixtime(time_t);
};
/** The SQL thread used to execute queries
@@ -161,12 +181,14 @@ class DispatcherThread : public Thread, public Condition
public:
DispatcherThread() : Thread() { }
- void Run() anope_override;
+ void Run() override;
};
class ModuleSQL;
static ModuleSQL *me;
-class ModuleSQL : public Module, public Pipe
+class ModuleSQL : public Module
+ , public Pipe
+ , public EventHook<Event::ModuleUnload>
{
/* SQL connections */
std::map<Anope::string, MySQLService *> MySQLServices;
@@ -179,6 +201,7 @@ class ModuleSQL : public Module, public Pipe
DispatcherThread *DThread;
ModuleSQL(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, EXTRA | VENDOR)
+ , EventHook<Event::ModuleUnload>(this)
{
me = this;
@@ -199,7 +222,7 @@ class ModuleSQL : public Module, public Pipe
delete DThread;
}
- void OnReload(Configuration::Conf *conf) anope_override
+ void OnReload(Configuration::Conf *conf) override
{
Configuration::Block *config = conf->GetModule(this);
@@ -212,7 +235,7 @@ class ModuleSQL : public Module, public Pipe
++it;
for (i = 0; i < config->CountBlock("mysql"); ++i)
- if (config->GetBlock("mysql", i)->Get<const Anope::string>("name", "mysql/main") == cname)
+ if (config->GetBlock("mysql", i)->Get<Anope::string>("name", "mysql/main") == cname)
break;
if (i == config->CountBlock("mysql"))
@@ -227,14 +250,14 @@ class ModuleSQL : public Module, public Pipe
for (int i = 0; i < config->CountBlock("mysql"); ++i)
{
Configuration::Block *block = config->GetBlock("mysql", i);
- const Anope::string &connname = block->Get<const Anope::string>("name", "mysql/main");
+ const Anope::string &connname = block->Get<Anope::string>("name", "mysql/main");
if (this->MySQLServices.find(connname) == this->MySQLServices.end())
{
- const Anope::string &database = block->Get<const Anope::string>("database", "anope");
- const Anope::string &server = block->Get<const Anope::string>("server", "127.0.0.1");
- const Anope::string &user = block->Get<const Anope::string>("username", "anope");
- const Anope::string &password = block->Get<const Anope::string>("password");
+ const Anope::string &database = block->Get<Anope::string>("database", "anope");
+ const Anope::string &server = block->Get<Anope::string>("server", "127.0.0.1");
+ const Anope::string &user = block->Get<Anope::string>("username", "anope");
+ const Anope::string &password = block->Get<Anope::string>("password");
int port = block->Get<int>("port", "3306");
try
@@ -252,7 +275,7 @@ class ModuleSQL : public Module, public Pipe
}
}
- void OnModuleUnload(User *, Module *m) anope_override
+ void OnModuleUnload(User *, Module *m) override
{
this->DThread->Lock();
@@ -277,7 +300,7 @@ class ModuleSQL : public Module, public Pipe
this->OnNotify();
}
- void OnNotify() anope_override
+ void OnNotify() override
{
this->DThread->Lock();
std::deque<QueryResult> finishedRequests = this->FinishedRequests;
@@ -366,90 +389,146 @@ Result MySQLService::RunQuery(const Query &query)
}
}
-std::vector<Query> MySQLService::CreateTable(const Anope::string &table, const Data &data)
+std::vector<Query> MySQLService::InitSchema(const Anope::string &prefix)
+{
+ std::vector<Query> queries;
+
+ Query t = "CREATE TABLE IF NOT EXISTS `" + prefix + "id` ("
+ "`id` bigint(20) NOT NULL"
+ ") ENGINE=InnoDB";
+ queries.push_back(t);
+
+ t = "CREATE TABLE IF NOT EXISTS `" + prefix + "objects` (`id` bigint(20) NOT NULL PRIMARY KEY, `type` varchar(256)) ENGINE=InnoDB";
+ queries.push_back(t);
+
+ t = "CREATE TABLE IF NOT EXISTS `" + prefix + "edges` ("
+ "`id` bigint(20) NOT NULL,"
+ "`field` varchar(64) NOT NULL,"
+ "`other_id` bigint(20) NOT NULL,"
+ "PRIMARY KEY (`id`, `field`),"
+ "KEY `other` (`other_id`),"
+ "CONSTRAINT `edges_id_fk` FOREIGN KEY (`id`) REFERENCES `" + prefix + "objects` (`id`),"
+ "CONSTRAINT `edges_other_id_fk` FOREIGN KEY (`other_id`) REFERENCES `" + prefix + "objects` (`id`)"
+ ") ENGINE=InnoDB";
+ queries.push_back(t);
+
+ return queries;
+}
+
+std::vector<Query> MySQLService::Replace(const Anope::string &table, const Query &q, const std::set<Anope::string> &keys)
{
std::vector<Query> queries;
- std::set<Anope::string> &known_cols = this->active_schema[table];
- if (known_cols.empty())
+ Anope::string query_text = "INSERT INTO `" + table + "` (";
+ for (const std::pair<Anope::string, QueryData> &p : q.parameters)
+ query_text += "`" + p.first + "`,";
+ query_text.erase(query_text.length() - 1);
+ query_text += ") VALUES (";
+ for (const std::pair<Anope::string, QueryData> &p : q.parameters)
+ query_text += "@" + p.first + "@,";
+ query_text.erase(query_text.length() - 1);
+ query_text += ") ON DUPLICATE KEY UPDATE ";
+ for (const std::pair<Anope::string, QueryData> &p : q.parameters)
+ if (!keys.count(p.first))
+ query_text += "`" + p.first + "` = VALUES(`" + p.first + "`),";
+ query_text.erase(query_text.length() - 1);
+
+ Query query(query_text);
+ query.parameters = q.parameters;
+
+ queries.push_back(query);
+
+ return queries;
+}
+
+std::vector<Query> MySQLService::CreateTable(const Anope::string &prefix, const Anope::string &table)
+{
+ std::vector<Query> queries;
+
+ if (active_schema.find(prefix + table) == active_schema.end())
{
- Log(LOG_DEBUG) << "m_mysql: Fetching columns for " << table;
+ Query t = "CREATE TABLE IF NOT EXISTS `" + prefix + table + "` (`id` bigint(20) NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB";
+ queries.push_back(t);
- Result columns = this->RunQuery("SHOW COLUMNS FROM `" + table + "`");
- for (int i = 0; i < columns.Rows(); ++i)
- {
- const Anope::string &column = columns.Get(i, "Field");
+ t = "ALTER TABLE `" + prefix + table + "` "
+ "ADD CONSTRAINT `" + table + "_id_fk` FOREIGN KEY (`id`) REFERENCES `" + prefix + "objects` (`id`)";
+ queries.push_back(t);
- Log(LOG_DEBUG) << "m_mysql: Column #" << i << " for " << table << ": " << column;
- known_cols.insert(column);
- }
+ active_schema[prefix + table];
}
- if (known_cols.empty())
- {
- Anope::string query_text = "CREATE TABLE `" + table + "` (`id` int(10) unsigned NOT NULL AUTO_INCREMENT,"
- " `timestamp` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP";
- for (Data::Map::const_iterator it = data.data.begin(), it_end = data.data.end(); it != it_end; ++it)
- {
- known_cols.insert(it->first);
+ return queries;
+}
- query_text += ", `" + it->first + "` ";
- if (data.GetType(it->first) == Serialize::Data::DT_INT)
- query_text += "int(11)";
- else
- query_text += "text";
- }
- query_text += ", PRIMARY KEY (`id`), KEY `timestamp_idx` (`timestamp`))";
- queries.push_back(query_text);
+std::vector<Query> MySQLService::AlterTable(const Anope::string &prefix, const Anope::string &table, const Anope::string &field, bool object)
+{
+ std::vector<Query> queries;
+ std::set<Anope::string> &s = active_schema[prefix + table];
+
+ if (!s.count(field))
+ {
+ Query column;
+ if (!object)
+ column = "ALTER TABLE `" + prefix + table + "` ADD COLUMN `" + field + "` TINYTEXT";
+ else
+ column = "ALTER TABLE `" + prefix + table + "` "
+ "ADD COLUMN `" + field + "` bigint(20), "
+ "ADD CONSTRAINT `" + table + "_" + field + "_fk` FOREIGN KEY (`" + field + "`) REFERENCES `" + prefix + "objects` (`id`)";
+ queries.push_back(column);
+ s.insert(field);
}
- else
- for (Data::Map::const_iterator it = data.data.begin(), it_end = data.data.end(); it != it_end; ++it)
- {
- if (known_cols.count(it->first) > 0)
- continue;
- known_cols.insert(it->first);
+ return queries;
+}
- Anope::string query_text = "ALTER TABLE `" + table + "` ADD `" + it->first + "` ";
- if (data.GetType(it->first) == Serialize::Data::DT_INT)
- query_text += "int(11)";
- else
- query_text += "text";
+std::vector<Query> MySQLService::CreateIndex(const Anope::string &table, const Anope::string &field)
+{
+ std::vector<Query> queries;
- queries.push_back(query_text);
- }
+ if (indexes[table].count(field))
+ return queries;
+
+ Query t = "ALTER TABLE `" + table + "` ADD KEY `idx_" + field + "` (`" + field + "`(512))";
+ queries.push_back(t);
+
+ indexes[table].insert(field);
return queries;
}
-Query MySQLService::BuildInsert(const Anope::string &table, unsigned int id, Data &data)
+Query MySQLService::BeginTransaction()
{
- /* Empty columns not present in the data set */
- const std::set<Anope::string> &known_cols = this->active_schema[table];
- for (std::set<Anope::string>::iterator it = known_cols.begin(), it_end = known_cols.end(); it != it_end; ++it)
- if (*it != "id" && *it != "timestamp" && data.data.count(*it) == 0)
- data[*it] << "";
-
- Anope::string query_text = "INSERT INTO `" + table + "` (`id`";
- for (Data::Map::const_iterator it = data.data.begin(), it_end = data.data.end(); it != it_end; ++it)
- query_text += ",`" + it->first + "`";
- query_text += ") VALUES (" + stringify(id);
- for (Data::Map::const_iterator it = data.data.begin(), it_end = data.data.end(); it != it_end; ++it)
- query_text += ",@" + it->first + "@";
- query_text += ") ON DUPLICATE KEY UPDATE ";
- for (Data::Map::const_iterator it = data.data.begin(), it_end = data.data.end(); it != it_end; ++it)
- query_text += "`" + it->first + "`=VALUES(`" + it->first + "`),";
- query_text.erase(query_text.end() - 1);
+ return Query("START TRANSACTION WITH CONSISTENT SNAPSHOT");
+}
- Query query(query_text);
- for (Data::Map::const_iterator it = data.data.begin(), it_end = data.data.end(); it != it_end; ++it)
+Query MySQLService::Commit()
+{
+ return Query("COMMIT");
+}
+
+Serialize::ID MySQLService::GetID(const Anope::string &prefix)
+{
+ Query query("SELECT `id` FROM `" + prefix + "id` FOR UPDATE");
+ Serialize::ID id;
+
+ Result res = RunQuery(query);
+ if (res.Rows())
+ {
+ id = convertTo<Serialize::ID>(res.Get(0, "id"));
+
+ Query update_query("UPDATE `" + prefix + "id` SET `id` = `id` + 1");
+ RunQuery(update_query);
+ }
+ else
{
- Anope::string buf;
- *it->second >> buf;
- query.SetValue(it->first, buf);
+ id = 0;
+
+ Query insert_query("INSERT INTO `" + prefix + "id` (id) VALUES(@id@)");
+ insert_query.SetValue("id", 1);
+ RunQuery(insert_query);
}
-
- return query;
+
+ return id;
}
Query MySQLService::GetTables(const Anope::string &prefix)
@@ -467,9 +546,9 @@ void MySQLService::Connect()
bool connect = mysql_real_connect(this->sql, this->server.c_str(), this->user.c_str(), this->password.c_str(), this->database.c_str(), this->port, NULL, CLIENT_MULTI_RESULTS);
if (!connect)
- throw SQL::Exception("Unable to connect to MySQL service " + this->name + ": " + mysql_error(this->sql));
-
- Log(LOG_DEBUG) << "Successfully connected to MySQL service " << this->name << " at " << this->server << ":" << this->port;
+ throw SQL::Exception("Unable to connect to MySQL service " + this->GetName() + ": " + mysql_error(this->sql));
+
+ Log(LOG_DEBUG) << "Successfully connected to MySQL service " << this->GetName() << " at " << this->server << ":" << this->port;
}
@@ -502,14 +581,21 @@ Anope::string MySQLService::BuildQuery(const Query &q)
Anope::string real_query = q.query;
for (std::map<Anope::string, QueryData>::const_iterator it = q.parameters.begin(), it_end = q.parameters.end(); it != it_end; ++it)
- real_query = real_query.replace_all_cs("@" + it->first + "@", (it->second.escape ? ("'" + this->Escape(it->second.data) + "'") : it->second.data));
+ {
+ const QueryData& qd = it->second;
+ Anope::string replacement;
- return real_query;
-}
+ if (qd.null)
+ replacement = "NULL";
+ else if (!qd.escape)
+ replacement = qd.data;
+ else
+ replacement = "'" + this->Escape(qd.data) + "'";
-Anope::string MySQLService::FromUnixtime(time_t t)
-{
- return "FROM_UNIXTIME(" + stringify(t) + ")";
+ real_query = real_query.replace_all_cs("@" + it->first + "@", replacement);
+ }
+
+ return real_query;
}
void DispatcherThread::Run()
diff --git a/modules/extra/sasl_dh-aes.cpp b/modules/extra/sasl_dh-aes.cpp
new file mode 100644
index 000000000..b4920aeb4
--- /dev/null
+++ b/modules/extra/sasl_dh-aes.cpp
@@ -0,0 +1,184 @@
+/* RequiredLibraries: ssl,crypto */
+/* RequiredWindowsLibraries: ssleay32,libeay32 */
+
+#include "module.h"
+#include "modules/sasl.h"
+
+#include <openssl/bn.h>
+#include <openssl/dh.h>
+#include <openssl/aes.h>
+
+using namespace SASL;
+
+class DHAES : public Mechanism
+{
+ void Err(Session* sess, BIGNUM* key = NULL)
+ {
+ if (key)
+ BN_free(key);
+
+ sasl->Fail(sess);
+ delete sess;
+ }
+
+ public:
+ struct DHAESSession : SASL::Session
+ {
+ DH* dh;
+ DHAESSession(Mechanism *m, const Anope::string &u, DH* dh_params) : SASL::Session(m, u)
+ {
+ if (!(dh = DH_new()))
+ return;
+
+ dh->g = BN_dup(dh_params->g);
+ dh->p = BN_dup(dh_params->p);
+
+ if (!DH_generate_key(dh))
+ {
+ DH_free(dh);
+ dh = NULL;
+ }
+ }
+
+ ~DHAESSession()
+ {
+ if (dh)
+ DH_free(dh);
+ }
+ };
+
+ DH* dh_params;
+ const size_t keysize;
+ SASL::Session* CreateSession(const Anope::string &uid) override
+ {
+ return new DHAESSession(this, uid, dh_params);
+ }
+
+ DHAES(Module *o) : Mechanism(o, "DH-AES"), keysize(256 / 8)
+ {
+ if (!(dh_params = DH_new()))
+ throw ModuleException("DH_new() failed!");
+
+ if (!DH_generate_parameters_ex(dh_params, keysize * 8, 5, NULL))
+ {
+ DH_free(dh_params);
+ throw ModuleException("Could not generate DH-params");
+ }
+ }
+
+ ~DHAES()
+ {
+ DH_free(dh_params);
+ }
+
+ void ProcessMessage(SASL::Session *session, const SASL::Message &m) override
+ {
+ DHAESSession *sess = anope_dynamic_static_cast<DHAESSession *>(session);
+
+ if (!sess->dh)
+ {
+ sasl->SendMessage(sess, "D", "A");
+ delete sess;
+ return;
+ }
+
+ if (m.type == "S")
+ {
+ // Format: [ss]<p>[ss]<g>[ss]<pub_key>
+ // Where ss is a unsigned short with the size of the key
+ const BIGNUM* dhval[] = { sess->dh->p, sess->dh->g, sess->dh->pub_key };
+
+ // Find the size of our buffer - initialized at 6 because of string size data
+ size_t size = 6;
+ for (size_t i = 0; i < 3; i++)
+ size += BN_num_bytes(dhval[i]);
+
+ // Fill in the DH data
+ std::vector<unsigned char> buffer(size);
+ for (size_t i = 0, pos = 0; i < 3; i++)
+ {
+ *reinterpret_cast<uint16_t*>(&buffer[pos]) = htons(BN_num_bytes(dhval[i]));
+ pos += 2;
+ BN_bn2bin(dhval[i], &buffer[pos]);
+ pos += BN_num_bytes(dhval[i]);
+ }
+
+ Anope::string encoded;
+ Anope::B64Encode(Anope::string(buffer.begin(), buffer.end()), encoded);
+ sasl->SendMessage(sess, "C", encoded);
+ }
+ else if (m.type == "C")
+ {
+ // Make sure we have some data - actual size check is done later
+ if (m.data.length() < 10)
+ return Err(sess);
+
+ // Format: [ss]<key>[ss]<iv>[ss]<encrypted>
+ // <encrypted> = <username>\0<password>\0
+
+ Anope::string decoded;
+ Anope::B64Decode(m.data, decoded);
+
+ // Make sure we have an IV and at least one encrypted block
+ if ((decoded.length() < keysize + 2 + (AES_BLOCK_SIZE * 2)) || ((decoded.length() - keysize - 2) % AES_BLOCK_SIZE))
+ return Err(sess);
+
+ const unsigned char* data = reinterpret_cast<const unsigned char*>(decoded.data());
+
+ // Control the size of the key
+ if (ntohs(*reinterpret_cast<const uint16_t*>(&data[0])) != keysize)
+ return Err(sess);
+
+ // Convert pubkey from binary
+ size_t pos = 2;
+ BIGNUM* pubkey = BN_bin2bn(&data[pos], keysize, NULL);
+ if (!pubkey)
+ return Err(sess);
+
+ // Find shared key
+ std::vector<unsigned char> secretkey(keysize);
+ if (DH_compute_key(&secretkey[0], pubkey, sess->dh) != static_cast<int>(keysize))
+ return Err(sess, pubkey);
+
+ // Set decryption key
+ AES_KEY AESKey;
+ AES_set_decrypt_key(&secretkey[0], keysize * 8, &AESKey);
+
+ // Fetch IV
+ pos += keysize;
+ std::vector<unsigned char> IV(data + pos, data + pos + AES_BLOCK_SIZE);
+
+ // Find encrypted blocks, and decrypt
+ pos += AES_BLOCK_SIZE;
+ size_t size = decoded.length() - pos;
+ std::vector<char> decrypted(size + 2, 0);
+ AES_cbc_encrypt(&data[pos], reinterpret_cast<unsigned char*>(&decrypted[0]), size, &AESKey, &IV[0], AES_DECRYPT);
+
+ std::string username = &decrypted[0];
+ std::string password = &decrypted[username.length() + 1];
+
+ if (username.empty() || password.empty() || !IRCD->IsNickValid(username) || password.find_first_of("\r\n") != Anope::string::npos)
+ return Err(sess, pubkey);
+
+ SASL::IdentifyRequest* req = new SASL::IdentifyRequest(this->owner, m.source, username, password);
+ EventManager::Get()->Dispatch(&Event::CheckAuthentication::OnCheckAuthentication, nullptr, req);
+ req->Dispatch();
+
+ BN_free(pubkey);
+ }
+ }
+};
+
+
+class ModuleSASLDHAES : public Module
+{
+ DHAES dhaes;
+
+ public:
+ ModuleSASLDHAES(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR | EXTRA)
+ , dhaes(this)
+ {
+ }
+};
+
+MODULE_INIT(ModuleSASLDHAES)
diff --git a/modules/extra/sasl_dh-blowfish.cpp b/modules/extra/sasl_dh-blowfish.cpp
new file mode 100644
index 000000000..e3e0eb4f9
--- /dev/null
+++ b/modules/extra/sasl_dh-blowfish.cpp
@@ -0,0 +1,194 @@
+/* RequiredLibraries: ssl,crypto */
+/* RequiredWindowsLibraries: ssleay32,libeay32 */
+
+#include "module.h"
+#include "modules/sasl.h"
+
+#include <openssl/bn.h>
+#include <openssl/dh.h>
+#include <openssl/blowfish.h>
+
+using namespace SASL;
+
+class DHBS : public Mechanism
+{
+ void Err(Session* sess, BIGNUM* key = NULL)
+ {
+ if (key)
+ BN_free(key);
+
+ sasl->Fail(sess);
+ delete sess;
+ }
+
+ public:
+ struct DHBSSession : SASL::Session
+ {
+ DH* dh;
+ DHBSSession(Mechanism *m, const Anope::string &u, DH* dh_params) : SASL::Session(m, u)
+ {
+ if (!(dh = DH_new()))
+ return;
+
+ dh->g = BN_dup(dh_params->g);
+ dh->p = BN_dup(dh_params->p);
+
+ if (!DH_generate_key(dh))
+ {
+ DH_free(dh);
+ dh = NULL;
+ }
+ }
+
+ ~DHBSSession()
+ {
+ if (dh)
+ DH_free(dh);
+ }
+ };
+
+ DH* dh_params;
+ const size_t keysize;
+ SASL::Session* CreateSession(const Anope::string &uid) override
+ {
+ return new DHBSSession(this, uid, dh_params);
+ }
+
+ DHBS(Module *o) : Mechanism(o, "DH-BLOWFISH"), keysize(256 / 8)
+ {
+ if (!(dh_params = DH_new()))
+ throw ModuleException("DH_new() failed!");
+
+ if (!DH_generate_parameters_ex(dh_params, keysize * 8, 5, NULL))
+ {
+ DH_free(dh_params);
+ throw ModuleException("Could not generate DH-params");
+ }
+ }
+
+ ~DHBS()
+ {
+ DH_free(dh_params);
+ }
+
+ void ProcessMessage(SASL::Session *session, const SASL::Message &m) override
+ {
+ DHBSSession *sess = anope_dynamic_static_cast<DHBSSession *>(session);
+
+ if (!sess->dh)
+ {
+ sasl->SendMessage(sess, "D", "A");
+ delete sess;
+ return;
+ }
+
+ if (m.type == "S")
+ {
+ // Format: [ss]<p>[ss]<g>[ss]<pub_key>
+ // Where ss is a unsigned short with the size of the key
+ const BIGNUM* dhval[] = { sess->dh->p, sess->dh->g, sess->dh->pub_key };
+
+ // Find the size of our buffer - initialized at 6 because of string size data
+ size_t size = 6;
+ for (size_t i = 0; i < 3; i++)
+ size += BN_num_bytes(dhval[i]);
+
+ // Fill in the DH data
+ std::vector<unsigned char> buffer(size);
+ for (size_t i = 0, pos = 0; i < 3; i++)
+ {
+ *reinterpret_cast<uint16_t*>(&buffer[pos]) = htons(BN_num_bytes(dhval[i]));
+ pos += 2;
+ BN_bn2bin(dhval[i], &buffer[pos]);
+ pos += BN_num_bytes(dhval[i]);
+ }
+
+ Anope::string encoded;
+ Anope::B64Encode(Anope::string(buffer.begin(), buffer.end()), encoded);
+ sasl->SendMessage(sess, "C", encoded);
+ }
+ else if (m.type == "C")
+ {
+ // Make sure we have some data - actual size check is done later
+ if (m.data.length() < 10)
+ return Err(sess);
+
+ // Format: [ss]<key><username><\0><encrypted>
+
+ Anope::string decoded;
+ Anope::B64Decode(m.data, decoded);
+
+ // As we rely on the client giving us a null terminator at the right place,
+ // let's add one extra in case the client tries to crash us
+ const size_t decodedlen = decoded.length();
+ decoded.push_back('\0');
+
+ // Make sure we have enough data for at least the key, a one letter username, and a block of data
+ if (decodedlen < keysize + 2 + 2 + 8)
+ return Err(sess);
+
+ const unsigned char* data = reinterpret_cast<const unsigned char*>(decoded.data());
+
+ // Control the size of the key
+ if (ntohs(*reinterpret_cast<const uint16_t*>(&data[0])) != keysize)
+ return Err(sess);
+
+ // Convert pubkey from binary
+ size_t pos = 2;
+ BIGNUM* pubkey = BN_bin2bn(&data[pos], keysize, NULL);
+ if (!pubkey)
+ return Err(sess);
+
+ // Find shared key
+ std::vector<unsigned char> secretkey(DH_size(sess->dh) + 1, 0);
+ if (DH_compute_key(&secretkey[0], pubkey, sess->dh) != static_cast<int>(keysize))
+ return Err(sess, pubkey);
+
+ // Set decryption key
+ BF_KEY BFKey;
+ BF_set_key(&BFKey, keysize, &secretkey[0]);
+
+ pos += keysize;
+ const Anope::string username = reinterpret_cast<const char*>(&data[pos]);
+ // Check that the username is valid, and that we have at least one block of data
+ // 2 + 1 + 8 = uint16_t size for keylen, \0 for username, 8 for one block of data
+ if (username.empty() || username.length() + keysize + 2 + 1 + 8 > decodedlen || !IRCD->IsNickValid(username))
+ return Err(sess, pubkey);
+
+ pos += username.length() + 1;
+ size_t size = decodedlen - pos;
+
+ // Blowfish data blocks are 64 bits wide - valid format?
+ if (size % 8)
+ return Err(sess, pubkey);
+
+ std::vector<char> decrypted(size + 1, 0);
+ for (size_t i = 0; i < size; i += 8)
+ BF_ecb_encrypt(&data[pos + i], reinterpret_cast<unsigned char*>(&decrypted[i]), &BFKey, BF_DECRYPT);
+
+ std::string password = &decrypted[0];
+ if (password.empty() || password.find_first_of("\r\n") != Anope::string::npos)
+ return Err(sess, pubkey);
+
+ SASL::IdentifyRequest* req = new SASL::IdentifyRequest(this->owner, m.source, username, password);
+ EventManager::Get()->Dispatch(&Event::CheckAuthentication::OnCheckAuthentication, nullptr, req);
+ req->Dispatch();
+
+ BN_free(pubkey);
+ }
+ }
+};
+
+
+class ModuleSASLDHBS : public Module
+{
+ DHBS dhbs;
+
+ public:
+ ModuleSASLDHBS(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR | EXTRA)
+ , dhbs(this)
+ {
+ }
+};
+
+MODULE_INIT(ModuleSASLDHBS)
diff --git a/modules/extra/m_sql_authentication.cpp b/modules/extra/sql_authentication.cpp
index a742af096..98128d325 100644
--- a/modules/extra/m_sql_authentication.cpp
+++ b/modules/extra/sql_authentication.cpp
@@ -1,13 +1,25 @@
/*
+ * Anope IRC Services
*
- * (C) 2012-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "module.h"
#include "modules/sql.h"
+#include "modules/nickserv.h"
static Module *me;
@@ -27,7 +39,7 @@ class SQLAuthenticationResult : public SQL::Interface
req->Release(me);
}
- void OnResult(const SQL::Result &r) anope_override
+ void OnResult(const SQL::Result &r) override
{
if (r.Rows() == 0)
{
@@ -45,19 +57,19 @@ class SQLAuthenticationResult : public SQL::Interface
}
catch (const SQL::Exception &) { }
- NickAlias *na = NickAlias::Find(req->GetAccount());
- BotInfo *NickServ = Config->GetClient("NickServ");
+ NickServ::Nick *na = NickServ::FindNick(req->GetAccount());
+ ServiceBot *NickServ = Config->GetClient("NickServ");
if (na == NULL)
{
- na = new NickAlias(req->GetAccount(), new NickCore(req->GetAccount()));
- FOREACH_MOD(OnNickRegister, (user, na, ""));
+ na = new NickServ::Nick(req->GetAccount(), new NickServ::Account(req->GetAccount()));
+ NickServ::EventManager::Get()->Dispatch(&NickServ::Event::NickRegister::OnNickRegister, user, na, "");
if (user && NickServ)
- user->SendMessage(NickServ, _("Your account \002%s\002 has been successfully created."), na->nick.c_str());
+ user->SendMessage(NickServ, _("Your account \002%s\002 has been successfully created."), na->GetNick().c_str());
}
- if (!email.empty() && email != na->nc->email)
+ if (!email.empty() && email != na->GetAccount()->GetEmail())
{
- na->nc->email = email;
+ na->GetAccount()->GetEmail() = email;
if (user && NickServ)
user->SendMessage(NickServ, _("Your email has been updated to \002%s\002."), email.c_str());
}
@@ -66,7 +78,7 @@ class SQLAuthenticationResult : public SQL::Interface
delete this;
}
- void OnError(const SQL::Result &r) anope_override
+ void OnError(const SQL::Result &r) override
{
Log(this->owner) << "m_sql_authentication: Error executing query " << r.GetQuery().query << ": " << r.GetError();
delete this;
@@ -74,6 +86,8 @@ class SQLAuthenticationResult : public SQL::Interface
};
class ModuleSQLAuthentication : public Module
+ , public EventHook<Event::PreCommand>
+ , public EventHook<Event::CheckAuthentication>
{
Anope::string engine;
Anope::string query;
@@ -88,18 +102,18 @@ class ModuleSQLAuthentication : public Module
}
- void OnReload(Configuration::Conf *conf) anope_override
+ void OnReload(Configuration::Conf *conf) override
{
Configuration::Block *config = conf->GetModule(this);
- this->engine = config->Get<const Anope::string>("engine");
- this->query = config->Get<const Anope::string>("query");
- this->disable_reason = config->Get<const Anope::string>("disable_reason");
+ this->engine = config->Get<Anope::string>("engine");
+ this->query = config->Get<Anope::string>("query");
+ this->disable_reason = config->Get<Anope::string>("disable_reason");
this->disable_email_reason = config->Get<Anope::string>("disable_email_reason");
this->SQL = ServiceReference<SQL::Provider>("SQL::Provider", this->engine);
}
- EventReturn OnPreCommand(CommandSource &source, Command *command, std::vector<Anope::string> &params) anope_override
+ EventReturn OnPreCommand(CommandSource &source, Command *command, std::vector<Anope::string> &params) override
{
if (!this->disable_reason.empty() && (command->name == "nickserv/register" || command->name == "nickserv/group"))
{
@@ -116,7 +130,7 @@ class ModuleSQLAuthentication : public Module
return EVENT_CONTINUE;
}
- void OnCheckAuthentication(User *u, IdentifyRequest *req) anope_override
+ void OnCheckAuthentication(User *u, IdentifyRequest *req) override
{
if (!this->SQL)
{
diff --git a/modules/extra/m_sql_log.cpp b/modules/extra/sql_log.cpp
index 3dd6c936f..320350ced 100644
--- a/modules/extra/m_sql_log.cpp
+++ b/modules/extra/sql_log.cpp
@@ -1,15 +1,27 @@
/*
+ * Anope IRC Services
*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2014-2016 Anope Team <team@anope.org>
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "module.h"
#include "modules/sql.h"
class SQLLog : public Module
+ , public EventHook<Event::LogMessage>
{
std::set<Anope::string> inited;
Anope::string table;
@@ -19,13 +31,13 @@ class SQLLog : public Module
{
}
- void OnReload(Configuration::Conf *conf) anope_override
+ void OnReload(Configuration::Conf *conf) override
{
Configuration::Block *config = conf->GetModule(this);
- this->table = config->Get<const Anope::string>("table", "logs");
+ this->table = config->Get<Anope::string>("table", "logs");
}
- void OnLogMessage(LogInfo *li, const Log *l, const Anope::string &msg) anope_override
+ void OnLogMessage(LogInfo *li, const Log *l, const Anope::string &msg) override
{
Anope::string ref_name;
ServiceReference<SQL::Provider> SQL;
@@ -96,9 +108,9 @@ class SQLLog : public Module
}
insert.SetValue("user", l->u ? l->u->nick : "");
- insert.SetValue("acc", l->nc ? l->nc->display : "");
+ insert.SetValue("acc", l->nc ? l->nc->GetDisplay() : "");
insert.SetValue("command", l->c ? l->c->name : "");
- insert.SetValue("channel", l->ci ? l->ci->name : "");
+ insert.SetValue("channel", l->ci ? l->ci->GetName() : "");
insert.SetValue("msg", msg);
SQL->Run(NULL, insert);
diff --git a/modules/extra/sql_oper.cpp b/modules/extra/sql_oper.cpp
new file mode 100644
index 000000000..17c825828
--- /dev/null
+++ b/modules/extra/sql_oper.cpp
@@ -0,0 +1,194 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/sql.h"
+
+class SQLOperResult : public SQL::Interface
+{
+ Reference<User> user;
+
+ struct SQLOperResultDeleter
+ {
+ SQLOperResult *res;
+ SQLOperResultDeleter(SQLOperResult *r) : res(r) { }
+ ~SQLOperResultDeleter() { delete res; }
+ };
+
+ void Deoper()
+ {
+ if (user->Account()->o && user->Account()->o->owner == this->owner)
+ {
+ user->Account()->o->Delete();
+ user->Account()->o = nullptr;
+
+ Log(this->owner) << "Removed services operator from " << user->nick << " (" << user->Account()->GetDisplay() << ")";
+ user->RemoveMode(Config->GetClient("OperServ"), "OPER"); // Probably not set, just incase
+ }
+ }
+
+ public:
+ SQLOperResult(Module *m, User *u) : SQL::Interface(m), user(u) { }
+
+ void OnResult(const SQL::Result &r) override
+ {
+ SQLOperResultDeleter d(this);
+
+ if (!user || !user->Account())
+ return;
+
+ if (r.Rows() == 0)
+ {
+ Log(LOG_DEBUG) << "m_sql_oper: Got 0 rows for " << user->nick;
+ Deoper();
+ return;
+ }
+
+ Anope::string opertype;
+ try
+ {
+ opertype = r.Get(0, "opertype");
+ }
+ catch (const SQL::Exception &)
+ {
+ Log(this->owner) << "Expected column named \"opertype\" but one was not found";
+ return;
+ }
+
+ Log(LOG_DEBUG) << "m_sql_oper: Got result for " << user->nick << ", opertype " << opertype;
+
+ Anope::string modes;
+ try
+ {
+ modes = r.Get(0, "modes");
+ }
+ catch (const SQL::Exception &) { }
+
+ ServiceBot *OperServ = Config->GetClient("OperServ");
+ if (opertype.empty())
+ {
+ Deoper();
+ return;
+ }
+
+ OperType *ot = OperType::Find(opertype);
+ if (ot == NULL)
+ {
+ Log(this->owner) << "m_sql_oper: Oper " << user->nick << " has type " << opertype << ", but this opertype does not exist?";
+ return;
+ }
+
+ if (user->Account()->o && user->Account()->o->owner != this->owner)
+ {
+ Log(this->owner) << "Oper " << user->Account()->GetDisplay() << " has type " << ot->GetName() << ", but is already configured as an oper of type " << user->Account()->o->GetType()->GetName();
+ return;
+ }
+
+ if (!user->Account()->o || user->Account()->o->GetType() != ot)
+ {
+ Log(this->owner) << "m_sql_oper: Tieing oper " << user->nick << " to type " << opertype;
+
+ if (user->Account()->o)
+ user->Account()->o->Delete();
+
+ Oper *o = Serialize::New<Oper *>();
+ o->owner = this->owner;
+ o->SetName(user->Account()->GetDisplay());
+ o->SetType(ot);
+
+ user->Account()->o = o;
+ }
+
+ if (!user->HasMode("OPER"))
+ {
+ IRCD->SendOper(user);
+
+ if (!modes.empty())
+ user->SetModes(OperServ, "%s", modes.c_str());
+ }
+ }
+
+ void OnError(const SQL::Result &r) override
+ {
+ SQLOperResultDeleter d(this);
+ Log(this->owner) << "m_sql_oper: Error executing query " << r.GetQuery().query << ": " << r.GetError();
+ }
+};
+
+class ModuleSQLOper : public Module
+ , public EventHook<Event::NickIdentify>
+{
+ Anope::string engine;
+ Anope::string query;
+
+ ServiceReference<SQL::Provider> SQL;
+
+ public:
+ ModuleSQLOper(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, EXTRA | VENDOR),
+ EventHook<Event::NickIdentify>(this)
+ {
+ }
+
+ ~ModuleSQLOper()
+ {
+ if (NickServ::service == nullptr)
+ return;
+
+ NickServ::nickcore_map& map = NickServ::service->GetAccountMap();
+ for (NickServ::nickcore_map::const_iterator it = map.begin(); it != map.end(); ++it)
+ {
+ NickServ::Account *nc = it->second;
+
+ if (nc->o && nc->o->owner == this)
+ {
+ nc->o->Delete();
+ nc->o = nullptr;
+ }
+ }
+ }
+
+ void OnReload(Configuration::Conf *conf) override
+ {
+ Configuration::Block *config = conf->GetModule(this);
+
+ this->engine = config->Get<Anope::string>("engine");
+ this->query = config->Get<Anope::string>("query");
+
+ this->SQL = ServiceReference<SQL::Provider>(engine);
+ }
+
+ void OnNickIdentify(User *u) override
+ {
+ if (!this->SQL)
+ {
+ Log() << "Unable to find SQL engine: " << engine;
+ return;
+ }
+
+ SQL::Query q(this->query);
+ q.SetValue("a", u->Account()->GetDisplay());
+ q.SetValue("i", u->ip.addr());
+
+ this->SQL->Run(new SQLOperResult(this, u), q);
+
+ Log(LOG_DEBUG) << "m_sql_oper: Checking authentication for " << u->Account()->GetDisplay();
+ }
+};
+
+MODULE_INIT(ModuleSQLOper)
diff --git a/modules/extra/sqlite.cpp b/modules/extra/sqlite.cpp
new file mode 100644
index 000000000..59acbf43f
--- /dev/null
+++ b/modules/extra/sqlite.cpp
@@ -0,0 +1,399 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2011-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+/* RequiredLibraries: sqlite3 */
+/* RequiredWindowsLibraries: sqlite3 */
+
+#include "module.h"
+#include "modules/sql.h"
+#include <sqlite3.h>
+
+using namespace SQL;
+
+/* SQLite3 API, based from InspiRCd */
+
+/** A SQLite result
+ */
+class SQLiteResult : public Result
+{
+ public:
+ SQLiteResult(sqlite3 *sql, unsigned int id, const Query &q, const Anope::string &fq, sqlite3_stmt *stmt) : Result(id, q, fq)
+ {
+ int cols = sqlite3_column_count(stmt);
+ for (int i = 0; i < cols; ++i)
+ this->columns.push_back(sqlite3_column_name(stmt, i));
+
+ int err;
+ while ((err = sqlite3_step(stmt)) == SQLITE_ROW)
+ {
+ std::vector<SQL::Result::Value> values;
+
+ for (int i = 0; i < cols; ++i)
+ {
+ const char *data = reinterpret_cast<const char *>(sqlite3_column_text(stmt, i));
+
+ Value v;
+ v.null = !data;
+ v.value = data ? data : "";
+ values.push_back(v);
+ }
+
+ this->values.push_back(values);
+ }
+
+ if (err != SQLITE_DONE)
+ {
+ error = sqlite3_errmsg(sql);
+ }
+ }
+
+ SQLiteResult(const Query &q, const Anope::string &fq, const Anope::string &err) : Result(0, q, fq, err)
+ {
+ }
+};
+
+/** A SQLite database, there can be multiple
+ */
+class SQLiteService : public Provider
+{
+ std::map<Anope::string, std::set<Anope::string> > active_schema, indexes;
+
+ Anope::string database;
+
+ sqlite3 *sql;
+
+ Anope::string Escape(const Anope::string &query);
+
+ public:
+ SQLiteService(Module *o, const Anope::string &n, const Anope::string &d);
+
+ ~SQLiteService();
+
+ void Run(Interface *i, const Query &query) override;
+
+ Result RunQuery(const Query &query);
+
+ std::vector<Query> InitSchema(const Anope::string &prefix) override;
+ std::vector<Query> Replace(const Anope::string &table, const Query &, const std::set<Anope::string> &) override;
+ std::vector<Query> CreateTable(const Anope::string &, const Anope::string &table) override;
+ std::vector<Query> AlterTable(const Anope::string &, const Anope::string &table, const Anope::string &field, bool) override;
+ std::vector<Query> CreateIndex(const Anope::string &table, const Anope::string &field) override;
+
+ Query BeginTransaction() override;
+ Query Commit() override;
+
+ Serialize::ID GetID(const Anope::string &) override;
+
+ Query GetTables(const Anope::string &prefix);
+
+ Anope::string BuildQuery(const Query &q);
+};
+
+class ModuleSQLite : public Module
+{
+ /* SQL connections */
+ std::map<Anope::string, SQLiteService *> SQLiteServices;
+
+ public:
+ ModuleSQLite(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, EXTRA | VENDOR)
+ {
+ }
+
+ ~ModuleSQLite()
+ {
+ for (std::map<Anope::string, SQLiteService *>::iterator it = this->SQLiteServices.begin(); it != this->SQLiteServices.end(); ++it)
+ delete it->second;
+ SQLiteServices.clear();
+ }
+
+ void OnReload(Configuration::Conf *conf) override
+ {
+ Configuration::Block *config = conf->GetModule(this);
+
+ for (std::map<Anope::string, SQLiteService *>::iterator it = this->SQLiteServices.begin(); it != this->SQLiteServices.end();)
+ {
+ const Anope::string &cname = it->first;
+ SQLiteService *s = it->second;
+ int i, num;
+ ++it;
+
+ for (i = 0, num = config->CountBlock("sqlite"); i < num; ++i)
+ if (config->GetBlock("sqlite", i)->Get<Anope::string>("name", "sqlite/main") == cname)
+ break;
+
+ if (i == num)
+ {
+ Log(LOG_NORMAL, "sqlite") << "SQLite: Removing server connection " << cname;
+
+ delete s;
+ this->SQLiteServices.erase(cname);
+ }
+ }
+
+ for (int i = 0; i < config->CountBlock("sqlite"); ++i)
+ {
+ Configuration::Block *block = config->GetBlock("sqlite", i);
+ Anope::string connname = block->Get<Anope::string>("name", "sqlite/main");
+
+ if (this->SQLiteServices.find(connname) == this->SQLiteServices.end())
+ {
+ Anope::string database = Anope::DataDir + "/" + block->Get<Anope::string>("database", "anope");
+
+ try
+ {
+ SQLiteService *ss = new SQLiteService(this, connname, database);
+ this->SQLiteServices[connname] = ss;
+
+ Log(LOG_NORMAL, "sqlite") << "SQLite: Successfully added database " << database;
+ }
+ catch (const SQL::Exception &ex)
+ {
+ Log(LOG_NORMAL, "sqlite") << "SQLite: " << ex.GetReason();
+ }
+ }
+ }
+ }
+};
+
+SQLiteService::SQLiteService(Module *o, const Anope::string &n, const Anope::string &d)
+: Provider(o, n), database(d), sql(NULL)
+{
+ int db = sqlite3_open_v2(database.c_str(), &this->sql, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0);
+ if (db != SQLITE_OK)
+ throw SQL::Exception("Unable to open SQLite database " + database + ": " + sqlite3_errmsg(this->sql));
+}
+
+SQLiteService::~SQLiteService()
+{
+ sqlite3_interrupt(this->sql);
+ sqlite3_close(this->sql);
+}
+
+void SQLiteService::Run(Interface *i, const Query &query)
+{
+ Result res = this->RunQuery(query);
+ if (!res.GetError().empty())
+ i->OnError(res);
+ else
+ i->OnResult(res);
+}
+
+Result SQLiteService::RunQuery(const Query &query)
+{
+ Anope::string real_query = this->BuildQuery(query);
+ sqlite3_stmt *stmt;
+ int err = sqlite3_prepare_v2(this->sql, real_query.c_str(), real_query.length(), &stmt, NULL);
+ if (err != SQLITE_OK)
+ {
+ return SQLiteResult(query, real_query, sqlite3_errmsg(this->sql));
+ }
+
+ int id = sqlite3_last_insert_rowid(this->sql);
+ SQLiteResult result(this->sql, id, query, real_query, stmt);
+
+ sqlite3_finalize(stmt);
+
+ return result;
+}
+
+std::vector<Query> SQLiteService::InitSchema(const Anope::string &prefix)
+{
+ std::vector<Query> queries;
+
+ Query t = "CREATE TABLE IF NOT EXISTS `" + prefix + "id` ("
+ "`id`"
+ ")";
+ queries.push_back(t);
+
+ t = "CREATE TABLE IF NOT EXISTS `" + prefix + "objects` (`id` PRIMARY KEY, `type`)";
+ queries.push_back(t);
+
+ t = "CREATE TABLE IF NOT EXISTS `" + prefix + "edges` ("
+ "`id`,"
+ "`field`,"
+ "`other_id`,"
+ "PRIMARY KEY (`id`, `field`)"
+ ")";
+ queries.push_back(t);
+
+ t = "CREATE INDEX IF NOT EXISTS idx_edge ON `" + prefix + "edges` (other_id)";
+ queries.push_back(t);
+
+ return queries;
+}
+
+std::vector<Query> SQLiteService::Replace(const Anope::string &table, const Query &q, const std::set<Anope::string> &keys)
+{
+ std::vector<Query> queries;
+
+ Anope::string query_text = "INSERT OR IGNORE INTO `" + table + "` (";
+ for (const std::pair<Anope::string, QueryData> &p : q.parameters)
+ query_text += "`" + p.first + "`,";
+ query_text.erase(query_text.length() - 1);
+ query_text += ") VALUES (";
+ for (const std::pair<Anope::string, QueryData> &p : q.parameters)
+ query_text += "@" + p.first + "@,";
+ query_text.erase(query_text.length() - 1);
+ query_text += ")";
+
+ Query query(query_text);
+ query.parameters = q.parameters;
+ queries.push_back(query);
+
+ query_text = "UPDATE `" + table + "` SET ";
+ for (const std::pair<Anope::string, QueryData> &p : q.parameters)
+ if (!keys.count(p.first))
+ query_text += "`" + p.first + "` = @" + p.first + "@,";
+ query_text.erase(query_text.length() - 1);
+ unsigned int i = 0;
+ for (const Anope::string &key : keys)
+ {
+ if (!i++)
+ query_text += " WHERE ";
+ else
+ query_text += " AND ";
+ query_text += "`" + key + "` = @" + key + "@";
+ }
+
+ query = query_text;
+ query.parameters = q.parameters;
+ queries.push_back(query);
+
+ return queries;
+}
+
+std::vector<Query> SQLiteService::CreateTable(const Anope::string &prefix, const Anope::string &table)
+{
+ std::vector<Query> queries;
+
+ if (active_schema.find(prefix + table) == active_schema.end())
+ {
+ Query t = "CREATE TABLE IF NOT EXISTS `" + prefix + table + "` (`id` bigint(20) NOT NULL, PRIMARY KEY (`id`))";
+ queries.push_back(t);
+
+ active_schema[prefix + table];
+ }
+
+ return queries;
+}
+
+std::vector<Query> SQLiteService::AlterTable(const Anope::string &prefix, const Anope::string &table, const Anope::string &field, bool)
+{
+ std::vector<Query> queries;
+ std::set<Anope::string> &s = active_schema[prefix + table];
+
+ if (!s.count(field))
+ {
+ Query t = "ALTER TABLE `" + prefix + table + "` ADD `" + field + "` COLLATE NOCASE";
+ queries.push_back(t);
+ s.insert(field);
+ }
+
+ return queries;
+}
+
+std::vector<Query> SQLiteService::CreateIndex(const Anope::string &table, const Anope::string &field)
+{
+ std::vector<Query> queries;
+
+ if (indexes[table].count(field))
+ return queries;
+
+ Query t = "CREATE INDEX IF NOT EXISTS idx_" + field + " ON `" + table + "` (" + field + ")";
+ queries.push_back(t);
+
+ indexes[table].insert(field);
+
+ return queries;
+}
+
+Query SQLiteService::BeginTransaction()
+{
+ return Query("BEGIN TRANSACTION");
+}
+
+Query SQLiteService::Commit()
+{
+ return Query("COMMIT");
+}
+
+Serialize::ID SQLiteService::GetID(const Anope::string &prefix)
+{
+ /* must be in a deferred or reserved transaction here for atomic row update */
+
+ Query query("SELECT `id` FROM `" + prefix + "id`");
+ Serialize::ID id;
+
+ Result res = RunQuery(query);
+ if (res.Rows())
+ {
+ id = convertTo<Serialize::ID>(res.Get(0, "id"));
+
+ Query update_query("UPDATE `" + prefix + "id` SET `id` = `id` + 1");
+ RunQuery(update_query);
+ }
+ else
+ {
+ id = 0;
+
+ Query insert_query("INSERT INTO `" + prefix + "id` (id) VALUES(@id@)");
+ insert_query.SetValue("id", 1);
+ RunQuery(insert_query);
+ }
+
+ return id;
+}
+
+Query SQLiteService::GetTables(const Anope::string &prefix)
+{
+ return Query("SELECT name FROM sqlite_master WHERE type='table' AND name LIKE '" + prefix + "%';");
+}
+
+Anope::string SQLiteService::Escape(const Anope::string &query)
+{
+ char *e = sqlite3_mprintf("%q", query.c_str());
+ Anope::string buffer = e;
+ sqlite3_free(e);
+ return buffer;
+}
+
+Anope::string SQLiteService::BuildQuery(const Query &q)
+{
+ Anope::string real_query = q.query;
+
+ for (std::map<Anope::string, QueryData>::const_iterator it = q.parameters.begin(), it_end = q.parameters.end(); it != it_end; ++it)
+ {
+ const QueryData& qd = it->second;
+ Anope::string replacement;
+
+ if (qd.null)
+ replacement = "NULL";
+ else if (!qd.escape)
+ replacement = qd.data;
+ else
+ replacement = "'" + this->Escape(qd.data) + "'";
+
+ real_query = real_query.replace_all_cs("@" + it->first + "@", replacement);
+ }
+
+ return real_query;
+}
+
+MODULE_INIT(ModuleSQLite)
+
diff --git a/modules/extra/m_ssl_gnutls.cpp b/modules/extra/ssl_gnutls.cpp
index 318efe650..223b72519 100644
--- a/modules/extra/m_ssl_gnutls.cpp
+++ b/modules/extra/ssl_gnutls.cpp
@@ -1,10 +1,21 @@
/*
+ * Anope IRC Services
*
- * (C) 2014 Attila Molnar <attilamolnar@hush.com>
- * (C) 2014-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ * Copyright (C) 2014-2016 Anope Team <team@anope.org>
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
/* RequiredLibraries: gnutls */
@@ -30,7 +41,7 @@ class MySSLService : public SSLService
/** Initialize a socket to use SSL
* @param s The socket
*/
- void Init(Socket *s) anope_override;
+ void Init(Socket *s) override;
};
class SSLSocketIO : public SocketIO
@@ -49,43 +60,43 @@ class SSLSocketIO : public SocketIO
* @param sz How much to read
* @return Number of bytes received
*/
- int Recv(Socket *s, char *buf, size_t sz) anope_override;
+ int Recv(Socket *s, char *buf, size_t sz) override;
/** Write something to the socket
* @param s The socket
* @param buf The data to write
* @param size The length of the data
*/
- int Send(Socket *s, const char *buf, size_t sz) anope_override;
+ int Send(Socket *s, const char *buf, size_t sz) override;
/** Accept a connection from a socket
* @param s The socket
* @return The new socket
*/
- ClientSocket *Accept(ListenSocket *s) anope_override;
+ ClientSocket *Accept(ListenSocket *s) override;
/** Finished accepting a connection from a socket
* @param s The socket
* @return SF_ACCEPTED if accepted, SF_ACCEPTING if still in process, SF_DEAD on error
*/
- SocketFlag FinishAccept(ClientSocket *cs) anope_override;
+ SocketFlag FinishAccept(ClientSocket *cs) override;
/** Connect the socket
* @param s THe socket
* @param target IP to connect to
* @param port to connect to
*/
- void Connect(ConnectionSocket *s, const Anope::string &target, int port) anope_override;
+ void Connect(ConnectionSocket *s, const Anope::string &target, int port) override;
/** Called to potentially finish a pending connection
* @param s The socket
* @return SF_CONNECTED on success, SF_CONNECTING if still pending, and SF_DEAD on error.
*/
- SocketFlag FinishConnect(ConnectionSocket *s) anope_override;
+ SocketFlag FinishConnect(ConnectionSocket *s) override;
/** Called when the socket is destructing
*/
- void Destroy() anope_override;
+ void Destroy() override;
};
namespace GnuTLS
@@ -295,6 +306,7 @@ namespace GnuTLS
}
class GnuTLSModule : public Module
+ , public EventHook<Event::PreServerConnect>
{
GnuTLS::Init libinit;
@@ -302,7 +314,9 @@ class GnuTLSModule : public Module
GnuTLS::X509CertCredentials *cred;
MySSLService service;
- GnuTLSModule(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, EXTRA | VENDOR), cred(NULL), service(this, "ssl")
+ GnuTLSModule(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, EXTRA | VENDOR)
+ , cred(NULL)
+ , service(this, "ssl")
{
me = this;
this->SetPermanent(true);
@@ -332,13 +346,13 @@ class GnuTLSModule : public Module
}
}
- void OnReload(Configuration::Conf *conf) anope_override
+ void OnReload(Configuration::Conf *conf) override
{
Configuration::Block *config = conf->GetModule(this);
- const Anope::string certfile = config->Get<const Anope::string>("cert", "data/anope.crt");
- const Anope::string keyfile = config->Get<const Anope::string>("key", "data/anope.key");
- const Anope::string dhfile = config->Get<const Anope::string>("dh", "data/dhparams.pem");
+ const Anope::string certfile = config->Get<Anope::string>("cert", "data/anope.crt");
+ const Anope::string keyfile = config->Get<Anope::string>("key", "data/anope.key");
+ const Anope::string dhfile = config->Get<Anope::string>("dh", "data/dhparams.pem");
CheckFile(certfile);
CheckFile(keyfile);
@@ -368,7 +382,7 @@ class GnuTLSModule : public Module
Log(LOG_DEBUG) << "m_ssl_gnutls: Successfully loaded certificate " << certfile << " and private key " << keyfile;
}
- void OnPreServerConnect() anope_override
+ void OnPreServerConnect() override
{
Configuration::Block *config = Config->GetBlock("uplink", Anope::CurrentUplink);
diff --git a/modules/extra/m_ssl_openssl.cpp b/modules/extra/ssl_openssl.cpp
index 00df168de..8958592e3 100644
--- a/modules/extra/m_ssl_openssl.cpp
+++ b/modules/extra/ssl_openssl.cpp
@@ -1,9 +1,20 @@
/*
+ * Anope IRC Services
*
- * (C) 2010-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2010-2016 Anope Team <team@anope.org>
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
/* RequiredLibraries: ssl,crypto */
@@ -29,7 +40,7 @@ class MySSLService : public SSLService
/** Initialize a socket to use SSL
* @param s The socket
*/
- void Init(Socket *s) anope_override;
+ void Init(Socket *s) override;
};
class SSLSocketIO : public SocketIO
@@ -48,55 +59,57 @@ class SSLSocketIO : public SocketIO
* @param sz How much to read
* @return Number of bytes received
*/
- int Recv(Socket *s, char *buf, size_t sz) anope_override;
+ int Recv(Socket *s, char *buf, size_t sz) override;
/** Write something to the socket
* @param s The socket
* @param buf The data to write
* @param size The length of the data
*/
- int Send(Socket *s, const char *buf, size_t sz) anope_override;
+ int Send(Socket *s, const char *buf, size_t sz) override;
/** Accept a connection from a socket
* @param s The socket
* @return The new socket
*/
- ClientSocket *Accept(ListenSocket *s) anope_override;
+ ClientSocket *Accept(ListenSocket *s) override;
/** Finished accepting a connection from a socket
* @param s The socket
* @return SF_ACCEPTED if accepted, SF_ACCEPTING if still in process, SF_DEAD on error
*/
- SocketFlag FinishAccept(ClientSocket *cs) anope_override;
+ SocketFlag FinishAccept(ClientSocket *cs) override;
/** Connect the socket
* @param s THe socket
* @param target IP to connect to
* @param port to connect to
*/
- void Connect(ConnectionSocket *s, const Anope::string &target, int port) anope_override;
+ void Connect(ConnectionSocket *s, const Anope::string &target, int port) override;
/** Called to potentially finish a pending connection
* @param s The socket
* @return SF_CONNECTED on success, SF_CONNECTING if still pending, and SF_DEAD on error.
*/
- SocketFlag FinishConnect(ConnectionSocket *s) anope_override;
+ SocketFlag FinishConnect(ConnectionSocket *s) override;
/** Called when the socket is destructing
*/
- void Destroy() anope_override;
+ void Destroy() override;
};
class SSLModule;
static SSLModule *me;
class SSLModule : public Module
+ , public EventHook<Event::PreServerConnect>
{
Anope::string certfile, keyfile;
public:
MySSLService service;
- SSLModule(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, EXTRA | VENDOR), service(this, "ssl")
+ SSLModule(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, EXTRA | VENDOR)
+ , service(this, "ssl")
{
me = this;
@@ -138,12 +151,12 @@ class SSLModule : public Module
SSL_CTX_free(server_ctx);
}
- void OnReload(Configuration::Conf *conf) anope_override
+ void OnReload(Configuration::Conf *conf) override
{
Configuration::Block *config = conf->GetModule(this);
- this->certfile = config->Get<const Anope::string>("cert", "data/anope.crt");
- this->keyfile = config->Get<const Anope::string>("key", "data/anope.key");
+ this->certfile = config->Get<Anope::string>("cert", "data/anope.crt");
+ this->keyfile = config->Get<Anope::string>("key", "data/anope.key");
if (Anope::IsFile(this->certfile.c_str()))
{
@@ -186,7 +199,7 @@ class SSLModule : public Module
}
}
- void OnPreServerConnect() anope_override
+ void OnPreServerConnect() override
{
Configuration::Block *config = Config->GetBlock("uplink", Anope::CurrentUplink);
@@ -205,7 +218,7 @@ void MySSLService::Init(Socket *s)
{
if (s->io != &NormalSocketIO)
throw CoreException("Socket initializing SSL twice");
-
+
s->io = new SSLSocketIO();
}
@@ -283,7 +296,7 @@ ClientSocket *SSLSocketIO::Accept(ListenSocket *s)
newsocket->flags[SF_ACCEPTING] = true;
this->FinishAccept(newsocket);
-
+
return newsocket;
}
@@ -297,7 +310,7 @@ SocketFlag SSLSocketIO::FinishAccept(ClientSocket *cs)
throw SocketException("SSLSocketIO::FinishAccept called for a socket not accepted nor accepting?");
SSLSocketIO *io = anope_dynamic_static_cast<SSLSocketIO *>(cs->io);
-
+
int ret = SSL_accept(io->sslsock);
if (ret <= 0)
{
@@ -378,7 +391,7 @@ SocketFlag SSLSocketIO::FinishConnect(ConnectionSocket *s)
if (!SSL_set_fd(io->sslsock, s->GetFD()))
throw SocketException("Unable to set SSL fd");
}
-
+
int ret = SSL_connect(io->sslsock);
if (ret <= 0)
{
diff --git a/modules/extra/stats/m_chanstats.cpp b/modules/extra/stats/chanstats.cpp
index e21b3641d..65a198449 100644
--- a/modules/extra/stats/m_chanstats.cpp
+++ b/modules/extra/stats/chanstats.cpp
@@ -10,9 +10,9 @@ class CommandCSSetChanstats : public Command
this->SetSyntax(_("\037channel\037 {ON | OFF}"));
}
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
{
- ChannelInfo *ci = ChannelInfo::Find(params[0]);
+ ChanServ::Channel *ci = ChanServ::Find(params[0]);
if (!ci)
{
source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
@@ -20,7 +20,7 @@ class CommandCSSetChanstats : public Command
}
EventReturn MOD_RESULT;
- FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, params[1]));
+ MOD_RESULT = EventManager::Get()->Dispatch(&Event::SetChannelOption::OnSetChannelOption, source, this, ci, params[1]);
if (MOD_RESULT == EVENT_STOP)
return;
@@ -46,7 +46,7 @@ class CommandCSSetChanstats : public Command
this->OnSyntaxError(source, "");
}
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
+ bool OnHelp(CommandSource &source, const Anope::string &) override
{
this->SendSyntax(source);
source.Reply(" ");
@@ -65,7 +65,7 @@ class CommandNSSetChanstats : public Command
}
void Run(CommandSource &source, const Anope::string &user, const Anope::string &param, bool saset = false)
{
- NickAlias *na = NickAlias::Find(user);
+ NickServ::Nick *na = NickServ::FindNick(user);
if (!na)
{
source.Reply(NICK_X_NOT_REGISTERED, user.c_str());
@@ -73,25 +73,25 @@ class CommandNSSetChanstats : public Command
}
EventReturn MOD_RESULT;
- FOREACH_RESULT(OnSetNickOption, MOD_RESULT, (source, this, na->nc, param));
+ MOD_RESULT = EventManager::Get()->Dispatch(&Event::SetNickOption::OnSetNickOption, source, this, na->GetAccount(), param);
if (MOD_RESULT == EVENT_STOP)
return;
if (param.equals_ci("ON"))
{
- Log(na->nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to enable chanstats for " << na->nc->display;
- na->nc->Extend<bool>("NS_STATS");
+ Log(na->GetAccount() == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to enable chanstats for " << na->GetAccount()->GetDisplay();
+ na->GetAccount()->Extend<bool>("NS_STATS");
if (saset)
- source.Reply(_("Chanstats statistics are now enabled for %s"), na->nc->display.c_str());
+ source.Reply(_("Chanstats statistics are now enabled for %s"), na->GetAccount()->GetDisplay().c_str());
else
source.Reply(_("Chanstats statistics are now enabled for your nick."));
}
else if (param.equals_ci("OFF"))
{
- Log(na->nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to disable chanstats for " << na->nc->display;
- na->nc->Shrink<bool>("NS_STATS");
+ Log(na->GetAccount() == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to disable chanstats for " << na->GetAccount()->GetDisplay();
+ na->GetAccount()->Shrink<bool>("NS_STATS");
if (saset)
- source.Reply(_("Chanstats statistics are now disabled for %s"), na->nc->display.c_str());
+ source.Reply(_("Chanstats statistics are now disabled for %s"), na->GetAccount()->GetDisplay().c_str());
else
source.Reply(_("Chanstats statistics are now disabled for your nick."));
}
@@ -99,12 +99,12 @@ class CommandNSSetChanstats : public Command
this->OnSyntaxError(source, "CHANSTATS");
}
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
{
- this->Run(source, source.nc->display, params[0]);
+ this->Run(source, source.nc->GetDisplay(), params[0]);
}
-
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
{
this->SendSyntax(source);
source.Reply(" ");
@@ -122,12 +122,12 @@ class CommandNSSASetChanstats : public CommandNSSetChanstats
this->SetSyntax(_("\037nickname\037 {ON | OFF}"));
}
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
{
this->Run(source, params[0], params[1], true);
}
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
+ bool OnHelp(CommandSource &source, const Anope::string &) override
{
this->SendSyntax(source);
source.Reply(" ");
@@ -141,11 +141,11 @@ class MySQLInterface : public SQL::Interface
public:
MySQLInterface(Module *o) : SQL::Interface(o) { }
- void OnResult(const SQL::Result &r) anope_override
+ void OnResult(const SQL::Result &r) override
{
}
- void OnError(const SQL::Result &r) anope_override
+ void OnError(const SQL::Result &r) override
{
if (!r.GetQuery().query.empty())
Log(LOG_DEBUG) << "Chanstats: Error executing query " << r.finished_query << ": " << r.GetError();
@@ -156,7 +156,7 @@ class MySQLInterface : public SQL::Interface
class MChanstats : public Module
{
- SerializableExtensibleItem<bool> cs_stats, ns_stats;
+ Serialize::Field<bool> cs_stats, ns_stats;
CommandCSSetChanstats commandcssetchanstats;
@@ -200,7 +200,7 @@ class MChanstats : public Module
const Anope::string GetDisplay(User *u)
{
if (u && u->Account() && ns_stats.HasExt(u->Account()))
- return u->Account()->display;
+ return u->Account()->GetDisplay();
else
return "";
}
@@ -484,16 +484,16 @@ class MChanstats : public Module
{
}
- void OnReload(Configuration::Conf *conf) anope_override
+ void OnReload(Configuration::Conf *conf) override
{
Configuration::Block *block = conf->GetModule(this);
- prefix = block->Get<const Anope::string>("prefix", "anope_");
- SmileysHappy = block->Get<const Anope::string>("SmileysHappy");
- SmileysSad = block->Get<const Anope::string>("SmileysSad");
- SmileysOther = block->Get<const Anope::string>("SmileysOther");
+ prefix = block->Get<Anope::string>("prefix", "anope_");
+ SmileysHappy = block->Get<Anope::string>("SmileysHappy");
+ SmileysSad = block->Get<Anope::string>("SmileysSad");
+ SmileysOther = block->Get<Anope::string>("SmileysOther");
NSDefChanstats = block->Get<bool>("ns_def_chanstats");
CSDefChanstats = block->Get<bool>("cs_def_chanstats");
- Anope::string engine = block->Get<const Anope::string>("engine");
+ Anope::string engine = block->Get<Anope::string>("engine");
this->sql = ServiceReference<SQL::Provider>("SQL::Provider", engine);
if (sql)
this->CheckTables();
@@ -501,7 +501,7 @@ class MChanstats : public Module
Log(this) << "no database connection to " << engine;
}
- void OnChanInfo(CommandSource &source, ChannelInfo *ci, InfoFormatter &info, bool show_all) anope_override
+ void OnChanInfo(CommandSource &source, ChanServ::Channel *ci, InfoFormatter &info, bool show_all) override
{
if (!show_all)
return;
@@ -509,15 +509,15 @@ class MChanstats : public Module
info.AddOption(_("Chanstats"));
}
- void OnNickInfo(CommandSource &source, NickAlias *na, InfoFormatter &info, bool show_hidden) anope_override
+ void OnNickInfo(CommandSource &source, NickServ::Nick *na, InfoFormatter &info, bool show_hidden) override
{
if (!show_hidden)
return;
- if (ns_stats.HasExt(na->nc))
+ if (ns_stats.HasExt(na->GetAccount()))
info.AddOption(_("Chanstats"));
}
- void OnTopicUpdated(User *source, Channel *c, const Anope::string &user, const Anope::string &topic) anope_override
+ void OnTopicUpdated(User *source, Channel *c, const Anope::string &user, const Anope::string &topic) override
{
if (!source || !source->Account() || !c->ci || !cs_stats.HasExt(c->ci))
return;
@@ -527,13 +527,13 @@ class MChanstats : public Module
this->RunQuery(query);
}
- EventReturn OnChannelModeSet(Channel *c, MessageSource &setter, ChannelMode *mode, const Anope::string &param) anope_override
+ EventReturn OnChannelModeSet(Channel *c, const MessageSource &setter, ChannelMode *mode, const Anope::string &param) override
{
this->OnModeChange(c, setter.GetUser());
return EVENT_CONTINUE;
}
- EventReturn OnChannelModeUnset(Channel *c, MessageSource &setter, ChannelMode *, const Anope::string &param) anope_override
+ EventReturn OnChannelModeUnset(Channel *c, const MessageSource &setter, ChannelMode *, const Anope::string &param) override
{
this->OnModeChange(c, setter.GetUser());
return EVENT_CONTINUE;
@@ -552,7 +552,7 @@ class MChanstats : public Module
}
public:
- void OnPreUserKicked(const MessageSource &source, ChanUserContainer *cu, const Anope::string &kickmsg) anope_override
+ void OnPreUserKicked(const MessageSource &source, ChanUserContainer *cu, const Anope::string &kickmsg) override
{
if (!cu->chan->ci || !cs_stats.HasExt(cu->chan->ci))
return;
@@ -568,7 +568,7 @@ class MChanstats : public Module
this->RunQuery(query);
}
- void OnPrivmsg(User *u, Channel *c, Anope::string &msg) anope_override
+ void OnPrivmsg(User *u, Channel *c, Anope::string &msg) override
{
if (!c->ci || !cs_stats.HasExt(c->ci))
return;
@@ -609,29 +609,29 @@ class MChanstats : public Module
this->RunQuery(query);
}
- void OnDelCore(NickCore *nc) anope_override
+ void OnDelCore(NickServ::Account *nc) override
{
query = "DELETE FROM `" + prefix + "chanstats` WHERE `nick` = @nick@;";
- query.SetValue("nick", nc->display);
+ query.SetValue("nick", nc->GetDisplay());
this->RunQuery(query);
}
- void OnChangeCoreDisplay(NickCore *nc, const Anope::string &newdisplay) anope_override
+ void OnChangeCoreDisplay(NickServ::Account *nc, const Anope::string &newdisplay) override
{
query = "CALL " + prefix + "chanstats_proc_chgdisplay(@old_display@, @new_display@);";
- query.SetValue("old_display", nc->display);
+ query.SetValue("old_display", nc->GetDisplay());
query.SetValue("new_display", newdisplay);
this->RunQuery(query);
}
- void OnDelChan(ChannelInfo *ci) anope_override
+ void OnDelChan(ChanServ::Channel *ci) override
{
query = "DELETE FROM `" + prefix + "chanstats` WHERE `chan` = @channel@;";
- query.SetValue("channel", ci->name);
+ query.SetValue("channel", ci->GetName());
this->RunQuery(query);
}
- void OnChanRegistered(ChannelInfo *ci)
+ void OnChanRegistered(ChanServ::Channel *ci)
{
if (CSDefChanstats)
ci->Extend<bool>("CS_STATS");
@@ -640,7 +640,7 @@ class MChanstats : public Module
void OnNickRegister(User *user, NickAlias *na, const Anope::string &)
{
if (NSDefChanstats)
- na->nc->Extend<bool>("NS_STATS");
+ na->GetAccount()->Extend<bool>("NS_STATS");
}
};
diff --git a/modules/extra/stats/cs_fantasy_stats.cpp b/modules/extra/stats/cs_fantasy_stats.cpp
index 37830abb1..118a7e496 100644
--- a/modules/extra/stats/cs_fantasy_stats.cpp
+++ b/modules/extra/stats/cs_fantasy_stats.cpp
@@ -1,6 +1,6 @@
/* Chanstats core functions
*
- * (C) 2003-2016 Anope Team
+ * (C) 2003-2014 Anope Team
* Contact us at team@anope.org
*
* Please read COPYING and README for further details.
@@ -17,11 +17,11 @@ class MySQLInterface : public SQL::Interface
public:
MySQLInterface(Module *o) : SQL::Interface(o) { }
- void OnResult(const SQL::Result &r) anope_override
+ void OnResult(const SQL::Result &r) override
{
}
- void OnError(const SQL::Result &r) anope_override
+ void OnError(const SQL::Result &r) override
{
if (!r.GetQuery().query.empty())
Log(LOG_DEBUG) << "Chanstats: Error executing query " << r.finished_query << ": " << r.GetError();
@@ -73,10 +73,10 @@ class CSStats : public Module
}
- void OnReload(Configuration::Conf *conf) anope_override
+ void OnReload(Configuration::Conf *conf) override
{
- prefix = conf->GetModule("m_chanstats")->Get<const Anope::string>("prefix", "anope_");
- this->sql = ServiceReference<SQL::Provider>("SQL::Provider", conf->GetModule("m_chanstats")->Get<const Anope::string>("engine"));
+ prefix = conf->GetModule("m_chanstats")->Get<Anope::string>("prefix", "anope_");
+ this->sql = ServiceReference<SQL::Provider>("SQL::Provider", conf->GetModule("m_chanstats")->Get<Anope::string>("engine"));
}
SQL::Result RunQuery(const SQL::Query &query)
@@ -113,8 +113,8 @@ class CSStats : public Module
channel = params[0];
else
{
- if (NickAlias *na = NickAlias::Find(params[0]))
- display = na->nc->display;
+ if (NickServ::Nick *na = NickServ::FindNick(params[0]))
+ display = na->GetAccount()->GetDisplay();
else
{
source.Reply(_("%s not found."), params[0].c_str());
@@ -125,7 +125,7 @@ class CSStats : public Module
}
if (display.empty())
- display = source.nc->display;
+ display = source.nc->GetDisplay();
try
{
diff --git a/modules/extra/stats/cs_fantasy_top.cpp b/modules/extra/stats/cs_fantasy_top.cpp
index 0de75d855..82480ffc3 100644
--- a/modules/extra/stats/cs_fantasy_top.cpp
+++ b/modules/extra/stats/cs_fantasy_top.cpp
@@ -1,6 +1,6 @@
/* Chanstats core functions
*
- * (C) 2003-2016 Anope Team
+ * (C) 2003-2014 Anope Team
* Contact us at team@anope.org
*
* Please read COPYING and README for further details.
@@ -17,11 +17,11 @@ class MySQLInterface : public SQL::Interface
public:
MySQLInterface(Module *o) : SQL::Interface(o) { }
- void OnResult(const SQL::Result &r) anope_override
+ void OnResult(const SQL::Result &r) override
{
}
- void OnError(const SQL::Result &r) anope_override
+ void OnError(const SQL::Result &r) override
{
if (!r.GetQuery().query.empty())
Log(LOG_DEBUG) << "Chanstats: Error executing query " << r.finished_query << ": " << r.GetError();
@@ -98,10 +98,10 @@ class CSTop : public Module
}
- void OnReload(Configuration::Conf *conf) anope_override
+ void OnReload(Configuration::Conf *conf) override
{
- prefix = conf->GetModule("m_chanstats")->Get<const Anope::string>("prefix", "anope_");
- this->sql = ServiceReference<SQL::Provider>("SQL::Provider", conf->GetModule("m_chanstats")->Get<const Anope::string>("engine"));
+ prefix = conf->GetModule("m_chanstats")->Get<Anope::string>("prefix", "anope_");
+ this->sql = ServiceReference<SQL::Provider>("SQL::Provider", conf->GetModule("m_chanstats")->Get<Anope::string>("engine"));
}
SQL::Result RunQuery(const SQL::Query &query)
@@ -122,7 +122,7 @@ class CSTop : public Module
if (!params.empty())
channel = params[0];
else if (source.c && source.c->ci)
- channel = source.c->ci->name;
+ channel = source.c->ci->GetName();
if (!is_global && channel.empty())
is_global = true;
@@ -151,7 +151,7 @@ class CSTop : public Module
{
source.Reply(_("%2lu \002%-16s\002 letters: %s, words: %s, lines: %s, smileys: %s, actions: %s"),
i+1, res.Get(i, "nick").c_str(), res.Get(i, "letters").c_str(),
- res.Get(i, "words").c_str(), res.Get(i, "line").c_str(),
+ res.Get(i, "words").c_str(), res.Get(i, "line").c_str(),
res.Get(i, "smileys").c_str(), res.Get(i, "actions").c_str());
}
}
diff --git a/modules/extra/stats/irc2sql/irc2sql.cpp b/modules/extra/stats/irc2sql/irc2sql.cpp
index a00181304..e0c03edbd 100644
--- a/modules/extra/stats/irc2sql/irc2sql.cpp
+++ b/modules/extra/stats/irc2sql/irc2sql.cpp
@@ -11,21 +11,21 @@ void IRC2SQL::OnShutdown()
void IRC2SQL::OnReload(Configuration::Conf *conf)
{
Configuration::Block *block = Config->GetModule(this);
- prefix = block->Get<const Anope::string>("prefix", "anope_");
- GeoIPDB = block->Get<const Anope::string>("geoip_database");
+ prefix = block->Get<Anope::string>("prefix", "anope_");
+ GeoIPDB = block->Get<Anope::string>("geoip_database");
ctcpuser = block->Get<bool>("ctcpuser", "no");
ctcpeob = block->Get<bool>("ctcpeob", "yes");
- Anope::string engine = block->Get<const Anope::string>("engine");
+ Anope::string engine = block->Get<Anope::string>("engine");
this->sql = ServiceReference<SQL::Provider>("SQL::Provider", engine);
if (sql)
this->CheckTables();
else
Log() << "IRC2SQL: no database connection to " << engine;
- const Anope::string &snick = block->Get<const Anope::string>("client");
+ const Anope::string &snick = block->Get<Anope::string>("client");
if (snick.empty())
throw ConfigException(Module::name + ": <client> must be defined");
- StatServ = BotInfo::Find(snick, true);
+ StatServ = ServiceBot::Find(snick, true);
if (!StatServ)
throw ConfigException(Module::name + ": no bot named " + snick);
@@ -99,7 +99,7 @@ void IRC2SQL::OnUserConnect(User *u, bool &exempt)
query.SetValue("ident", u->GetIdent());
query.SetValue("vident", u->GetVIdent());
query.SetValue("secure", u->HasMode("SSL") || u->HasExt("ssl") ? "Y" : "N");
- query.SetValue("account", u->Account() ? u->Account()->display : "");
+ query.SetValue("account", u->Account() ? u->Account()->GetDisplay() : "");
query.SetValue("fingerprint", u->fingerprint);
query.SetValue("signon", u->signon);
query.SetValue("server", u->server->GetName());
@@ -167,7 +167,7 @@ void IRC2SQL::OnUserLogin(User *u)
{
query = "UPDATE `" + prefix + "user` SET account=@account@ WHERE nick=@nick@";
query.SetValue("nick", u->nick);
- query.SetValue("account", u->Account() ? u->Account()->display : "");
+ query.SetValue("account", u->Account() ? u->Account()->GetDisplay() : "");
this->RunQuery(query);
}
@@ -221,7 +221,7 @@ void IRC2SQL::OnJoinChannel(User *u, Channel *c)
this->RunQuery(query);
}
-EventReturn IRC2SQL::OnChannelModeSet(Channel *c, MessageSource &setter, ChannelMode *mode, const Anope::string &param)
+EventReturn IRC2SQL::OnChannelModeSet(Channel *c, const MessageSource &setter, ChannelMode *mode, const Anope::string &param)
{
query = "UPDATE `" + prefix + "chan` SET modes=@modes@ WHERE channel=@channel@";
query.SetValue("channel", c->name);
@@ -230,7 +230,7 @@ EventReturn IRC2SQL::OnChannelModeSet(Channel *c, MessageSource &setter, Channel
return EVENT_CONTINUE;
}
-EventReturn IRC2SQL::OnChannelModeUnset(Channel *c, MessageSource &setter, ChannelMode *mode, const Anope::string &param)
+EventReturn IRC2SQL::OnChannelModeUnset(Channel *c, const MessageSource &setter, ChannelMode *mode, const Anope::string &param)
{
this->OnChannelModeSet(c, setter, mode, param);
return EVENT_CONTINUE;
@@ -264,7 +264,7 @@ void IRC2SQL::OnTopicUpdated(User *source, Channel *c, const Anope::string &user
this->RunQuery(query);
}
-void IRC2SQL::OnBotNotice(User *u, BotInfo *bi, Anope::string &message)
+void IRC2SQL::OnBotNotice(User *u, ServiceBot *bi, Anope::string &message)
{
Anope::string versionstr;
if (bi != StatServ)
diff --git a/modules/extra/stats/irc2sql/irc2sql.h b/modules/extra/stats/irc2sql/irc2sql.h
index a7396fcf0..b73e95cf1 100644
--- a/modules/extra/stats/irc2sql/irc2sql.h
+++ b/modules/extra/stats/irc2sql/irc2sql.h
@@ -6,11 +6,11 @@ class MySQLInterface : public SQL::Interface
public:
MySQLInterface(Module *o) : SQL::Interface(o) { }
- void OnResult(const SQL::Result &r) anope_override
+ void OnResult(const SQL::Result &r) override
{
}
- void OnError(const SQL::Result &r) anope_override
+ void OnError(const SQL::Result &r) override
{
if (!r.GetQuery().query.empty())
Log(LOG_DEBUG) << "m_irc2sql: Error executing query " << r.finished_query << ": " << r.GetError();
@@ -27,7 +27,7 @@ class IRC2SQL : public Module
std::vector<Anope::string> TableList, ProcedureList, EventList;
Anope::string prefix, GeoIPDB;
bool quitting, introduced_myself, ctcpuser, ctcpeob, firstrun;
- BotInfo *StatServ;
+ ServiceBot *StatServ;
PrimitiveExtensibleItem<bool> versionreply;
void RunQuery(const SQL::Query &q);
@@ -48,31 +48,31 @@ class IRC2SQL : public Module
introduced_myself = false;
}
- void OnShutdown() anope_override;
- void OnReload(Configuration::Conf *config) anope_override;
- void OnNewServer(Server *server) anope_override;
- void OnServerQuit(Server *server) anope_override;
- void OnUserConnect(User *u, bool &exempt) anope_override;
- void OnUserQuit(User *u, const Anope::string &msg) anope_override;
- void OnUserNickChange(User *u, const Anope::string &oldnick) anope_override;
- void OnUserAway(User *u, const Anope::string &message) anope_override;
- void OnFingerprint(User *u) anope_override;
- void OnUserModeSet(const MessageSource &setter, User *u, const Anope::string &mname) anope_override;
- void OnUserModeUnset(const MessageSource &setter, User *u, const Anope::string &mname) anope_override;
- void OnUserLogin(User *u) anope_override;
- void OnNickLogout(User *u) anope_override;
- void OnSetDisplayedHost(User *u) anope_override;
+ void OnShutdown() override;
+ void OnReload(Configuration::Conf *config) override;
+ void OnNewServer(Server *server) override;
+ void OnServerQuit(Server *server) override;
+ void OnUserConnect(User *u, bool &exempt) override;
+ void OnUserQuit(User *u, const Anope::string &msg) override;
+ void OnUserNickChange(User *u, const Anope::string &oldnick) override;
+ void OnUserAway(User *u, const Anope::string &message) override;
+ void OnFingerprint(User *u) override;
+ void OnUserModeSet(const MessageSource &setter, User *u, const Anope::string &mname) override;
+ void OnUserModeUnset(const MessageSource &setter, User *u, const Anope::string &mname) override;
+ void OnUserLogin(User *u) override;
+ void OnNickLogout(User *u) override;
+ void OnSetDisplayedHost(User *u) override;
- void OnChannelCreate(Channel *c) anope_override;
- void OnChannelDelete(Channel *c) anope_override;
- void OnLeaveChannel(User *u, Channel *c) anope_override;
- void OnJoinChannel(User *u, Channel *c) anope_override;
- EventReturn OnChannelModeSet(Channel *c, MessageSource &setter, ChannelMode *mode, const Anope::string &param) anope_override;
- EventReturn OnChannelModeUnset(Channel *c, MessageSource &setter, ChannelMode *mode, const Anope::string &param) anope_override;
+ void OnChannelCreate(Channel *c) override;
+ void OnChannelDelete(Channel *c) override;
+ void OnLeaveChannel(User *u, Channel *c) override;
+ void OnJoinChannel(User *u, Channel *c) override;
+ EventReturn OnChannelModeSet(Channel *c, const MessageSource &setter, ChannelMode *mode, const Anope::string &param) override;
+ EventReturn OnChannelModeUnset(Channel *c, const MessageSource &setter, ChannelMode *mode, const Anope::string &param) override;
- void OnTopicUpdated(User *source, Channel *c, const Anope::string &user, const Anope::string &topic) anope_override;
+ void OnTopicUpdated(User *source, Channel *c, const Anope::string &user, const Anope::string &topic) override;
- void OnBotNotice(User *u, BotInfo *bi, Anope::string &message) anope_override;
+ void OnBotNotice(User *u, ServiceBot *bi, Anope::string &message) override;
};
diff --git a/modules/fantasy.cpp b/modules/fantasy.cpp
index 023ef286f..20585f527 100644
--- a/modules/fantasy.cpp
+++ b/modules/fantasy.cpp
@@ -1,15 +1,25 @@
-/* Fantasy functionality
+/*
+ * Anope IRC Services
*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2013-2016 Anope Team <team@anope.org>
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
*
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "module.h"
+#include "modules/fantasy.h"
+#include "modules/botserv/info.h"
class CommandBSSetFantasy : public Command
{
@@ -20,20 +30,20 @@ class CommandBSSetFantasy : public Command
this->SetSyntax(_("\037channel\037 {\037ON|OFF\037}"));
}
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
{
- ChannelInfo *ci = ChannelInfo::Find(params[0]);
+ ChanServ::Channel *ci = ChanServ::Find(params[0]);
const Anope::string &value = params[1];
if (ci == NULL)
{
- source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
+ source.Reply(_("Channel \002{0}\002 isn't registered."), params[0]);
return;
}
if (!source.HasPriv("botserv/administration") && !source.AccessFor(ci).HasPriv("SET"))
{
- source.Reply(ACCESS_DENIED);
+ source.Reply(_("Access denied. You do not have the \002{0}\002 privilege on \002{1}\002."), "SET", ci->GetName());
return;
}
@@ -46,24 +56,24 @@ class CommandBSSetFantasy : public Command
if (value.equals_ci("ON"))
{
bool override = !source.AccessFor(ci).HasPriv("SET");
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to enable fantasy";
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to enable fantasy";
- ci->Extend<bool>("BS_FANTASY");
- source.Reply(_("Fantasy mode is now \002on\002 on channel %s."), ci->name.c_str());
+ ci->SetS<bool>("BS_FANTASY", true);
+ source.Reply(_("Fantasy mode is now \002on\002 on channel \002{0}\002."), ci->GetName());
}
else if (value.equals_ci("OFF"))
{
bool override = !source.AccessFor(ci).HasPriv("SET");
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to disable fantasy";
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to disable fantasy";
- ci->Shrink<bool>("BS_FANTASY");
- source.Reply(_("Fantasy mode is now \002off\002 on channel %s."), ci->name.c_str());
+ ci->UnsetS<bool>("BS_FANTASY");
+ source.Reply(_("Fantasy mode is now \002off\002 on channel \002{0}\002."), ci->GetName());
}
else
this->OnSyntaxError(source, source.command);
}
- bool OnHelp(CommandSource &source, const Anope::string &) anope_override
+ bool OnHelp(CommandSource &source, const Anope::string &) override
{
this->SendSyntax(source);
source.Reply(_(" \n"
@@ -75,26 +85,31 @@ class CommandBSSetFantasy : public Command
"Note that users wanting to use fantaisist\n"
"commands MUST have enough access for both\n"
"the FANTASIA and the command they are executing."),
- Config->GetModule(this->owner)->Get<const Anope::string>("fantasycharacter", "!").c_str());
+ Config->GetModule(this->GetOwner())->Get<Anope::string>("fantasycharacter", "!").c_str());
return true;
}
};
class Fantasy : public Module
+ , public EventHook<Event::Privmsg>
+ , public EventHook<Event::ServiceBotEvent>
{
- SerializableExtensibleItem<bool> fantasy;
+ Serialize::Field<ChanServ::Channel, bool> fantasy;
CommandBSSetFantasy commandbssetfantasy;
public:
- Fantasy(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- fantasy(this, "BS_FANTASY"), commandbssetfantasy(this)
+ Fantasy(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , EventHook<Event::Privmsg>(this)
+ , EventHook<Event::ServiceBotEvent>(this)
+ , fantasy(this, "BS_FANTASY")
+ , commandbssetfantasy(this)
{
}
- void OnPrivmsg(User *u, Channel *c, Anope::string &msg) anope_override
+ void OnPrivmsg(User *u, Channel *c, Anope::string &msg) override
{
- if (!u || !c || !c->ci || !c->ci->bi || msg.empty() || msg[0] == '\1')
+ if (!u || !c || !c->ci || !c->ci->GetBot() || msg.empty() || msg[0] == '\1')
return;
if (Config->GetClient("BotServ") && !fantasy.HasExt(c->ci))
@@ -109,7 +124,7 @@ class Fantasy : public Module
Anope::string normalized_param0 = Anope::NormalizeBuffer(params[0]);
Anope::string fantasy_chars = Config->GetModule(this)->Get<Anope::string>("fantasycharacter", "!");
- if (!normalized_param0.find(c->ci->bi->nick))
+ if (!normalized_param0.find(c->ci->GetBot()->nick))
{
params.erase(params.begin());
}
@@ -146,7 +161,7 @@ class Fantasy : public Module
return;
const CommandInfo &info = it->second;
- ServiceReference<Command> cmd("Command", info.name);
+ ServiceReference<Command> cmd(info.name);
if (!cmd)
{
Log(LOG_DEBUG) << "Fantasy command " << it->first << " exists for non-existent service " << info.name << "!";
@@ -173,22 +188,22 @@ class Fantasy : public Module
if (params.size() < cmd->min_params)
return;
- CommandSource source(u->nick, u, u->Account(), u, c->ci->bi);
+ CommandSource source(u->nick, u, u->Account(), u, c->ci->GetBot());
source.c = c;
source.command = it->first;
source.permission = info.permission;
- AccessGroup ag = c->ci->AccessFor(u);
+ ChanServ::AccessGroup ag = c->ci->AccessFor(u);
bool has_fantasia = ag.HasPriv("FANTASIA") || source.HasPriv("botserv/fantasy");
EventReturn MOD_RESULT;
if (has_fantasia)
{
- FOREACH_RESULT(OnBotFantasy, MOD_RESULT, (source, cmd, c->ci, params));
+ MOD_RESULT = EventManager::Get()->Dispatch(&Event::BotFantasy::OnBotFantasy, source, cmd, c->ci, params);
}
else
{
- FOREACH_RESULT(OnBotNoFantasyAccess, MOD_RESULT, (source, cmd, c->ci, params));
+ MOD_RESULT = EventManager::Get()->Dispatch(&Event::BotNoFantasyAccess::OnBotNoFantasyAccess, source, cmd, c->ci, params);
}
if (MOD_RESULT == EVENT_STOP || !has_fantasia)
@@ -197,18 +212,18 @@ class Fantasy : public Module
if (MOD_RESULT != EVENT_ALLOW && !info.permission.empty() && !source.HasCommand(info.permission))
return;
- FOREACH_RESULT(OnPreCommand, MOD_RESULT, (source, cmd, params));
+ MOD_RESULT = EventManager::Get()->Dispatch(&Event::PreCommand::OnPreCommand, source, cmd, params);
if (MOD_RESULT == EVENT_STOP)
return;
- Reference<NickCore> nc_reference(u->Account());
+ Reference<NickServ::Account> nc_reference(u->Account());
cmd->Execute(source, params);
if (!nc_reference)
source.nc = NULL;
- FOREACH_MOD(OnPostCommand, (source, cmd, params));
+ EventManager::Get()->Dispatch(&Event::PostCommand::OnPostCommand, source, cmd, params);
}
- void OnBotInfo(CommandSource &source, BotInfo *bi, ChannelInfo *ci, InfoFormatter &info)
+ void OnServiceBot(CommandSource &source, ServiceBot *bi, ChanServ::Channel *ci, InfoFormatter &info) override
{
if (fantasy.HasExt(ci))
info.AddOption(_("Fantasy"));
diff --git a/modules/global/CMakeLists.txt b/modules/global/CMakeLists.txt
new file mode 100644
index 000000000..cd225a94d
--- /dev/null
+++ b/modules/global/CMakeLists.txt
@@ -0,0 +1 @@
+build_modules(${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/modules/global/global.cpp b/modules/global/global.cpp
new file mode 100644
index 000000000..af4add942
--- /dev/null
+++ b/modules/global/global.cpp
@@ -0,0 +1,68 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/global.h"
+
+class CommandGLGlobal : public Command
+{
+ ServiceReference<Global::GlobalService> service;
+
+ public:
+ CommandGLGlobal(Module *creator) : Command(creator, "global/global", 1, 1)
+ {
+ this->SetDesc(_("Send a message to all users"));
+ this->SetSyntax(_("\037message\037"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &msg = params[0];
+
+ if (!service)
+ {
+ source.Reply("No global reference, is global loaded?");
+ return;
+ }
+
+ Log(LOG_ADMIN, source, this);
+ service->SendGlobal(NULL, source.GetNick(), msg);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Allows Services Operators to send a message to all users on the network."
+ " The message will be sent from \002{0}\002."), source.service->nick);
+ return true;
+ }
+};
+
+class GLGlobal : public Module
+{
+ CommandGLGlobal commandglglobal;
+
+ public:
+ GLGlobal(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandglglobal(this)
+ {
+
+ }
+};
+
+MODULE_INIT(GLGlobal)
diff --git a/modules/global/main/CMakeLists.txt b/modules/global/main/CMakeLists.txt
new file mode 100644
index 000000000..781f0ef1f
--- /dev/null
+++ b/modules/global/main/CMakeLists.txt
@@ -0,0 +1 @@
+build_subdir(${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/modules/global/main/global.cpp b/modules/global/main/global.cpp
new file mode 100644
index 000000000..cbc0e0757
--- /dev/null
+++ b/modules/global/main/global.cpp
@@ -0,0 +1,119 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2011-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/help.h"
+#include "modules/global.h"
+
+class GlobalCore : public Module
+ , public Global::GlobalService
+ , public EventHook<Event::Restart>
+ , public EventHook<Event::Shutdown>
+ , public EventHook<Event::NewServer>
+ , public EventHook<Event::Help>
+{
+ Reference<ServiceBot> Global;
+
+ void ServerGlobal(ServiceBot *sender, Server *s, const Anope::string &message)
+ {
+ if (s != Me && !s->IsJuped())
+ s->Notice(sender, message);
+ for (unsigned i = 0, j = s->GetLinks().size(); i < j; ++i)
+ this->ServerGlobal(sender, s->GetLinks()[i], message);
+ }
+
+ public:
+ GlobalCore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, PSEUDOCLIENT | VENDOR)
+ , EventHook<Event::Restart>(this)
+ , EventHook<Event::Shutdown>(this)
+ , EventHook<Event::NewServer>(this)
+ , EventHook<Event::Help>(this)
+ , GlobalService(this)
+ {
+ }
+
+ void SendGlobal(ServiceBot *sender, const Anope::string &source, const Anope::string &message) override
+ {
+ if (Me->GetLinks().empty())
+ return;
+ if (!sender)
+ sender = Global;
+ if (!sender)
+ return;
+
+ Anope::string rmessage;
+
+ if (!source.empty() && !Config->GetModule("global")->Get<bool>("anonymousglobal"))
+ rmessage = "[" + source + "] " + message;
+ else
+ rmessage = message;
+
+ this->ServerGlobal(sender, Servers::GetUplink(), rmessage);
+ }
+
+ void OnReload(Configuration::Conf *conf) override
+ {
+ const Anope::string &glnick = conf->GetModule(this)->Get<Anope::string>("client");
+
+ if (glnick.empty())
+ throw ConfigException(Module::name + ": <client> must be defined");
+
+ ServiceBot *bi = ServiceBot::Find(glnick, true);
+ if (!bi)
+ throw ConfigException(Module::name + ": no bot named " + glnick);
+
+ Global = bi;
+ }
+
+ void OnRestart() override
+ {
+ const Anope::string &gl = Config->GetModule(this)->Get<Anope::string>("globaloncycledown");
+ if (!gl.empty())
+ this->SendGlobal(Global, "", gl);
+ }
+
+ void OnShutdown() override
+ {
+ const Anope::string &gl = Config->GetModule(this)->Get<Anope::string>("globaloncycledown");
+ if (!gl.empty())
+ this->SendGlobal(Global, "", gl);
+ }
+
+ void OnNewServer(Server *s) override
+ {
+ const Anope::string &gl = Config->GetModule(this)->Get<Anope::string>("globaloncycleup");
+ if (!gl.empty() && !Me->IsSynced())
+ s->Notice(Global, gl);
+ }
+
+ EventReturn OnPreHelp(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ if (!params.empty() || source.c || source.service != *Global)
+ return EVENT_CONTINUE;
+ source.Reply(_("%s commands:"), Global->nick.c_str());
+ return EVENT_CONTINUE;
+ }
+
+ void OnPostHelp(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ }
+};
+
+MODULE_INIT(GlobalCore)
+
diff --git a/modules/greet.cpp b/modules/greet.cpp
new file mode 100644
index 000000000..d43becd16
--- /dev/null
+++ b/modules/greet.cpp
@@ -0,0 +1,223 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2013-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/nickserv/info.h"
+#include "modules/botserv/info.h"
+#include "modules/nickserv/set.h"
+
+class CommandBSSetGreet : public Command
+{
+ public:
+ CommandBSSetGreet(Module *creator, const Anope::string &sname = "botserv/set/greet") : Command(creator, sname, 2, 2)
+ {
+ this->SetDesc(_("Enable greet messages"));
+ this->SetSyntax(_("\037channel\037 {\037ON|OFF\037}"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &chan = params[0];
+ const Anope::string &value = params[1];
+
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ ChanServ::Channel *ci = ChanServ::Find(chan);
+ if (ci == NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
+ return;
+ }
+
+ if (!source.HasPriv("botserv/administration") && !source.AccessFor(ci).HasPriv("SET"))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "SET", ci->GetName());
+ return;
+ }
+
+ if (value.equals_ci("ON"))
+ {
+ bool override = !source.AccessFor(ci).HasPriv("SET");
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to enable greets";
+
+ ci->SetS<bool>("BS_GREET", true);
+ source.Reply(_("Greet mode for \002{0}\002 is now \002on\002."), ci->GetName());
+ }
+ else if (value.equals_ci("OFF"))
+ {
+ bool override = !source.AccessFor(ci).HasPriv("SET");
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to disable greets";
+
+ ci->UnsetS<bool>("BS_GREET");
+ source.Reply(_("Greet mode for \002{0}\002 is now \002off\002."), ci->GetName());
+ }
+ else
+ this->OnSyntaxError(source, source.command);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
+ {
+ source.Reply(_("Enables or disables \002greet\002 mode on \037channel\037."
+ " When \002greet\002 mode is enabled, the assigned service bot will display the greet messages of users joining the channel, if they have the \002{0}\002 privilege."),
+ "GREET");
+ return true;
+ }
+};
+
+class CommandNSSetGreet : public Command
+{
+ public:
+ CommandNSSetGreet(Module *creator, const Anope::string &sname = "nickserv/set/greet", size_t min = 0) : Command(creator, sname, min, min + 1)
+ {
+ this->SetDesc(_("Associate a greet message with your nickname"));
+ this->SetSyntax(_("\037message\037"));
+ }
+
+ void Run(CommandSource &source, const Anope::string &user, const Anope::string &param)
+ {
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ NickServ::Nick *na = NickServ::FindNick(user);
+ if (!na)
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), user);
+ return;
+ }
+ NickServ::Account *nc = na->GetAccount();
+
+ EventReturn MOD_RESULT = EventManager::Get()->Dispatch(&Event::SetNickOption::OnSetNickOption, source, this, nc, param);
+ if (MOD_RESULT == EVENT_STOP)
+ return;
+
+ if (!param.empty())
+ {
+ Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to change the greet of " << nc->GetDisplay();
+ nc->SetS<Anope::string>("greet", param);
+ source.Reply(_("Greet message for \002{0}\002 changed to \002{1}\002."), nc->GetDisplay(), param);
+ }
+ else
+ {
+ Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to unset the greet of " << nc->GetDisplay();
+ nc->UnsetS<Anope::string>("greet");
+ source.Reply(_("Greet message for \002{0}\002 unset."), nc->GetDisplay());
+ }
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ this->Run(source, source.nc->GetDisplay(), params.size() > 0 ? params[0] : "");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
+ {
+ source.Reply(_("Sets your greet to \037message\037. Your greet is displayed when you enter channels that have the greet option enabled if you have the \002{0}\002 privilege on the channel."),
+ "GREET");
+ return true;
+ }
+};
+
+class CommandNSSASetGreet : public CommandNSSetGreet
+{
+ public:
+ CommandNSSASetGreet(Module *creator) : CommandNSSetGreet(creator, "nickserv/saset/greet", 1)
+ {
+ this->ClearSyntax();
+ this->SetSyntax(_("\037user\037 \037message\037"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ this->Run(source, params[0], params.size() > 1 ? params[1] : "");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
+ {
+ source.Reply(_("Sets the greet for \037user\037 to \037message\037. Greet messages are displayed when users enter channels that have the greet option enabiled if they have the \002{0}\002 privilege on the channel."),
+ "GREET");
+ return true;
+ }
+};
+
+class Greet : public Module
+ , public EventHook<Event::JoinChannel>
+ , public EventHook<Event::NickInfo>
+ , public EventHook<Event::ServiceBotEvent>
+{
+ /* channel setting for whether or not greet should be shown */
+ Serialize::Field<ChanServ::Channel, bool> bs_greet;
+ /* user greets */
+ Serialize::Field<NickServ::Account, Anope::string> ns_greet;
+
+ CommandBSSetGreet commandbssetgreet;
+ CommandNSSetGreet commandnssetgreet;
+ CommandNSSASetGreet commandnssasetgreet;
+
+ public:
+ Greet(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , EventHook<Event::JoinChannel>(this)
+ , EventHook<Event::NickInfo>(this)
+ , EventHook<Event::ServiceBotEvent>(this)
+ , bs_greet(this, "BS_GREET")
+ , ns_greet(this, "greet")
+ , commandbssetgreet(this)
+ , commandnssetgreet(this)
+ , commandnssasetgreet(this)
+ {
+ }
+
+ void OnJoinChannel(User *user, Channel *c) override
+ {
+ /* Only display the greet if the main uplink we're connected
+ * to has synced, or we'll get greet-floods when the net
+ * recovers from a netsplit. -GD
+ */
+ if (!c->ci || !c->ci->GetBot() || !user->server->IsSynced() || !user->Account())
+ return;
+
+ Anope::string greet = ns_greet.Get(user->Account());
+ if (bs_greet.HasExt(c->ci) && !greet.empty() && c->FindUser(c->ci->GetBot()) && c->ci->AccessFor(user).HasPriv("GREET"))
+ {
+ IRCD->SendPrivmsg(c->ci->GetBot(), c->name, "[%s] %s", user->Account()->GetDisplay().c_str(), greet.c_str());
+ c->ci->GetBot()->lastmsg = Anope::CurTime;
+ }
+ }
+
+ void OnNickInfo(CommandSource &source, NickServ::Nick *na, InfoFormatter &info, bool show_hidden) override
+ {
+ Anope::string greet = ns_greet.Get(na->GetAccount());
+ if (!greet.empty())
+ info[_("Greet")] = greet;
+ }
+
+ void OnServiceBot(CommandSource &source, ServiceBot *bi, ChanServ::Channel *ci, InfoFormatter &info) override
+ {
+ if (bs_greet.HasExt(ci))
+ info.AddOption(_("Greet"));
+ }
+};
+
+MODULE_INIT(Greet)
diff --git a/modules/commands/help.cpp b/modules/help.cpp
index f6e459bde..b032d11a1 100644
--- a/modules/commands/help.cpp
+++ b/modules/help.cpp
@@ -1,20 +1,27 @@
-/* Core functions
+/*
+ * Anope IRC Services
*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2011-2016 Anope Team <team@anope.org>
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
*
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "module.h"
+#include "modules/help.h"
class CommandHelp : public Command
{
- static const unsigned help_wrap_len = 40;
-
static CommandGroup *FindGroup(const Anope::string &name)
{
for (unsigned i = 0; i < Config->CommandGroups.size(); ++i)
@@ -34,15 +41,14 @@ class CommandHelp : public Command
this->AllowUnregistered(true);
}
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
{
- EventReturn MOD_RESULT;
- FOREACH_RESULT(OnPreHelp, MOD_RESULT, (source, params));
+ EventReturn MOD_RESULT = EventManager::Get()->Dispatch(&Event::Help::OnPreHelp, source, params);
if (MOD_RESULT == EVENT_STOP)
return;
-
+
Anope::string source_command = source.command;
- const BotInfo *bi = source.service;
+ const ServiceBot *bi = source.service;
const CommandInfo::map &map = source.c ? Config->Fantasy : bi->commands;
bool hide_privileged_commands = Config->GetBlock("options")->Get<bool>("hideprivilegedcommands"),
hide_registered_commands = Config->GetBlock("options")->Get<bool>("hideregisteredcommands");
@@ -54,7 +60,7 @@ class CommandHelp : public Command
GroupInfo groups;
if (all)
- source.Reply(_("All available commands for \002%s\002:"), source.service->nick.c_str());
+ source.Reply(_("All available commands for \002{0}\002:"), source.service->nick);
for (CommandInfo::map::const_iterator it = map.begin(), it_end = map.end(); it != it_end; ++it)
{
@@ -70,7 +76,7 @@ class CommandHelp : public Command
if (cmd != it->first && map.count(cmd))
continue;
- ServiceReference<Command> c("Command", info.name);
+ ServiceReference<Command> c(info.name);
if (!c)
continue;
@@ -100,31 +106,21 @@ class CommandHelp : public Command
CommandGroup *gr = it->first;
source.Reply(" ");
- source.Reply("%s", gr->description.c_str());
+ source.Reply("{0}", gr->description);
Anope::string buf;
for (std::list<Anope::string>::iterator it2 = it->second.begin(), it2_end = it->second.end(); it2 != it2_end; ++it2)
{
const Anope::string &c_name = *it2;
-
buf += ", " + c_name;
-
- if (buf.length() > help_wrap_len)
- {
- source.Reply(" %s", buf.substr(2).c_str());
- buf.clear();
- }
}
if (buf.length() > 2)
- {
- source.Reply(" %s", buf.substr(2).c_str());
- buf.clear();
- }
+ source.Reply(" {0}", buf.substr(2));
}
if (!groups.empty())
{
source.Reply(" ");
- source.Reply(_("Use the \002%s ALL\002 command to list all commands and their descriptions."), source_command.c_str());
+ source.Reply(_("Use the \002{0} ALL\002 command to list all commands and their descriptions."), source_command);
}
}
else
@@ -143,17 +139,19 @@ class CommandHelp : public Command
const CommandInfo &info = it->second;
- ServiceReference<Command> c("Command", info.name);
+ ServiceReference<Command> c(info.name);
if (!c)
continue;
if (hide_privileged_commands && !info.permission.empty() && !source.HasCommand(info.permission))
continue;
-
+
// Allow unregistered users to see help for commands that they explicitly request help for
const Anope::string &subcommand = params.size() > max ? params[max] : "";
- source.command = full_command;
+ source.command = it->first;
+
+ c->SendSyntax(source);
if (!c->OnHelp(source, subcommand))
continue;
@@ -163,7 +161,7 @@ class CommandHelp : public Command
if (!info.permission.empty())
{
source.Reply(" ");
- source.Reply(_("Access to this command requires the permission \002%s\002 to be present in your opertype."), info.permission.c_str());
+ source.Reply(_("Access to this command requires the permission \002{0}\002 to be present in your opertype."), info.permission);
}
if (!c->AllowUnregistered() && !source.nc)
{
@@ -181,12 +179,10 @@ class CommandHelp : public Command
}
if (helped == false)
- source.Reply(_("No help available for \002%s\002."), params[0].c_str());
+ source.Reply(_("No help available for \002{0}\002."), params[0]);
}
-
- FOREACH_MOD(OnPostHelp, (source, params));
- return;
+ EventManager::Get()->Dispatch(&Event::Help::OnPostHelp, source, params);
}
};
@@ -195,8 +191,8 @@ class Help : public Module
CommandHelp commandhelp;
public:
- Help(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandhelp(this)
+ Help(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandhelp(this)
{
}
diff --git a/modules/helpchan.cpp b/modules/helpchan.cpp
new file mode 100644
index 000000000..294677a3f
--- /dev/null
+++ b/modules/helpchan.cpp
@@ -0,0 +1,45 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2010-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+
+class HelpChannel : public Module
+ , public EventHook<Event::ChannelModeSet>
+{
+ public:
+ HelpChannel(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , EventHook<Event::ChannelModeSet>(this)
+ {
+ }
+
+ EventReturn OnChannelModeSet(Channel *c, const MessageSource &, ChannelMode *mode, const Anope::string &param) override
+ {
+ if (mode->name == "OP" && c && c->ci && c->name.equals_ci(Config->GetModule(this)->Get<Anope::string>("helpchannel")))
+ {
+ User *u = User::Find(param);
+
+ if (u && c->ci->AccessFor(u).HasPriv("OPME"))
+ u->SetMode(Config->GetClient("OperServ"), "HELPOP");
+ }
+
+ return EVENT_CONTINUE;
+ }
+};
+
+MODULE_INIT(HelpChannel)
diff --git a/modules/hostserv/CMakeLists.txt b/modules/hostserv/CMakeLists.txt
new file mode 100644
index 000000000..cd225a94d
--- /dev/null
+++ b/modules/hostserv/CMakeLists.txt
@@ -0,0 +1 @@
+build_modules(${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/modules/hostserv/del.cpp b/modules/hostserv/del.cpp
new file mode 100644
index 000000000..bc9b08533
--- /dev/null
+++ b/modules/hostserv/del.cpp
@@ -0,0 +1,111 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/hostserv/del.h"
+
+class CommandHSDel : public Command
+{
+ public:
+ CommandHSDel(Module *creator) : Command(creator, "hostserv/del", 1, 1)
+ {
+ this->SetDesc(_("Delete the vhost of another user"));
+ this->SetSyntax(_("\037user\037"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ if (Anope::ReadOnly)
+ source.Reply(_("Services are in read-only mode. Any changes made may not persist."));
+
+ const Anope::string &nick = params[0];
+ NickServ::Nick *na = NickServ::FindNick(nick);
+ if (!na)
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), nick);
+ return;
+ }
+
+ Log(LOG_ADMIN, source, this) << "for user " << na->GetNick();
+ EventManager::Get()->Dispatch(&Event::DeleteVhost::OnDeleteVhost, na);
+ na->RemoveVhost();
+ source.Reply(_("Vhost for \002{0}\002 has been removed."), na->GetNick());
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Removes the vhost of \037user\037."));
+ return true;
+ }
+};
+
+class CommandHSDelAll : public Command
+{
+ public:
+ CommandHSDelAll(Module *creator) : Command(creator, "hostserv/delall", 1, 1)
+ {
+ this->SetDesc(_("Delete the vhost for all nicks in a group"));
+ this->SetSyntax(_("\037group\037"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ if (Anope::ReadOnly)
+ source.Reply(_("Services are in read-only mode. Any changes made may not persist."));
+
+ const Anope::string &nick = params[0];
+ NickServ::Nick *na = NickServ::FindNick(nick);
+ if (!na)
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), nick);
+ return;
+ }
+
+ EventManager::Get()->Dispatch(&Event::DeleteVhost::OnDeleteVhost, na);
+
+ NickServ::Account *nc = na->GetAccount();
+ for (NickServ::Nick *na2 : nc->GetRefs<NickServ::Nick *>())
+ na2->RemoveVhost();
+ Log(LOG_ADMIN, source, this) << "for all nicks in group " << nc->GetDisplay();
+ source.Reply(_("Vhosts for group \002{0}\002 have been removed."), nc->GetDisplay());
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Removes the vhost of all nicks in the group \037group\037."));
+ return true;
+ }
+};
+
+class HSDel : public Module
+{
+ CommandHSDel commandhsdel;
+ CommandHSDelAll commandhsdelall;
+
+ public:
+ HSDel(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandhsdel(this)
+ , commandhsdelall(this)
+ {
+ if (!IRCD || !IRCD->CanSetVHost)
+ throw ModuleException("Your IRCd does not support vhosts");
+ }
+};
+
+MODULE_INIT(HSDel)
diff --git a/modules/hostserv/group.cpp b/modules/hostserv/group.cpp
new file mode 100644
index 000000000..8113435fa
--- /dev/null
+++ b/modules/hostserv/group.cpp
@@ -0,0 +1,127 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/nickserv/group.h"
+
+class CommandHSGroup : public Command
+{
+ bool setting;
+
+ public:
+ void Sync(NickServ::Nick *na)
+ {
+ if (setting)
+ return;
+
+ if (!na || !na->HasVhost())
+ return;
+
+ setting = true;
+ for (NickServ::Nick *nick : na->GetAccount()->GetRefs<NickServ::Nick *>())
+ {
+ nick->SetVhost(na->GetVhostIdent(), na->GetVhostHost(), na->GetVhostCreator());
+ EventManager::Get()->Dispatch(&Event::SetVhost::OnSetVhost, nick);
+ }
+ setting = false;
+ }
+
+ CommandHSGroup(Module *creator) : Command(creator, "hostserv/group", 0, 0), setting(false)
+ {
+ this->SetDesc(_("Syncs the vhost for all nicks in a group"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ NickServ::Nick *na = NickServ::FindNick(source.GetNick());
+ if (!na || na->GetAccount() != source.GetAccount())
+ {
+ source.Reply(_("Access denied."));
+ return;
+ }
+
+ if (!na->HasVhost())
+ {
+ source.Reply(_("There is no vhost assigned to this nickname."));
+ return;
+ }
+
+ this->Sync(na);
+ if (!na->GetVhostIdent().empty())
+ source.Reply(_("All vhosts in the group \002{0}\002 have been set to \002{1}\002@\002{2}\002."), source.nc->GetDisplay(), na->GetVhostIdent(), na->GetVhostHost());
+ else
+ source.Reply(_("All vhosts in the group \002{0}\002 have been set to \002{1}\002."), source.nc->GetDisplay(), na->GetVhostHost());
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Sets the vhost of all nicks in your group to the vhost of your current nick."));
+ return true;
+ }
+};
+
+class HSGroup : public Module
+ , public EventHook<Event::SetVhost>
+ , public EventHook<Event::NickGroup>
+{
+ CommandHSGroup commandhsgroup;
+ bool syncongroup;
+ bool synconset;
+
+ public:
+ HSGroup(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , EventHook<Event::SetVhost>(this)
+ , EventHook<Event::NickGroup>(this)
+ , commandhsgroup(this)
+ {
+ if (!IRCD || !IRCD->CanSetVHost)
+ throw ModuleException("Your IRCd does not support vhosts");
+ }
+
+ void OnSetVhost(NickServ::Nick *na) override
+ {
+ if (!synconset)
+ return;
+
+ commandhsgroup.Sync(na);
+ }
+
+ void OnNickGroup(User *u, NickServ::Nick *na) override
+ {
+ if (!syncongroup)
+ return;
+
+ commandhsgroup.Sync(na);
+ }
+
+ void OnReload(Configuration::Conf *conf) override
+ {
+ Configuration::Block *block = conf->GetModule(this);
+ syncongroup = block->Get<bool>("syncongroup");
+ synconset = block->Get<bool>("synconset");
+ }
+};
+
+MODULE_INIT(HSGroup)
diff --git a/modules/commands/hs_list.cpp b/modules/hostserv/list.cpp
index a5258b04c..cdaf72a37 100644
--- a/modules/commands/hs_list.cpp
+++ b/modules/hostserv/list.cpp
@@ -1,12 +1,20 @@
-/* HostServ core functions
+/*
+ * Anope IRC Services
*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
*
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "module.h"
@@ -20,7 +28,7 @@ class CommandHSList : public Command
this->SetSyntax(_("[\037key\037|\037#X-Y\037]"));
}
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
{
const Anope::string &key = !params.empty() ? params[0] : "";
int from = 0, to = 0, counter = 1;
@@ -34,14 +42,14 @@ class CommandHSList : public Command
size_t tmp = key.find('-');
if (tmp == Anope::string::npos || tmp == key.length() || tmp == 1)
{
- source.Reply(LIST_INCORRECT_RANGE);
+ source.Reply(_("Incorrect range specified. The correct syntax is \002#\037from\037-\037to\037\002."));
return;
}
for (unsigned i = 1, end = key.length(); i < end; ++i)
{
if (!isdigit(key[i]) && i != tmp)
{
- source.Reply(LIST_INCORRECT_RANGE);
+ source.Reply(_("Incorrect range specified. The correct syntax is \002#\037from\037-\037to\037\002."));
return;
}
try
@@ -53,26 +61,24 @@ class CommandHSList : public Command
}
}
- unsigned display_counter = 0, listmax = Config->GetModule(this->owner)->Get<unsigned>("listmax", "50");
+ unsigned display_counter = 0, listmax = Config->GetModule(this->GetOwner())->Get<unsigned>("listmax", "50");
ListFormatter list(source.GetAccount());
list.AddColumn(_("Number")).AddColumn(_("Nick")).AddColumn(_("Vhost")).AddColumn(_("Creator")).AddColumn(_("Created"));
- for (nickalias_map::const_iterator it = NickAliasList->begin(), it_end = NickAliasList->end(); it != it_end; ++it)
+ for (NickServ::Nick *na : NickServ::service->GetNickList())
{
- const NickAlias *na = it->second;
-
if (!na->HasVhost())
continue;
if (!key.empty() && key[0] != '#')
{
- if ((Anope::Match(na->nick, key) || Anope::Match(na->GetVhostHost(), key)) && display_counter < listmax)
+ if ((Anope::Match(na->GetNick(), key) || Anope::Match(na->GetVhostHost(), key)) && display_counter < listmax)
{
++display_counter;
ListFormatter::ListEntry entry;
entry["Number"] = stringify(display_counter);
- entry["Nick"] = na->nick;
+ entry["Nick"] = na->GetNick();
if (!na->GetVhostIdent().empty())
entry["Vhost"] = na->GetVhostIdent() + "@" + na->GetVhostHost();
else
@@ -93,7 +99,7 @@ class CommandHSList : public Command
++display_counter;
ListFormatter::ListEntry entry;
entry["Number"] = stringify(display_counter);
- entry["Nick"] = na->nick;
+ entry["Nick"] = na->GetNick();
if (!na->GetVhostIdent().empty())
entry["Vhost"] = na->GetVhostIdent() + "@" + na->GetVhostHost();
else
@@ -113,13 +119,13 @@ class CommandHSList : public Command
}
if (!key.empty())
- source.Reply(_("Displayed records matching key \002%s\002 (count: \002%d\002)."), key.c_str(), display_counter);
+ source.Reply(_("Displayed records matching key \002{0}\002 (count: \002{1}\002)."), key, display_counter);
else
{
if (from)
- source.Reply(_("Displayed records from \002%d\002 to \002%d\002."), from, to);
+ source.Reply(_("Displayed records from \002{0}\002 to \002{1}\002."), from, to);
else
- source.Reply(_("Displayed all records (count: \002%d\002)."), display_counter);
+ source.Reply(_("Displayed all records (count: \002{0}\002)."), display_counter);
}
std::vector<Anope::string> replies;
@@ -129,17 +135,18 @@ class CommandHSList : public Command
source.Reply(replies[i]);
}
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
{
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("This command lists registered vhosts to the operator.\n"
- "If a \037key\037 is specified, only entries whose nick or vhost match\n"
- "the pattern given in \037key\037 are displayed e.g. Rob* for all\n"
- "entries beginning with \"Rob\"\n"
- "If a \037#X-Y\037 style is used, only entries between the range of \002X\002\n"
- "and \002Y\002 will be displayed, e.g. \002#1-3\002 will display the first 3\n"
- "nick/vhost entries."));
+ source.Reply(_("Lists all vhosts. If \037key\037 is specified, only entries whose nick or vhost match the pattern given in \037key\037 are displayed."
+ "If a \037#X-Y\037 style is used, only entries between the range of \002X\002 and \002Y\002 will be displayed.\n"
+ "\n"
+ "Examples:\n"
+ " {0} Rob*\n"
+ " Lists all entries with the nick or vhost beginning with \"Rob\"\n"
+ "\n"
+ " {0} #1-3\n"
+ " Lists the first three entries."),
+ source.command);
return true;
}
};
@@ -149,8 +156,8 @@ class HSList : public Module
CommandHSList commandhslist;
public:
- HSList(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandhslist(this)
+ HSList(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandhslist(this)
{
if (!IRCD || !IRCD->CanSetVHost)
throw ModuleException("Your IRCd does not support vhosts");
diff --git a/modules/hostserv/main/CMakeLists.txt b/modules/hostserv/main/CMakeLists.txt
new file mode 100644
index 000000000..781f0ef1f
--- /dev/null
+++ b/modules/hostserv/main/CMakeLists.txt
@@ -0,0 +1 @@
+build_subdir(${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/modules/hostserv/main/hostserv.cpp b/modules/hostserv/main/hostserv.cpp
new file mode 100644
index 000000000..c1c9408e4
--- /dev/null
+++ b/modules/hostserv/main/hostserv.cpp
@@ -0,0 +1,148 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2011-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/nickserv/update.h"
+#include "modules/hostserv/del.h"
+#include "modules/help.h"
+
+class HostServCore : public Module
+ , public EventHook<Event::UserLogin>
+ , public EventHook<Event::NickUpdate>
+ , public EventHook<Event::Help>
+ , public EventHook<Event::SetVhost>
+ , public EventHook<Event::DeleteVhost>
+{
+ Reference<ServiceBot> HostServ;
+
+ public:
+ HostServCore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, PSEUDOCLIENT | VENDOR)
+ , EventHook<Event::UserLogin>(this)
+ , EventHook<Event::NickUpdate>(this)
+ , EventHook<Event::Help>(this)
+ , EventHook<Event::SetVhost>(this)
+ , EventHook<Event::DeleteVhost>(this)
+ {
+ if (!IRCD || !IRCD->CanSetVHost)
+ throw ModuleException("Your IRCd does not support vhosts");
+ }
+
+ void OnReload(Configuration::Conf *conf) override
+ {
+ const Anope::string &hsnick = conf->GetModule(this)->Get<Anope::string>("client");
+
+ if (hsnick.empty())
+ throw ConfigException(Module::name + ": <client> must be defined");
+
+ ServiceBot *bi = ServiceBot::Find(hsnick, true);
+ if (!bi)
+ throw ConfigException(Module::name + ": no bot named " + hsnick);
+
+ HostServ = bi;
+ }
+
+ void OnUserLogin(User *u) override
+ {
+ if (!IRCD->CanSetVHost)
+ return;
+
+ NickServ::Nick *na = NickServ::FindNick(u->nick);
+ if (!na || na->GetAccount() != u->Account() || !na->HasVhost())
+ na = NickServ::FindNick(u->Account()->GetDisplay());
+ if (!na || !na->HasVhost())
+ return;
+
+ if (u->vhost.empty() || !u->vhost.equals_cs(na->GetVhostHost()) || (!na->GetVhostIdent().empty() && !u->GetVIdent().equals_cs(na->GetVhostIdent())))
+ {
+ IRCD->SendVhost(u, na->GetVhostIdent(), na->GetVhostHost());
+
+ u->vhost = na->GetVhostHost();
+ u->UpdateHost();
+
+ if (IRCD->CanSetVIdent && !na->GetVhostIdent().empty())
+ u->SetVIdent(na->GetVhostIdent());
+
+ if (HostServ)
+ {
+ if (!na->GetVhostIdent().empty())
+ u->SendMessage(*HostServ, _("Your vhost of \002%s\002@\002%s\002 is now activated."), na->GetVhostIdent().c_str(), na->GetVhostHost().c_str());
+ else
+ u->SendMessage(*HostServ, _("Your vhost of \002%s\002 is now activated."), na->GetVhostHost().c_str());
+ }
+ }
+ }
+
+ void OnNickUpdate(User *u) override
+ {
+ this->OnUserLogin(u);
+ }
+
+ EventReturn OnPreHelp(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ if (!params.empty() || source.c || source.service != *HostServ)
+ return EVENT_CONTINUE;
+ source.Reply(_("%s commands:"), HostServ->nick.c_str());
+ return EVENT_CONTINUE;
+ }
+
+ void OnPostHelp(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ }
+
+ void OnSetVhost(NickServ::Nick *na) override
+ {
+ if (Config->GetModule(this)->Get<bool>("activate_on_set"))
+ {
+ User *u = User::Find(na->GetNick());
+
+ if (u && u->Account() == na->GetAccount())
+ {
+ IRCD->SendVhost(u, na->GetVhostIdent(), na->GetVhostHost());
+
+ u->vhost = na->GetVhostHost();
+ u->UpdateHost();
+
+ if (IRCD->CanSetVIdent && !na->GetVhostIdent().empty())
+ u->SetVIdent(na->GetVhostIdent());
+
+ if (HostServ)
+ {
+ if (!na->GetVhostIdent().empty())
+ u->SendMessage(*HostServ, _("Your vhost of \002%s\002@\002%s\002 is now activated."), na->GetVhostIdent().c_str(), na->GetVhostHost().c_str());
+ else
+ u->SendMessage(*HostServ, _("Your vhost of \002%s\002 is now activated."), na->GetVhostHost().c_str());
+ }
+ }
+ }
+ }
+
+ void OnDeleteVhost(NickServ::Nick *na) override
+ {
+ if (Config->GetModule(this)->Get<bool>("activate_on_set"))
+ {
+ User *u = User::Find(na->GetNick());
+
+ if (u && u->Account() == na->GetAccount())
+ IRCD->SendVhostDel(u);
+ }
+ }
+};
+
+MODULE_INIT(HostServCore)
+
diff --git a/modules/hostserv/off.cpp b/modules/hostserv/off.cpp
new file mode 100644
index 000000000..e8c191164
--- /dev/null
+++ b/modules/hostserv/off.cpp
@@ -0,0 +1,71 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+
+class CommandHSOff : public Command
+{
+ public:
+ CommandHSOff(Module *creator) : Command(creator, "hostserv/off", 0, 0)
+ {
+ this->SetDesc(_("Deactivates your assigned vhost"));
+ this->RequireUser(true);
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ User *u = source.GetUser();
+ NickServ::Nick *na = NickServ::FindNick(u->nick);
+
+ if (!na || na->GetAccount() != u->Account() || !na->HasVhost())
+ na = NickServ::FindNick(u->Account()->GetDisplay());
+
+ if (!na || !na->HasVhost() || na->GetAccount() != source.GetAccount())
+ {
+ source.Reply(_("There is no vhost assigned to this nickname."));
+ return;
+ }
+
+ u->vhost.clear();
+ IRCD->SendVhostDel(u);
+ Log(LOG_COMMAND, source, this) << "to disable their vhost";
+ source.Reply(_("Your vhost was removed and the normal cloaking restored."));
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Deactivates your vhost."));
+ return true;
+ }
+};
+
+class HSOff : public Module
+{
+ CommandHSOff commandhsoff;
+
+ public:
+ HSOff(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandhsoff(this)
+ {
+ if (!IRCD || !IRCD->CanSetVHost)
+ throw ModuleException("Your IRCd does not support vhosts");
+ }
+};
+
+MODULE_INIT(HSOff)
diff --git a/modules/hostserv/on.cpp b/modules/hostserv/on.cpp
new file mode 100644
index 000000000..1332c7e7a
--- /dev/null
+++ b/modules/hostserv/on.cpp
@@ -0,0 +1,81 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+
+class CommandHSOn : public Command
+{
+ public:
+ CommandHSOn(Module *creator) : Command(creator, "hostserv/on", 0, 0)
+ {
+ this->SetDesc(_("Activates your assigned vhost"));
+ this->RequireUser(true);
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ if (!IRCD->CanSetVHost)
+ return; // HostServ wouldn't even be loaded at this point
+
+ User *u = source.GetUser();
+ NickServ::Nick *na = NickServ::FindNick(u->nick);
+
+ if (!na || na->GetAccount() != u->Account() || !na->HasVhost())
+ na = NickServ::FindNick(u->Account()->GetDisplay());
+
+ if (!na || !na->HasVhost() || na->GetAccount() != u->Account())
+ {
+ source.Reply(_("There is no vhost assigned to this nickname."));
+ return;
+ }
+
+ if (!na->GetVhostIdent().empty())
+ source.Reply(_("Your vhost of \002{0}\002@\002{1}\002 is now activated."), na->GetVhostIdent(), na->GetVhostHost());
+ else
+ source.Reply(_("Your vhost of \002{0}\002 is now activated."), na->GetVhostHost());
+
+ Log(LOG_COMMAND, source, this) << "to enable their vhost of " << (!na->GetVhostIdent().empty() ? na->GetVhostIdent() + "@" : "") << na->GetVhostHost();
+ IRCD->SendVhost(u, na->GetVhostIdent(), na->GetVhostHost());
+ u->vhost = na->GetVhostHost();
+ if (IRCD->CanSetVIdent && !na->GetVhostIdent().empty())
+ u->SetVIdent(na->GetVhostIdent());
+ u->UpdateHost();
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Activates your vhost."));
+ return true;
+ }
+};
+
+class HSOn : public Module
+{
+ CommandHSOn commandhson;
+
+ public:
+ HSOn(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandhson(this)
+ {
+ if (!IRCD || !IRCD->CanSetVHost)
+ throw ModuleException("Your IRCd does not support vhosts");
+ }
+};
+
+MODULE_INIT(HSOn)
diff --git a/modules/hostserv/request.cpp b/modules/hostserv/request.cpp
new file mode 100644
index 000000000..2024ef3b3
--- /dev/null
+++ b/modules/hostserv/request.cpp
@@ -0,0 +1,432 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2005-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/memoserv.h"
+
+class HostRequest : public Serialize::Object
+{
+ friend class HostRequestType;
+
+ NickServ::Nick *na = nullptr;
+ Anope::string ident, host;
+ time_t time = 0;
+
+ public:
+ static constexpr const char *const NAME = "hostrequest";
+
+ HostRequest(Serialize::TypeBase *type) : Serialize::Object(type) { }
+ HostRequest(Serialize::TypeBase *type, Serialize::ID id) : Serialize::Object(type, id) { }
+
+ NickServ::Nick *GetNick();
+ void SetNick(NickServ::Nick *na);
+
+ Anope::string GetIdent();
+ void SetIdent(const Anope::string &i);
+
+ Anope::string GetHost();
+ void SetHost(const Anope::string &h);
+
+ time_t GetTime();
+ void SetTime(const time_t &t);
+};
+
+class HostRequestType : public Serialize::Type<HostRequest>
+{
+ public:
+ Serialize::ObjectField<HostRequest, NickServ::Nick *> na;
+ Serialize::Field<HostRequest, Anope::string> ident, host;
+ Serialize::Field<HostRequest, time_t> time;
+
+ HostRequestType(Module *me) : Serialize::Type<HostRequest>(me)
+ , na(this, "na", &HostRequest::na, true)
+ , ident(this, "ident", &HostRequest::ident)
+ , host(this, "host", &HostRequest::host)
+ , time(this, "time", &HostRequest::time)
+ {
+ }
+};
+
+NickServ::Nick *HostRequest::GetNick()
+{
+ return Get(&HostRequestType::na);
+}
+
+void HostRequest::SetNick(NickServ::Nick *na)
+{
+ Set(&HostRequestType::na, na);
+}
+
+Anope::string HostRequest::GetIdent()
+{
+ return Get(&HostRequestType::ident);
+}
+
+void HostRequest::SetIdent(const Anope::string &i)
+{
+ Set(&HostRequestType::ident, i);
+}
+
+Anope::string HostRequest::GetHost()
+{
+ return Get(&HostRequestType::host);
+}
+
+void HostRequest::SetHost(const Anope::string &h)
+{
+ Set(&HostRequestType::host, h);
+}
+
+time_t HostRequest::GetTime()
+{
+ return Get(&HostRequestType::time);
+}
+
+void HostRequest::SetTime(const time_t &t)
+{
+ Set(&HostRequestType::time, t);
+}
+
+class CommandHSRequest : public Command
+{
+ ServiceReference<MemoServ::MemoServService> memoserv;
+
+ void SendMemos(CommandSource &source, const Anope::string &vIdent, const Anope::string &vHost)
+ {
+ Anope::string host;
+
+ if (!vIdent.empty())
+ host = vIdent + "@" + vHost;
+ else
+ host = vHost;
+
+ if (Config->GetModule(GetOwner())->Get<bool>("memooper") && memoserv)
+ for (Oper *o : Serialize::GetObjects<Oper *>())
+ {
+ NickServ::Nick *na = NickServ::FindNick(o->GetName());
+ if (!na)
+ continue;
+
+ Anope::string message = Anope::printf(_("[auto memo] vHost \002%s\002 has been requested by %s."), host.c_str(), source.GetNick().c_str());
+
+ memoserv->Send(source.service->nick, na->GetNick(), message, true);
+ }
+ }
+
+ public:
+ CommandHSRequest(Module *creator) : Command(creator, "hostserv/request", 1, 1)
+ {
+ this->SetDesc(_("Request a vHost for your nick"));
+ this->SetSyntax(_("vhost"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ User *u = source.GetUser();
+ NickServ::Nick *na = NickServ::FindNick(source.GetNick());
+ if (!na || na->GetAccount() != source.GetAccount())
+ {
+ source.Reply(_("Access denied.")); //XXX with nonickownership this should be allowed.
+ return;
+ }
+
+ if (source.GetAccount()->HasFieldS("UNCONFIRMED"))
+ {
+ source.Reply(_("You must confirm your account before you may request a vhost."));
+ return;
+ }
+
+ Anope::string rawhostmask = params[0];
+
+ Anope::string user, host;
+ size_t a = rawhostmask.find('@');
+
+ if (a == Anope::string::npos)
+ host = rawhostmask;
+ else
+ {
+ user = rawhostmask.substr(0, a);
+ host = rawhostmask.substr(a + 1);
+ }
+
+ if (host.empty())
+ {
+ this->OnSyntaxError(source, "");
+ return;
+ }
+
+ if (!user.empty())
+ {
+ if (user.length() > Config->GetBlock("networkinfo")->Get<unsigned>("userlen"))
+ {
+ source.Reply(_("The username \002{0}\002 is too long, please use a username shorter than %d characters."), Config->GetBlock("networkinfo")->Get<unsigned>("userlen"));
+ return;
+ }
+
+ if (!IRCD->CanSetVIdent)
+ {
+ source.Reply(_("Vhosts may not contain a username."));
+ return;
+ }
+
+ if (!IRCD->IsIdentValid(user))
+ {
+ source.Reply(_("The requested username is not valid."));
+ return;
+ }
+ }
+
+ if (host.length() > Config->GetBlock("networkinfo")->Get<unsigned>("hostlen"))
+ {
+ source.Reply(_("The requested vhost is too long, please use a hostname no longer than {0} characters."), Config->GetBlock("networkinfo")->Get<unsigned>("hostlen"));
+ return;
+ }
+
+ if (!IRCD->IsHostValid(host))
+ {
+ source.Reply(_("The requested hostname is not valid."));
+ return;
+ }
+
+ time_t send_delay = Config->GetModule("memoserv")->Get<time_t>("senddelay");
+ if (Config->GetModule(this->GetOwner())->Get<bool>("memooper") && send_delay > 0 && u && u->lastmemosend + send_delay > Anope::CurTime)
+ {
+ source.Reply(_("Please wait %d seconds before requesting a new vHost."), send_delay);
+ u->lastmemosend = Anope::CurTime;
+ return;
+ }
+
+ HostRequest *req = Serialize::New<HostRequest *>();
+ req->SetNick(na);
+ req->SetIdent(user);
+ req->SetHost(host);
+ req->SetTime(Anope::CurTime);
+
+ source.Reply(_("Your vhost has been requested."));
+ this->SendMemos(source, user, host);
+ Log(LOG_COMMAND, source, this) << "to request new vhost " << (!user.empty() ? user + "@" : "") << host;
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Request the given \036vhost\037 to be actived for your nick by the network administrators. Please be patient while your request is being considered."));
+ return true;
+ }
+};
+
+class CommandHSActivate : public Command
+{
+ ServiceReference<MemoServ::MemoServService> memoserv;
+
+ public:
+ CommandHSActivate(Module *creator) : Command(creator, "hostserv/activate", 1, 1)
+ {
+ this->SetDesc(_("Approve the requested vHost of a user"));
+ this->SetSyntax(_("\037user\037"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ const Anope::string &nick = params[0];
+ NickServ::Nick *na = NickServ::FindNick(nick);
+
+ if (!na)
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), nick);
+ return;
+ }
+
+ HostRequest *req = na->GetExt<HostRequest>("hostrequest");
+ if (!req)
+ {
+ source.Reply(_("\002{0}\002 does not have a pending vhost request."), na->GetNick());
+ return;
+ }
+
+ na->SetVhost(req->GetIdent(), req->GetHost(), source.GetNick(), req->GetTime());
+ EventManager::Get()->Dispatch(&Event::SetVhost::OnSetVhost, na);
+
+ if (Config->GetModule(this->GetOwner())->Get<bool>("memouser") && memoserv)
+ memoserv->Send(source.service->nick, na->GetNick(), _("[auto memo] Your requested vHost has been approved."), true);
+
+ source.Reply(_("Vhost for \002{0}\002 has been activated."), na->GetNick());
+ Log(LOG_COMMAND, source, this) << "for " << na->GetNick() << " for vhost " << (!req->GetIdent().empty() ? req->GetIdent() + "@" : "") << req->GetHost();
+ req->Delete();
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Activate the requested vhost for the given user."));
+ if (Config->GetModule(this->GetOwner())->Get<bool>("memouser"))
+ source.Reply(_("A memo informing the user will also be sent."));
+
+ return true;
+ }
+};
+
+class CommandHSReject : public Command
+{
+ ServiceReference<MemoServ::MemoServService> memoserv;
+
+ public:
+ CommandHSReject(Module *creator) : Command(creator, "hostserv/reject", 1, 2)
+ {
+ this->SetDesc(_("Reject the requested vHost of a user"));
+ this->SetSyntax(_("\037user\037 [\037reason\037]"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ const Anope::string &nick = params[0];
+ const Anope::string &reason = params.size() > 1 ? params[1] : "";
+
+ NickServ::Nick *na = NickServ::FindNick(nick);
+ if (!na)
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), nick);
+ return;
+ }
+
+ HostRequest *req = na->GetExt<HostRequest>("hostrequest");
+ if (!req)
+ {
+ source.Reply(_("\002{0}\002 does not have a pending vhost request."), na->GetNick());
+ return;
+ }
+
+ req->Delete();
+
+ if (Config->GetModule(this->GetOwner())->Get<bool>("memouser") && memoserv)
+ {
+ Anope::string message;
+ if (!reason.empty())
+ message = Anope::printf(_("[auto memo] Your requested vHost has been rejected. Reason: %s"), reason.c_str());
+ else
+ message = _("[auto memo] Your requested vHost has been rejected.");
+
+ memoserv->Send(source.service->nick, nick, Language::Translate(source.GetAccount(), message.c_str()), true);
+ }
+
+ source.Reply(_("Vhost for \002{0}\002 has been rejected."), na->GetNick());
+ Log(LOG_COMMAND, source, this) << "to reject vhost for " << nick << " (" << (!reason.empty() ? reason : "no reason") << ")";
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Reject the requested vhost for the given user."));
+ if (Config->GetModule(this->GetOwner())->Get<bool>("memouser"))
+ source.Reply(_("A memo informing the user will also be sent, which includes the reason for the rejection if supplied."));
+
+ return true;
+ }
+};
+
+class CommandHSWaiting : public Command
+{
+ public:
+ CommandHSWaiting(Module *creator) : Command(creator, "hostserv/waiting", 0, 0)
+ {
+ this->SetDesc(_("Retrieves the vhost requests"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ unsigned counter = 0;
+ unsigned display_counter = 0, listmax = Config->GetModule(this->GetOwner())->Get<unsigned>("listmax");
+ ListFormatter list(source.GetAccount());
+
+ list.AddColumn(_("Number")).AddColumn(_("Nick")).AddColumn(_("Vhost")).AddColumn(_("Created"));
+
+ for (HostRequest *hr : Serialize::GetObjects<HostRequest *>())
+ {
+ if (!listmax || display_counter < listmax)
+ {
+ ++display_counter;
+
+ ListFormatter::ListEntry entry;
+ entry["Number"] = stringify(display_counter);
+ entry["Nick"] = hr->GetNick()->GetNick();
+ if (!hr->GetIdent().empty())
+ entry["Vhost"] = hr->GetIdent() + "@" + hr->GetHost();
+ else
+ entry["Vhost"] = hr->GetHost();
+ entry["Created"] = Anope::strftime(hr->GetTime(), NULL, true);
+ list.AddEntry(entry);
+ }
+ ++counter;
+ }
+
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+
+ source.Reply(_("Displayed \002{0}\002 records (\002{1}\002 total)."), display_counter, counter);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Lists the vhost requests."));
+
+ return true;
+ }
+};
+
+class HSRequest : public Module
+{
+ CommandHSRequest commandhsrequest;
+ CommandHSActivate commandhsactive;
+ CommandHSReject commandhsreject;
+ CommandHSWaiting commandhswaiting;
+
+ HostRequestType hrtype;
+
+ public:
+ HSRequest(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandhsrequest(this)
+ , commandhsactive(this)
+ , commandhsreject(this)
+ , commandhswaiting(this)
+ , hrtype(this)
+ {
+ if (!IRCD || !IRCD->CanSetVHost)
+ throw ModuleException("Your IRCd does not support vhosts");
+ }
+};
+
+MODULE_INIT(HSRequest)
diff --git a/modules/hostserv/set.cpp b/modules/hostserv/set.cpp
new file mode 100644
index 000000000..357c0466a
--- /dev/null
+++ b/modules/hostserv/set.cpp
@@ -0,0 +1,224 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+
+class CommandHSSet : public Command
+{
+ public:
+ CommandHSSet(Module *creator) : Command(creator, "hostserv/set", 2, 2)
+ {
+ this->SetDesc(_("Set the vhost of another user"));
+ this->SetSyntax(_("\037user\037 \037hostmask\037"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ const Anope::string &nick = params[0];
+
+ NickServ::Nick *na = NickServ::FindNick(nick);
+ if (na == NULL)
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), nick);
+ return;
+ }
+
+ Anope::string rawhostmask = params[1];
+
+ Anope::string user, host;
+ size_t a = rawhostmask.find('@');
+
+ if (a == Anope::string::npos)
+ host = rawhostmask;
+ else
+ {
+ user = rawhostmask.substr(0, a);
+ host = rawhostmask.substr(a + 1);
+ }
+
+ if (host.empty())
+ {
+ this->OnSyntaxError(source, "");
+ return;
+ }
+
+ if (!user.empty())
+ {
+ if (!IRCD->CanSetVIdent)
+ {
+ source.Reply(_("Vhosts may not contain a username."));
+ return;
+ }
+
+ if (!IRCD->IsIdentValid(user))
+ {
+ source.Reply(_("The requested username is not valid."));
+ return;
+ }
+ }
+
+ if (host.length() > Config->GetBlock("networkinfo")->Get<unsigned>("hostlen"))
+ {
+ source.Reply(_("The requested vhost is too long, please use a hostname no longer than {0} characters."), Config->GetBlock("networkinfo")->Get<unsigned>("hostlen"));
+ return;
+ }
+
+ if (!IRCD->IsHostValid(host))
+ {
+ source.Reply(_("The requested hostname is not valid."));
+ return;
+ }
+
+ Log(LOG_ADMIN, source, this) << "to set the vhost of " << na->GetNick() << " to " << (!user.empty() ? user + "@" : "") << host;
+
+ na->SetVhost(user, host, source.GetNick());
+ EventManager::Get()->Dispatch(&Event::SetVhost::OnSetVhost, na);
+ if (!user.empty())
+ source.Reply(_("Vhost for \002{0}\002 set to \002{0}\002@\002{1}\002."), nick, user, host);
+ else
+ source.Reply(_("Vhost for \002{0}\002 set to \002{0}\002."), nick, host);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Sets the vhost of the given \037user\037 to the given \037hostmask\037."));
+ return true;
+ }
+};
+
+class CommandHSSetAll : public Command
+{
+ void Sync(NickServ::Nick *na)
+ {
+ if (!na || !na->HasVhost())
+ return;
+
+ for (NickServ::Nick *nick : na->GetAccount()->GetRefs<NickServ::Nick *>())
+ nick->SetVhost(na->GetVhostIdent(), na->GetVhostHost(), na->GetVhostCreator());
+ }
+
+ public:
+ CommandHSSetAll(Module *creator) : Command(creator, "hostserv/setall", 2, 2)
+ {
+ this->SetDesc(_("Set the vhost for all nicks in a group"));
+ this->SetSyntax(_("\037user\037 \037hostmask\037"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ const Anope::string &nick = params[0];
+ NickServ::Nick *na = NickServ::FindNick(nick);
+ if (na == NULL)
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), nick);
+ return;
+ }
+
+ Anope::string rawhostmask = params[1];
+
+ Anope::string user, host;
+ size_t a = rawhostmask.find('@');
+
+ if (a == Anope::string::npos)
+ host = rawhostmask;
+ else
+ {
+ user = rawhostmask.substr(0, a);
+ host = rawhostmask.substr(a + 1);
+ }
+
+ if (host.empty())
+ {
+ this->OnSyntaxError(source, "");
+ return;
+ }
+
+ if (!user.empty())
+ {
+ if (!IRCD->CanSetVIdent)
+ {
+ source.Reply(_("Vhosts may not contain a username."));
+ return;
+ }
+
+ if (!IRCD->IsIdentValid(user))
+ {
+ source.Reply(_("The requested username is not valid."));
+ return;
+ }
+ }
+
+ if (host.length() > Config->GetBlock("networkinfo")->Get<unsigned>("hostlen"))
+ {
+ source.Reply(_("The requested vhost is too long, please use a hostname no longer than {0} characters."), Config->GetBlock("networkinfo")->Get<unsigned>("hostlen"));
+ return;
+ }
+
+ if (!IRCD->IsHostValid(host))
+ {
+ source.Reply(_("The requested hostname is not valid."));
+ return;
+ }
+
+ Log(LOG_ADMIN, source, this) << "to set the vhost of " << na->GetNick() << " to " << (!user.empty() ? user + "@" : "") << host;
+
+ na->SetVhost(user, host, source.GetNick());
+ this->Sync(na);
+ EventManager::Get()->Dispatch(&Event::SetVhost::OnSetVhost, na);
+ if (!user.empty())
+ source.Reply(_("Vhost for group \002{0}\002 set to \002{1}\002@\002{2}\002."), nick.c_str(), user.c_str(), host.c_str());
+ else
+ source.Reply(_("host for group \002{0}\002 set to \002{1}\002."), nick.c_str(), host.c_str());
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Sets the vhost for all nicknames in the group of \037user\037."));
+ return true;
+ }
+};
+
+class HSSet : public Module
+{
+ CommandHSSet commandhsset;
+ CommandHSSetAll commandhssetall;
+
+ public:
+ HSSet(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandhsset(this)
+ , commandhssetall(this)
+ {
+ if (!IRCD || !IRCD->CanSetVHost)
+ throw ModuleException("Your IRCd does not support vhosts");
+ }
+};
+
+MODULE_INIT(HSSet)
diff --git a/modules/m_httpd.cpp b/modules/httpd.cpp
index 20c98dd56..11f8318e7 100644
--- a/modules/m_httpd.cpp
+++ b/modules/httpd.cpp
@@ -1,9 +1,20 @@
/*
+ * Anope IRC Services
*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "module.h"
@@ -105,17 +116,17 @@ class MyHTTPClient : public HTTPClient
}
/* Close connection once all data is written */
- bool ProcessWrite() anope_override
+ bool ProcessWrite() override
{
return !BinarySocket::ProcessWrite() || this->write_buffer.empty() ? false : true;
}
- const Anope::string GetIP() anope_override
+ const Anope::string GetIP() override
{
return this->ip;
}
- bool Read(const char *buffer, size_t l) anope_override
+ bool Read(const char *buffer, size_t l) override
{
message.content.append(buffer, l);
@@ -149,7 +160,7 @@ class MyHTTPClient : public HTTPClient
this->Serve();
}
-
+
return true;
}
@@ -233,7 +244,7 @@ class MyHTTPClient : public HTTPClient
return true;
}
- void SendError(HTTPError err, const Anope::string &msg) anope_override
+ void SendError(HTTPError err, const Anope::string &msg) override
{
HTTPReply h;
@@ -244,7 +255,7 @@ class MyHTTPClient : public HTTPClient
this->SendReply(&h);
}
- void SendReply(HTTPReply *msg) anope_override
+ void SendReply(HTTPReply *msg) override
{
this->WriteClient("HTTP/1.1 " + GetStatusFromCode(msg->error));
this->WriteClient("Date: " + BuildDate());
@@ -296,7 +307,7 @@ class MyHTTPProvider : public HTTPProvider, public Timer
public:
MyHTTPProvider(Module *c, const Anope::string &n, const Anope::string &i, const unsigned short p, const int t, bool s) : Socket(-1, i.find(':') != Anope::string::npos), HTTPProvider(c, n, i, p, s), Timer(c, 10, Anope::CurTime, true), timeout(t) { }
- void Tick(time_t) anope_override
+ void Tick(time_t) override
{
while (!this->clients.empty())
{
@@ -309,24 +320,24 @@ class MyHTTPProvider : public HTTPProvider, public Timer
}
}
- ClientSocket* OnAccept(int fd, const sockaddrs &addr) anope_override
+ ClientSocket* OnAccept(int fd, const sockaddrs &addr) override
{
MyHTTPClient *c = new MyHTTPClient(this, fd, addr);
this->clients.push_back(c);
return c;
}
- bool RegisterPage(HTTPPage *page) anope_override
+ bool RegisterPage(HTTPPage *page) override
{
return this->pages.insert(std::make_pair(page->GetURL(), page)).second;
}
- void UnregisterPage(HTTPPage *page) anope_override
+ void UnregisterPage(HTTPPage *page) override
{
this->pages.erase(page->GetURL());
}
- HTTPPage* FindPage(const Anope::string &pname)
+ HTTPPage* FindPage(const Anope::string &pname) override
{
if (this->pages.count(pname) == 0)
return NULL;
@@ -335,11 +346,13 @@ class MyHTTPProvider : public HTTPProvider, public Timer
};
class HTTPD : public Module
+ , public EventHook<Event::ModuleLoad>
{
ServiceReference<SSLService> sslref;
std::map<Anope::string, MyHTTPProvider *> providers;
public:
- HTTPD(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, EXTRA | VENDOR), sslref("SSLService", "ssl")
+ HTTPD(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, EXTRA | VENDOR)
+ , EventHook<Event::ModuleLoad>(this)
{
}
@@ -358,7 +371,7 @@ class HTTPD : public Module
this->providers.clear();
}
- void OnReload(Configuration::Conf *config) anope_override
+ void OnReload(Configuration::Conf *config) override
{
Configuration::Block *conf = config->GetModule(this);
std::set<Anope::string> existing;
@@ -368,15 +381,15 @@ class HTTPD : public Module
Configuration::Block *block = conf->GetBlock("httpd", i);
- const Anope::string &hname = block->Get<const Anope::string>("name", "httpd/main");
+ const Anope::string &hname = block->Get<Anope::string>("name", "httpd/main");
existing.insert(hname);
- Anope::string ip = block->Get<const Anope::string>("ip");
+ Anope::string ip = block->Get<Anope::string>("ip");
int port = block->Get<int>("port", "8080");
int timeout = block->Get<int>("timeout", "30");
bool ssl = block->Get<bool>("ssl", "no");
- Anope::string ext_ip = block->Get<const Anope::string>("extforward_ip");
- Anope::string ext_header = block->Get<const Anope::string>("extforward_header");
+ Anope::string ext_ip = block->Get<Anope::string>("extforward_ip");
+ Anope::string ext_header = block->Get<Anope::string>("extforward_header");
if (ip.empty())
{
@@ -444,16 +457,16 @@ class HTTPD : public Module
HTTPProvider *p = it->second;
++it;
- if (existing.count(p->name) == 0)
+ if (existing.count(p->GetName()) == 0)
{
- Log(this) << "Removing HTTP server " << p->name;
- this->providers.erase(p->name);
+ Log(this) << "Removing HTTP server " << p->GetName();
+ this->providers.erase(p->GetName());
delete p;
}
}
}
- void OnModuleLoad(User *u, Module *m) anope_override
+ void OnModuleLoad(User *u, Module *m) override
{
for (std::map<Anope::string, MyHTTPProvider *>::iterator it = this->providers.begin(), it_end = this->providers.end(); it != it_end; ++it)
{
diff --git a/modules/m_helpchan.cpp b/modules/m_helpchan.cpp
deleted file mode 100644
index f8cd2b231..000000000
--- a/modules/m_helpchan.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- */
-
-#include "module.h"
-
-class HelpChannel : public Module
-{
- public:
- HelpChannel(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
- {
- }
-
- EventReturn OnChannelModeSet(Channel *c, MessageSource &, ChannelMode *mode, const Anope::string &param) anope_override
- {
- if (mode->name == "OP" && c && c->ci && c->name.equals_ci(Config->GetModule(this)->Get<const Anope::string>("helpchannel")))
- {
- User *u = User::Find(param);
-
- if (u && c->ci->AccessFor(u).HasPriv("OPME"))
- u->SetMode(Config->GetClient("OperServ"), "HELPOP");
- }
-
- return EVENT_CONTINUE;
- }
-};
-
-MODULE_INIT(HelpChannel)
diff --git a/modules/m_proxyscan.cpp b/modules/m_proxyscan.cpp
deleted file mode 100644
index ac38895e4..000000000
--- a/modules/m_proxyscan.cpp
+++ /dev/null
@@ -1,379 +0,0 @@
-/*
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- */
-
-#include "module.h"
-
-struct ProxyCheck
-{
- std::set<Anope::string, ci::less> types;
- std::vector<unsigned short> ports;
- time_t duration;
- Anope::string reason;
-};
-
-static Anope::string ProxyCheckString;
-static Anope::string target_ip;
-static unsigned short target_port;
-static bool add_to_akill;
-
-class ProxyCallbackListener : public ListenSocket
-{
- class ProxyCallbackClient : public ClientSocket, public BufferedSocket
- {
- public:
- ProxyCallbackClient(ListenSocket *l, int f, const sockaddrs &a) : Socket(f, l->IsIPv6()), ClientSocket(l, a), BufferedSocket()
- {
- }
-
- void OnAccept() anope_override
- {
- this->Write(ProxyCheckString);
- }
-
- bool ProcessWrite() anope_override
- {
- return !BufferedSocket::ProcessWrite() || this->write_buffer.empty() ? false : true;
- }
- };
-
- public:
- ProxyCallbackListener(const Anope::string &b, int p) : Socket(-1, b.find(':') != Anope::string::npos), ListenSocket(b, p, false)
- {
- }
-
- ClientSocket *OnAccept(int fd, const sockaddrs &addr) anope_override
- {
- return new ProxyCallbackClient(this, fd, addr);
- }
-};
-
-class ProxyConnect : public ConnectionSocket
-{
- static ServiceReference<XLineManager> akills;
-
- public:
- static std::set<ProxyConnect *> proxies;
-
- ProxyCheck proxy;
- unsigned short port;
- time_t created;
-
- ProxyConnect(ProxyCheck &p, unsigned short po) : Socket(-1), ConnectionSocket(), proxy(p),
- port(po), created(Anope::CurTime)
- {
- proxies.insert(this);
- }
-
- ~ProxyConnect()
- {
- proxies.erase(this);
- }
-
- virtual void OnConnect() anope_override = 0;
- virtual const Anope::string GetType() const = 0;
-
- protected:
- void Ban()
- {
- Anope::string reason = this->proxy.reason;
-
- reason = reason.replace_all_cs("%t", this->GetType());
- reason = reason.replace_all_cs("%i", this->conaddr.addr());
- reason = reason.replace_all_cs("%p", stringify(this->conaddr.port()));
-
- BotInfo *OperServ = Config->GetClient("OperServ");
- Log(OperServ) << "PROXYSCAN: Open " << this->GetType() << " proxy found on " << this->conaddr.addr() << ":" << this->conaddr.port() << " (" << reason << ")";
- XLine *x = new XLine("*@" + this->conaddr.addr(), OperServ ? OperServ->nick : "", Anope::CurTime + this->proxy.duration, reason, XLineManager::GenerateUID());
- if (add_to_akill && akills)
- {
- akills->AddXLine(x);
- akills->Send(NULL, x);
- }
- else
- {
- if (IRCD->CanSZLine)
- IRCD->SendSZLine(NULL, x);
- else
- IRCD->SendAkill(NULL, x);
- delete x;
- }
- }
-};
-ServiceReference<XLineManager> ProxyConnect::akills("XLineManager", "xlinemanager/sgline");
-std::set<ProxyConnect *> ProxyConnect::proxies;
-
-class HTTPProxyConnect : public ProxyConnect, public BufferedSocket
-{
- public:
- HTTPProxyConnect(ProxyCheck &p, unsigned short po) : Socket(-1), ProxyConnect(p, po), BufferedSocket()
- {
- }
-
- void OnConnect() anope_override
- {
- this->Write("CONNECT %s:%d HTTP/1.0", target_ip.c_str(), target_port);
- this->Write("Content-length: 0");
- this->Write("Connection: close");
- this->Write("");
- }
-
- const Anope::string GetType() const anope_override
- {
- return "HTTP";
- }
-
- bool ProcessRead() anope_override
- {
- bool b = BufferedSocket::ProcessRead();
- if (this->GetLine() == ProxyCheckString)
- {
- this->Ban();
- return false;
- }
- return b;
- }
-};
-
-class SOCKS5ProxyConnect : public ProxyConnect, public BinarySocket
-{
- public:
- SOCKS5ProxyConnect(ProxyCheck &p, unsigned short po) : Socket(-1), ProxyConnect(p, po), BinarySocket()
- {
- }
-
- void OnConnect() anope_override
- {
- sockaddrs target_addr;
- char buf[4 + sizeof(target_addr.sa4.sin_addr.s_addr) + sizeof(target_addr.sa4.sin_port)];
- int ptr = 0;
- target_addr.pton(AF_INET, target_ip, target_port);
- if (!target_addr.valid())
- return;
-
- buf[ptr++] = 5; // Version
- buf[ptr++] = 1; // # of methods
- buf[ptr++] = 0; // No authentication
-
- this->Write(buf, ptr);
-
- ptr = 1;
- buf[ptr++] = 1; // Connect
- buf[ptr++] = 0; // Reserved
- buf[ptr++] = 1; // IPv4
- memcpy(buf + ptr, &target_addr.sa4.sin_addr.s_addr, sizeof(target_addr.sa4.sin_addr.s_addr));
- ptr += sizeof(target_addr.sa4.sin_addr.s_addr);
- memcpy(buf + ptr, &target_addr.sa4.sin_port, sizeof(target_addr.sa4.sin_port));
- ptr += sizeof(target_addr.sa4.sin_port);
-
- this->Write(buf, ptr);
- }
-
- const Anope::string GetType() const anope_override
- {
- return "SOCKS5";
- }
-
- bool Read(const char *buffer, size_t l) anope_override
- {
- if (l >= ProxyCheckString.length() && !strncmp(buffer, ProxyCheckString.c_str(), ProxyCheckString.length()))
- {
- this->Ban();
- return false;
- }
- return true;
- }
-};
-
-class ModuleProxyScan : public Module
-{
- Anope::string listen_ip;
- unsigned short listen_port;
- Anope::string con_notice, con_source;
- std::vector<ProxyCheck> proxyscans;
-
- ProxyCallbackListener *listener;
-
- class ConnectionTimeout : public Timer
- {
- public:
- ConnectionTimeout(Module *c, long timeout) : Timer(c, timeout, Anope::CurTime, true)
- {
- }
-
- void Tick(time_t) anope_override
- {
- for (std::set<ProxyConnect *>::iterator it = ProxyConnect::proxies.begin(), it_end = ProxyConnect::proxies.end(); it != it_end;)
- {
- ProxyConnect *p = *it;
- ++it;
-
- if (p->created + this->GetSecs() < Anope::CurTime)
- delete p;
- }
- }
- } connectionTimeout;
-
- public:
- ModuleProxyScan(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, EXTRA | VENDOR),
- connectionTimeout(this, 5)
- {
-
-
- this->listener = NULL;
- }
-
- ~ModuleProxyScan()
- {
- for (std::set<ProxyConnect *>::iterator it = ProxyConnect::proxies.begin(), it_end = ProxyConnect::proxies.end(); it != it_end;)
- {
- ProxyConnect *p = *it;
- ++it;
- delete p;
- }
-
- for (std::map<int, Socket *>::const_iterator it = SocketEngine::Sockets.begin(), it_end = SocketEngine::Sockets.end(); it != it_end;)
- {
- Socket *s = it->second;
- ++it;
-
- ClientSocket *cs = dynamic_cast<ClientSocket *>(s);
- if (cs != NULL && cs->ls == this->listener)
- delete s;
- }
-
- delete this->listener;
- }
-
- void OnReload(Configuration::Conf *conf) anope_override
- {
- Configuration::Block *config = Config->GetModule(this);
-
- Anope::string s_target_ip = config->Get<const Anope::string>("target_ip");
- if (s_target_ip.empty())
- throw ConfigException(this->name + " target_ip may not be empty");
-
- int s_target_port = config->Get<int>("target_port", "-1");
- if (s_target_port <= 0)
- throw ConfigException(this->name + " target_port may not be empty and must be a positive number");
-
- Anope::string s_listen_ip = config->Get<const Anope::string>("listen_ip");
- if (s_listen_ip.empty())
- throw ConfigException(this->name + " listen_ip may not be empty");
-
- int s_listen_port = config->Get<int>("listen_port", "-1");
- if (s_listen_port <= 0)
- throw ConfigException(this->name + " listen_port may not be empty and must be a positive number");
-
- target_ip = s_target_ip;
- target_port = s_target_port;
- this->listen_ip = s_listen_ip;
- this->listen_port = s_listen_port;
- this->con_notice = config->Get<const Anope::string>("connect_notice");
- this->con_source = config->Get<const Anope::string>("connect_source");
- add_to_akill = config->Get<bool>("add_to_akill", "true");
- this->connectionTimeout.SetSecs(config->Get<time_t>("timeout", "5s"));
-
- ProxyCheckString = Config->GetBlock("networkinfo")->Get<const Anope::string>("networkname") + " proxy check";
- delete this->listener;
- this->listener = NULL;
- try
- {
- this->listener = new ProxyCallbackListener(this->listen_ip, this->listen_port);
- }
- catch (const SocketException &ex)
- {
- throw ConfigException("m_proxyscan: " + ex.GetReason());
- }
-
- this->proxyscans.clear();
- for (int i = 0; i < config->CountBlock("proxyscan"); ++i)
- {
- Configuration::Block *block = config->GetBlock("proxyscan", i);
- ProxyCheck p;
- Anope::string token;
-
- commasepstream sep(block->Get<const Anope::string>("type"));
- while (sep.GetToken(token))
- {
- if (!token.equals_ci("HTTP") && !token.equals_ci("SOCKS5"))
- continue;
- p.types.insert(token);
- }
- if (p.types.empty())
- continue;
-
- commasepstream sep2(block->Get<const Anope::string>("port"));
- while (sep2.GetToken(token))
- {
- try
- {
- unsigned short port = convertTo<unsigned short>(token);
- p.ports.push_back(port);
- }
- catch (const ConvertException &) { }
- }
- if (p.ports.empty())
- continue;
-
- p.duration = block->Get<time_t>("time", "4h");
- p.reason = block->Get<const Anope::string>("reason");
- if (p.reason.empty())
- continue;
-
- this->proxyscans.push_back(p);
- }
- }
-
- void OnUserConnect(User *user, bool &exempt) anope_override
- {
- if (exempt || user->Quitting() || !Me->IsSynced() || !user->server->IsSynced())
- return;
-
- /* At this time we only support IPv4 */
- if (!user->ip.valid() || user->ip.sa.sa_family != AF_INET)
- /* User doesn't have a valid IPv4 IP (ipv6/spoof/etc) */
- return;
-
- if (!this->con_notice.empty() && !this->con_source.empty())
- {
- BotInfo *bi = BotInfo::Find(this->con_source, true);
- if (bi)
- user->SendMessage(bi, this->con_notice);
- }
-
- for (unsigned i = this->proxyscans.size(); i > 0; --i)
- {
- ProxyCheck &p = this->proxyscans[i - 1];
-
- for (std::set<Anope::string, ci::less>::iterator it = p.types.begin(), it_end = p.types.end(); it != it_end; ++it)
- {
- for (unsigned k = 0; k < p.ports.size(); ++k)
- {
- try
- {
- ProxyConnect *con = NULL;
- if (it->equals_ci("HTTP"))
- con = new HTTPProxyConnect(p, p.ports[k]);
- else if (it->equals_ci("SOCKS5"))
- con = new SOCKS5ProxyConnect(p, p.ports[k]);
- else
- continue;
- con->Connect(user->ip.addr(), p.ports[k]);
- }
- catch (const SocketException &ex)
- {
- Log(LOG_DEBUG) << "m_proxyscan: " << ex.GetReason();
- }
- }
- }
- }
- }
-};
-
-MODULE_INIT(ModuleProxyScan)
-
diff --git a/modules/m_sasl.cpp b/modules/m_sasl.cpp
deleted file mode 100644
index e1f55bcc9..000000000
--- a/modules/m_sasl.cpp
+++ /dev/null
@@ -1,329 +0,0 @@
-/*
- *
- * (C) 2014-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- */
-
-#include "module.h"
-#include "modules/sasl.h"
-#include "modules/ns_cert.h"
-
-using namespace SASL;
-
-class Plain : public Mechanism
-{
- public:
- Plain(Module *o) : Mechanism(o, "PLAIN") { }
-
- void ProcessMessage(Session *sess, const SASL::Message &m) anope_override
- {
- if (m.type == "S")
- {
- sasl->SendMessage(sess, "C", "+");
- }
- else if (m.type == "C")
- {
- Anope::string decoded;
- Anope::B64Decode(m.data, decoded);
-
- size_t p = decoded.find('\0');
- if (p == Anope::string::npos)
- {
- sasl->Fail(sess);
- delete sess;
- return;
- }
- decoded = decoded.substr(p + 1);
-
- p = decoded.find('\0');
- if (p == Anope::string::npos)
- {
- sasl->Fail(sess);
- delete sess;
- return;
- }
-
- Anope::string acc = decoded.substr(0, p),
- pass = decoded.substr(p + 1);
-
- if (acc.empty() || pass.empty() || !IRCD->IsNickValid(acc) || pass.find_first_of("\r\n") != Anope::string::npos)
- {
- sasl->Fail(sess);
- delete sess;
- return;
- }
-
- SASL::IdentifyRequest *req = new SASL::IdentifyRequest(this->owner, m.source, acc, pass);
- FOREACH_MOD(OnCheckAuthentication, (NULL, req));
- req->Dispatch();
- }
- }
-};
-
-class External : public Mechanism
-{
- ServiceReference<CertService> certs;
-
- struct Session : SASL::Session
- {
- Anope::string cert;
-
- Session(Mechanism *m, const Anope::string &u) : SASL::Session(m, u) { }
- };
-
- public:
- External(Module *o) : Mechanism(o, "EXTERNAL"), certs("CertService", "certs")
- {
- if (!IRCD || !IRCD->CanCertFP)
- throw ModuleException("No CertFP");
- }
-
- Session* CreateSession(const Anope::string &uid) anope_override
- {
- return new Session(this, uid);
- }
-
- void ProcessMessage(SASL::Session *sess, const SASL::Message &m) anope_override
- {
- Session *mysess = anope_dynamic_static_cast<Session *>(sess);
-
- if (m.type == "S")
- {
- mysess->cert = m.ext;
-
- sasl->SendMessage(sess, "C", "+");
- }
- else if (m.type == "C")
- {
- if (!certs || mysess->cert.empty())
- {
- sasl->Fail(sess);
- delete sess;
- return;
- }
-
- NickCore *nc = certs->FindAccountFromCert(mysess->cert);
- if (!nc || nc->HasExt("NS_SUSPENDED"))
- {
- Log(Config->GetClient("NickServ"), "sasl") << "A user failed to identify using certificate " << mysess->cert << " using SASL EXTERNAL";
- sasl->Fail(sess);
- delete sess;
- return;
- }
-
- Log(Config->GetClient("NickServ"), "sasl") << "A user identified to account " << nc->display << " using SASL EXTERNAL";
- sasl->Succeed(sess, nc);
- delete sess;
- }
- }
-};
-
-class SASLService : public SASL::Service, public Timer
-{
- std::map<Anope::string, SASL::Session *> sessions;
-
- public:
- SASLService(Module *o) : SASL::Service(o), Timer(o, 60, Anope::CurTime, true) { }
-
- ~SASLService()
- {
- for (std::map<Anope::string, Session *>::iterator it = sessions.begin(); it != sessions.end(); it++)
- delete it->second;
- }
-
- void ProcessMessage(const SASL::Message &m) anope_override
- {
- if (m.target != "*")
- {
- Server *s = Server::Find(m.target);
- if (s != Me)
- {
- User *u = User::Find(m.target);
- if (!u || u->server != Me)
- return;
- }
- }
-
- Session* &session = sessions[m.source];
-
- if (m.type == "S")
- {
- ServiceReference<Mechanism> mech("SASL::Mechanism", m.data);
- if (!mech)
- {
- Session tmp(NULL, m.source);
-
- sasl->SendMechs(&tmp);
- sasl->Fail(&tmp);
- return;
- }
-
- if (!session)
- session = mech->CreateSession(m.source);
- }
- else if (m.type == "D")
- {
- delete session;
- sessions.erase(m.source);
- return;
- }
-
- if (session && session->mech)
- session->mech->ProcessMessage(session, m);
- }
-
- Anope::string GetAgent() anope_override
- {
- Anope::string agent = Config->GetModule(Service::owner)->Get<Anope::string>("agent", "NickServ");
- BotInfo *bi = Config->GetClient(agent);
- if (bi)
- agent = bi->GetUID();
- return agent;
- }
-
- Session* GetSession(const Anope::string &uid) anope_override
- {
- std::map<Anope::string, Session *>::iterator it = sessions.find(uid);
- if (it != sessions.end())
- return it->second;
- return NULL;
- }
-
- void RemoveSession(Session *sess) anope_override
- {
- sessions.erase(sess->uid);
- }
-
- void DeleteSessions(Mechanism *mech, bool da) anope_override
- {
- for (std::map<Anope::string, Session *>::iterator it = sessions.begin(); it != sessions.end();)
- {
- std::map<Anope::string, Session *>::iterator del = it++;
- if (*del->second->mech == mech)
- {
- if (da)
- this->SendMessage(del->second, "D", "A");
- delete del->second;
- }
- }
- }
-
- void SendMessage(Session *session, const Anope::string &mtype, const Anope::string &data) anope_override
- {
- SASL::Message msg;
- msg.source = this->GetAgent();
- msg.target = session->uid;
- msg.type = mtype;
- msg.data = data;
-
- IRCD->SendSASLMessage(msg);
- }
-
- void Succeed(Session *session, NickCore *nc) anope_override
- {
- // If the user is already introduced then we log them in now.
- // Otherwise, we send an SVSLOGIN to log them in later.
- User *user = User::Find(session->uid);
- NickAlias *na = NickAlias::Find(nc->display);
- if (user)
- {
- user->Identify(na);
- }
- else
- {
- IRCD->SendSVSLogin(session->uid, nc->display, na->GetVhostIdent(), na->GetVhostHost());
- }
- this->SendMessage(session, "D", "S");
- }
-
- void Fail(Session *session) anope_override
- {
- this->SendMessage(session, "D", "F");
- }
-
- void SendMechs(Session *session) anope_override
- {
- std::vector<Anope::string> mechs = Service::GetServiceKeys("SASL::Mechanism");
- Anope::string buf;
- for (unsigned j = 0; j < mechs.size(); ++j)
- buf += "," + mechs[j];
-
- this->SendMessage(session, "M", buf.empty() ? "" : buf.substr(1));
- }
-
- void Tick(time_t) anope_override
- {
- for (std::map<Anope::string, Session *>::iterator it = sessions.begin(); it != sessions.end();)
- {
- Anope::string key = it->first;
- Session *s = it->second;
- ++it;
-
- if (!s || !s->mech || s->created + 60 < Anope::CurTime)
- {
- delete s;
- sessions.erase(key);
- }
- }
- }
-};
-
-class ModuleSASL : public Module
-{
- SASLService sasl;
-
- Plain plain;
- External *external;
-
- std::vector<Anope::string> mechs;
-
- void CheckMechs()
- {
- std::vector<Anope::string> newmechs = ::Service::GetServiceKeys("SASL::Mechanism");
- if (newmechs == mechs)
- return;
-
- mechs = newmechs;
-
- // If we are connected to the network then broadcast the mechlist.
- if (Me && Me->IsSynced())
- IRCD->SendSASLMechanisms(mechs);
- }
-
- public:
- ModuleSASL(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- sasl(this), plain(this), external(NULL)
- {
- try
- {
- external = new External(this);
- CheckMechs();
- }
- catch (ModuleException &) { }
- }
-
- ~ModuleSASL()
- {
- delete external;
- }
-
- void OnModuleLoad(User *, Module *) anope_override
- {
- CheckMechs();
- }
-
- void OnModuleUnload(User *, Module *) anope_override
- {
- CheckMechs();
- }
-
- void OnPreUplinkSync(Server *) anope_override
- {
- // We have not yet sent a mechanism list so always do it here.
- IRCD->SendSASLMechanisms(mechs);
- }
-};
-
-MODULE_INIT(ModuleSASL)
diff --git a/modules/memoserv/CMakeLists.txt b/modules/memoserv/CMakeLists.txt
new file mode 100644
index 000000000..cd225a94d
--- /dev/null
+++ b/modules/memoserv/CMakeLists.txt
@@ -0,0 +1 @@
+build_modules(${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/modules/memoserv/cancel.cpp b/modules/memoserv/cancel.cpp
new file mode 100644
index 000000000..3ba25ffaf
--- /dev/null
+++ b/modules/memoserv/cancel.cpp
@@ -0,0 +1,98 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/memoserv.h"
+
+class CommandMSCancel : public Command
+{
+ public:
+ CommandMSCancel(Module *creator) : Command(creator, "memoserv/cancel", 1, 1)
+ {
+ this->SetDesc(_("Cancel the last memo you sent"));
+ this->SetSyntax(_("{\037user\037 | \037channel\037}"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ const Anope::string &nname = params[0];
+
+ bool ischan, isregistered;
+ MemoServ::MemoInfo *mi = MemoServ::service->GetMemoInfo(nname, ischan, isregistered, false);
+
+ if (!isregistered)
+ {
+ if (ischan)
+ source.Reply(_("Channel \002{0}\002 isn't registered."), nname);
+ else
+ source.Reply(_("\002{0}\002 isn't registered."), nname);
+ }
+ else
+ {
+ ChanServ::Channel *ci = NULL;
+ NickServ::Nick *na = NULL;
+ if (ischan)
+ ci = ChanServ::Find(nname);
+ else
+ na = NickServ::FindNick(nname);
+ if (mi)
+ {
+ auto memos = mi->GetMemos();
+ for (int i = memos.size() - 1; i >= 0; --i)
+ {
+ MemoServ::Memo *m = memos[i];
+ if (m->GetUnread() && source.nc->GetDisplay().equals_ci(m->GetSender()))
+ {
+ EventManager::Get()->Dispatch(&MemoServ::Event::MemoDel::OnMemoDel, ischan ? ci->GetName() : na->GetAccount()->GetDisplay(), mi, m);
+ mi->Del(i);
+ source.Reply(_("Your last memo to \002{0}\002 has been cancelled."), nname);
+ return;
+ }
+ }
+ }
+
+ source.Reply(_("No memo to \002{0}\002 was cancelable."), nname);
+ }
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Cancels the last memo you sent to \037user\037, provided it has not yet been read."));
+ return true;
+ }
+};
+
+class MSCancel : public Module
+{
+ CommandMSCancel commandmscancel;
+
+ public:
+ MSCancel(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandmscancel(this)
+ {
+ }
+};
+
+MODULE_INIT(MSCancel)
diff --git a/modules/memoserv/check.cpp b/modules/memoserv/check.cpp
new file mode 100644
index 000000000..8174f3eb4
--- /dev/null
+++ b/modules/memoserv/check.cpp
@@ -0,0 +1,88 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+
+class CommandMSCheck : public Command
+{
+ public:
+ CommandMSCheck(Module *creator) : Command(creator, "memoserv/check", 1, 1)
+ {
+ this->SetDesc(_("Checks if last memo to a user was read"));
+ this->SetSyntax(_("\037user\037"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &recipient = params[0];
+
+ bool found = false;
+
+ NickServ::Nick *na = NickServ::FindNick(recipient);
+ if (!na)
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), recipient);
+ return;
+ }
+
+ MemoServ::MemoInfo *mi = na->GetAccount()->GetMemos();
+
+ if (mi)
+ /* find the last memo */
+ for (unsigned i = mi->GetMemos().size(); i > 0; --i)
+ {
+ MemoServ::Memo *m = mi->GetMemo(i - 1);
+ NickServ::Nick *na2 = NickServ::FindNick(m->GetSender());
+
+ if (na2 != NULL && na2->GetAccount() == source.GetAccount())
+ {
+ found = true; /* Yes, we've found the memo */
+
+ if (m->GetUnread())
+ source.Reply(_("The last memo you sent to \002{0}\002 (sent on \002{1}\002) has not yet been read."), na->GetNick(), Anope::strftime(m->GetTime(), source.GetAccount()));
+ else
+ source.Reply(_("The last memo you sent to \002{0}\002 (sent on \002{1}\002) has been read."), na->GetNick(), Anope::strftime(m->GetTime(), source.GetAccount()));
+ break;
+ }
+ }
+
+ if (!found)
+ source.Reply(_("\002{0}\002 doesn't have a memo from you."), na->GetNick());
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Checks whether the last memo you sent to \037user\037 has been read or not."));
+ return true;
+ }
+};
+
+class MSCheck : public Module
+{
+ CommandMSCheck commandmscheck;
+
+ public:
+ MSCheck(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandmscheck(this)
+ {
+
+ }
+};
+
+MODULE_INIT(MSCheck)
diff --git a/modules/memoserv/del.cpp b/modules/memoserv/del.cpp
new file mode 100644
index 000000000..17ef2a39f
--- /dev/null
+++ b/modules/memoserv/del.cpp
@@ -0,0 +1,153 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/memoserv.h"
+
+class CommandMSDel : public Command
+{
+ public:
+ CommandMSDel(Module *creator) : Command(creator, "memoserv/del", 0, 2)
+ {
+ this->SetDesc(_("Delete a memo or memos"));
+ this->SetSyntax(_("[\037channel\037] {\037num\037 | \037list\037 | LAST | ALL}"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ MemoServ::MemoInfo *mi;
+ ChanServ::Channel *ci = NULL;
+ Anope::string numstr = !params.empty() ? params[0] : "", chan;
+
+ if (!numstr.empty() && numstr[0] == '#')
+ {
+ chan = numstr;
+ numstr = params.size() > 1 ? params[1] : "";
+
+ ci = ChanServ::Find(chan);
+ if (!ci)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
+ return;
+ }
+
+ if (!source.AccessFor(ci).HasPriv("MEMO"))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "MEMO", ci->GetName());
+ return;
+ }
+
+ mi = ci->GetMemos();
+ }
+ else
+ mi = source.nc->GetMemos();
+
+ if (numstr.empty() || (!isdigit(numstr[0]) && !numstr.equals_ci("ALL") && !numstr.equals_ci("LAST")))
+ {
+ this->OnSyntaxError(source, numstr);
+ return;
+ }
+
+ if (!mi || mi->GetMemos().empty())
+ {
+ if (!chan.empty())
+ source.Reply(_("\002{0}\002 has no memos."), chan);
+ else
+ source.Reply(_("You have no memos."));
+ return;
+ }
+
+ auto memos = mi->GetMemos();
+
+ if (isdigit(numstr[0]))
+ {
+ NumberList(numstr, true,
+ [&](unsigned int number)
+ {
+ if (!number || number > memos.size())
+ return;
+
+ EventManager::Get()->Dispatch(&MemoServ::Event::MemoDel::OnMemoDel, ci ? ci->GetName() : source.nc->GetDisplay(), mi, mi->GetMemo(number - 1));
+
+ mi->Del(number - 1);
+ source.Reply(_("Memo \002{0}\002 has been deleted."), number);
+ },
+ [](){});
+ }
+ else if (numstr.equals_ci("LAST"))
+ {
+ /* Delete last memo. */
+ EventManager::Get()->Dispatch(&MemoServ::Event::MemoDel::OnMemoDel, ci ? ci->GetName() : source.nc->GetDisplay(), mi, mi->GetMemo(memos.size() - 1));
+ mi->Del(memos.size() - 1);
+ source.Reply(_("Memo \002{0}\002 has been deleted."), memos.size() + 1);
+ }
+ else
+ {
+ /* Delete all memos. */
+ std::for_each(memos.begin(), memos.end(),
+ [&](MemoServ::Memo *m)
+ {
+ EventManager::Get()->Dispatch(&MemoServ::Event::MemoDel::OnMemoDel, ci ? ci->GetName() : source.nc->GetDisplay(), mi, m);
+ delete m;
+ });
+ if (!chan.empty())
+ source.Reply(_("All memos for channel \002{0}\002 have been deleted."), chan);
+ else
+ source.Reply(_("All of your memos have been deleted."));
+ }
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Deletes the specified memo or memos."
+ " You can supply multiple memo numbers or ranges of numbers instead of a single number, as in the second example below."
+ " If \002LAST\002 is given, the last memo will be deleted."
+ " If \002ALL\002 is given, deletes all of your memos.\n"
+ "\n"
+ "Examples:\n"
+ "\n"
+ " {0} 1\n"
+ " Deletes your first memo.\n"
+ "\n"
+ " {0} 2-5,7-9\n"
+ " Deletes memos numbered 2 through 5 and 7 through 9."),
+ source.command);
+ return true;
+ }
+};
+
+class MSDel : public Module
+{
+ CommandMSDel commandmsdel;
+
+ public:
+ MSDel(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandmsdel(this)
+ {
+
+ }
+};
+
+MODULE_INIT(MSDel)
diff --git a/modules/memoserv/ignore.cpp b/modules/memoserv/ignore.cpp
new file mode 100644
index 000000000..42e94a609
--- /dev/null
+++ b/modules/memoserv/ignore.cpp
@@ -0,0 +1,155 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2010-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+
+class CommandMSIgnore : public Command
+{
+ public:
+ CommandMSIgnore(Module *creator) : Command(creator, "memoserv/ignore", 1, 3)
+ {
+ this->SetDesc(_("Manage the memo ignore list"));
+ this->SetSyntax(_("[\037channel\037] ADD \037entry\037"));
+ this->SetSyntax(_("[\037channel\037] DEL \037entry\037"));
+ this->SetSyntax(_("[\037channel\037] LIST"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ if (!MemoServ::service)
+ return;
+
+ Anope::string channel = params[0];
+ Anope::string command = (params.size() > 1 ? params[1] : "");
+ Anope::string param = (params.size() > 2 ? params[2] : "");
+
+ if (channel[0] != '#')
+ {
+ param = command;
+ command = channel;
+ channel = source.GetNick();
+ }
+
+ bool ischan, isregistered;
+ MemoServ::MemoInfo *mi = MemoServ::service->GetMemoInfo(channel, ischan, isregistered, true);
+ ChanServ::Channel *ci = ChanServ::Find(channel);
+ if (!isregistered)
+ {
+ if (ischan)
+ source.Reply(_("Channel \002{0}\002 isn't registered."), channel);
+ else
+ source.Reply(_("\002{0}\002 isn't registered."), channel);
+ return;
+ }
+
+ if (ischan && !source.AccessFor(ci).HasPriv("MEMO"))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "MEMO", ci->GetName());
+ return;
+ }
+
+ auto ignores = mi->GetIgnores();
+
+ if (command.equals_ci("ADD") && !param.empty())
+ {
+ if (ignores.size() >= Config->GetModule(this->GetOwner())->Get<unsigned>("max", "32"))
+ {
+ source.Reply(_("Sorry, the memo ignore list for \002{0}\002 is full."), channel);
+ return;
+ }
+
+ for (MemoServ::Ignore *ign : ignores)
+ if (ign->GetMask().equals_ci(param))
+ {
+ source.Reply(_("\002{0}\002 is already on your memo ignore list."), param);
+ return;
+ }
+
+ MemoServ::Ignore *ign = Serialize::New<MemoServ::Ignore *>();
+ ign->SetMemoInfo(mi);
+ ign->SetMask(param);
+
+ source.Reply(_("\002{0}\002 has been added to your memo ignore list."), param);
+ }
+ else if (command.equals_ci("DEL") && !param.empty())
+ {
+ for (MemoServ::Ignore *ign : ignores)
+ if (ign->GetMask().equals_ci(param))
+ {
+ delete ign;
+ source.Reply(_("\002{0}\002 has been removed from your memo ignore list."), param);
+ return;
+ }
+
+ source.Reply(_("\002{0}\002 is not on your memo ignore list."), param);
+ }
+ else if (command.equals_ci("LIST"))
+ {
+ if (ignores.empty())
+ {
+ source.Reply(_("Your memo ignore list is empty."));
+ return;
+ }
+ ListFormatter list(source.GetAccount());
+ list.AddColumn(_("Mask"));
+ for (MemoServ::Ignore *ign : ignores)
+ {
+ ListFormatter::ListEntry entry;
+ entry["Mask"] = ign->GetMask();
+ list.AddEntry(entry);
+ }
+
+ source.Reply(_("Memo ignore list:"));
+
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+ }
+ else
+ this->OnSyntaxError(source, "");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Allows you to ignore users from memoing you or a channel."
+ " If someone on the memo ignore list tries to memo you or a channel, they will not be told that you have them ignored."));
+ return true;
+ }
+};
+
+class MSIgnore : public Module
+{
+ CommandMSIgnore commandmsignore;
+
+ public:
+ MSIgnore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandmsignore(this)
+ {
+ }
+};
+
+MODULE_INIT(MSIgnore)
diff --git a/modules/memoserv/info.cpp b/modules/memoserv/info.cpp
new file mode 100644
index 000000000..aff622b21
--- /dev/null
+++ b/modules/memoserv/info.cpp
@@ -0,0 +1,227 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+
+class CommandMSInfo : public Command
+{
+ public:
+ CommandMSInfo(Module *creator) : Command(creator, "memoserv/info", 0, 1)
+ {
+ this->SetDesc(_("Displays information about your memos"));
+ this->SetSyntax(_("[\037user\037 | \037channel\037]"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ NickServ::Account *nc = source.nc;
+ MemoServ::MemoInfo *mi;
+ NickServ::Nick *na = NULL;
+ ChanServ::Channel *ci = NULL;
+ const Anope::string &nname = !params.empty() ? params[0] : "";
+ bool hardmax;
+
+ if (!nname.empty() && nname[0] != '#' && source.HasPriv("memoserv/info"))
+ {
+ na = NickServ::FindNick(nname);
+ if (!na)
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), nname);
+ return;
+ }
+ mi = na->GetAccount()->GetMemos();
+ hardmax = na->GetAccount()->HasFieldS("MEMO_HARDMAX");
+ }
+ else if (!nname.empty() && nname[0] == '#')
+ {
+ ci = ChanServ::Find(nname);
+ if (!ci)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), nname);
+ return;
+ }
+
+ if (!source.AccessFor(ci).HasPriv("MEMO"))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "MEMO", ci->GetName());
+ return;
+ }
+
+ mi = ci->GetMemos();
+ hardmax = ci->HasFieldS("MEMO_HARDMAX");
+ }
+ else if (!nname.empty()) /* It's not a chan and we aren't an oper */
+ {
+ source.Reply(_("Access denied. You do not have the correct operator privilege to see the memo info of \002{0}\002."), nname);
+ return;
+ }
+ else
+ {
+ mi = nc->GetMemos();
+ hardmax = nc->HasFieldS("MEMO_HARDMAX");
+ }
+ if (!mi)
+ return;
+
+ auto memos = mi->GetMemos();
+
+ if (!nname.empty() && (ci || na->GetAccount() != nc))
+ {
+ if (memos.empty())
+ source.Reply(_("%s currently has no memos."), nname.c_str());
+ else if (memos.size() == 1)
+ {
+ if (mi->GetMemo(0)->GetUnread())
+ source.Reply(_("\002{0}\002 currently has \0021\002 memo, and it has not yet been read."), nname);
+ else
+ source.Reply(_("\002{0}\002 currently has \0021\002 memo."), nname);
+ }
+ else
+ {
+ unsigned count = 0, i, end;
+ for (i = 0, end = memos.size(); i < end; ++i)
+ if (mi->GetMemo(i)->GetUnread())
+ ++count;
+ if (count == memos.size())
+ source.Reply(_("\002{0}\002 currently has \002{1]\002 memos; all of them are unread."), nname, count);
+ else if (!count)
+ source.Reply(_("\002{0}\002 currently has \002{1}\002 memos."), nname, memos.size());
+ else if (count == 1)
+ source.Reply(_("\002{0}\002 currently has \002{1}\002 memos, of which \0021\002 is unread."), nname, memos.size());
+ else
+ source.Reply(_("\002{0}\002 currently has \002{1]\002 memos, of which \002{2}\002 are unread."), nname, memos.size(), count);
+ }
+ if (mi->GetMemoMax() >= 0)
+ {
+ if (hardmax)
+ source.Reply(_("The memo limit of \002{0}\002 is \002{1}\002, and may not be changed."), nname, mi->GetMemoMax());
+ else
+ source.Reply(_("The memo limit of \002{0}\002 is \002{1}\002."), nname, mi->GetMemoMax());
+ }
+ else
+ source.Reply(_("\002{0}\002 has no memo limit."), nname);
+
+ if (na)
+ {
+ if (na->GetAccount()->HasFieldS("MEMO_RECEIVE") && na->GetAccount()->HasFieldS("MEMO_SIGNON"))
+ source.Reply(_("\002{0}\002 is notified of new memos at logon and when they arrive."), nname);
+ else if (na->GetAccount()->HasFieldS("MEMO_RECEIVE"))
+ source.Reply(_("\002{0}\002 is notified when new memos arrive."), nname);
+ else if (na->GetAccount()->HasFieldS("MEMO_SIGNON"))
+ source.Reply(_("\002{0}\002 is notified of news memos at logon."), nname);
+ else
+ source.Reply(_("\002{0}\002 is not notified of new memos."), nname);
+ }
+ }
+ else
+ {
+ if (memos.empty())
+ source.Reply(_("You currently have no memos."));
+ else if (memos.size() == 1)
+ {
+ if (mi->GetMemo(0)->GetUnread())
+ source.Reply(_("You currently have \0021\002 memo, and it has not yet been read."));
+ else
+ source.Reply(_("You currently have \0021\002 memo."));
+ }
+ else
+ {
+ unsigned count = 0, i, end;
+ for (i = 0, end = memos.size(); i < end; ++i)
+ if (mi->GetMemo(i)->GetUnread())
+ ++count;
+ if (count == memos.size())
+ source.Reply(_("You currently have \002{0}\002 memos; all of them are unread."), count);
+ else if (!count)
+ source.Reply(_("You currently have \002{0}\002 memos."), memos.size());
+ else if (count == 1)
+ source.Reply(_("You currently have \002{0}\002 memos, of which \0021\002 is unread."), memos.size());
+ else
+ source.Reply(_("You currently have \002{0}\002 memos, of which \002{1}\002 are unread."), memos.size(), count);
+ }
+
+ if (!mi->GetMemoMax())
+ {
+ if (!source.IsServicesOper() && hardmax)
+ source.Reply(_("Your memo limit is \0020\002; you will not receive any new memos. You cannot change this limit."));
+ else
+ source.Reply(_("Your memo limit is \0020\002; you will not receive any new memos."));
+ }
+ else if (mi->GetMemoMax() > 0)
+ {
+ if (!source.IsServicesOper() && hardmax)
+ source.Reply(_("Your memo limit is \002{0}\002, and may not be changed."), mi->GetMemoMax());
+ else
+ source.Reply(_("Your memo limit is \002{0}\002."), mi->GetMemoMax());
+ }
+ else
+ source.Reply(_("You have no limit on the number of memos you may keep."));
+
+ bool memo_mail = nc->HasFieldS("MEMO_MAIL");
+ if (nc->HasFieldS("MEMO_RECEIVE") && nc->HasFieldS("MEMO_SIGNON"))
+ {
+ if (memo_mail)
+ source.Reply(_("You will be notified of new memos at logon and when they arrive, and by mail when they arrive."));
+ else
+ source.Reply(_("You will be notified of new memos at logon and when they arrive."));
+ }
+ else if (nc->HasFieldS("MEMO_RECEIVE"))
+ {
+ if (memo_mail)
+ source.Reply(_("You will be notified by message and by mail when new memos arrive."));
+ else
+ source.Reply(_("You will be notified when new memos arrive."));
+ }
+ else if (nc->HasFieldS("MEMO_SIGNON"))
+ {
+ if (memo_mail)
+ source.Reply(_("You will be notified of new memos at logon, and by mail when they arrive."));
+ else
+ source.Reply(_("You will be notified of new memos at logon."));
+ }
+ else
+ source.Reply(_("You will not be notified of new memos."));
+ }
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Without a parameter, displays information on the number of memos you have, how many of them are unread, and how many total memos you can receive."
+ " With a parameter, displays the same information for the given \037user\037 or \037channel\037, if you have the appropriate privilege.\n"
+ "\n"
+ "Use of this command on a channel requires the \002{0}\002 privilege on \037channel\037."),
+ source.command);
+
+ return true;
+ }
+};
+
+class MSInfo : public Module
+{
+ CommandMSInfo commandmsinfo;
+
+ public:
+ MSInfo(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandmsinfo(this)
+ {
+
+ }
+};
+
+MODULE_INIT(MSInfo)
diff --git a/modules/memoserv/list.cpp b/modules/memoserv/list.cpp
new file mode 100644
index 000000000..0fd169cc2
--- /dev/null
+++ b/modules/memoserv/list.cpp
@@ -0,0 +1,171 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+
+class CommandMSList : public Command
+{
+ public:
+ CommandMSList(Module *creator) : Command(creator, "memoserv/list", 0, 2)
+ {
+ this->SetDesc(_("List your memos"));
+ this->SetSyntax(_("[\037channel\037] [\037list\037 | NEW]"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+
+ Anope::string param = !params.empty() ? params[0] : "", chan;
+ ChanServ::Channel *ci = NULL;
+ MemoServ::MemoInfo *mi;
+
+ if (!param.empty() && param[0] == '#')
+ {
+ chan = param;
+ param = params.size() > 1 ? params[1] : "";
+
+ ci = ChanServ::Find(chan);
+ if (!ci)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
+ return;
+ }
+
+ if (!source.AccessFor(ci).HasPriv("MEMO"))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "MEMO", ci->GetName());
+ return;
+ }
+
+ mi = ci->GetMemos();
+ }
+ else
+ mi = source.nc->GetMemos();
+
+ if (!param.empty() && !isdigit(param[0]) && !param.equals_ci("NEW"))
+ {
+ this->OnSyntaxError(source, param);
+ return;
+ }
+
+ if (!mi)
+ return;
+
+ auto memos = mi->GetMemos();
+
+ if (!memos.size())
+ {
+ if (!chan.empty())
+ source.Reply(_("\002{0}\002 has no memos."), chan);
+ else
+ source.Reply(_("You have no memos."));
+ return;
+ }
+
+ ListFormatter list(source.GetAccount());
+
+ list.AddColumn(_("Number")).AddColumn(_("Sender")).AddColumn(_("Date/Time"));
+
+ if (!param.empty() && isdigit(param[0]))
+ {
+ NumberList(param, false,
+ [&](unsigned int number)
+ {
+ if (!number || number > memos.size())
+ return;
+
+ MemoServ::Memo *m = mi->GetMemo(number - 1);
+
+ ListFormatter::ListEntry entry;
+ entry["Number"] = (m->GetUnread() ? "* " : " ") + stringify(number);
+ entry["Sender"] = m->GetSender();
+ entry["Date/Time"] = Anope::strftime(m->GetTime(), source.GetAccount());
+ list.AddEntry(entry);
+ },
+ []{});
+ }
+ else
+ {
+ if (!param.empty())
+ {
+ unsigned i, end;
+ for (i = 0, end = memos.size(); i < end; ++i)
+ if (mi->GetMemo(i)->GetUnread())
+ break;
+ if (i == end)
+ {
+ if (!chan.empty())
+ source.Reply(_("\002{0}\002 has no new memos."), chan);
+ else
+ source.Reply(_("You have no new memos."));
+ return;
+ }
+ }
+
+ for (unsigned i = 0, end = memos.size(); i < end; ++i)
+ {
+ if (!param.empty() && !mi->GetMemo(i)->GetUnread())
+ continue;
+
+ MemoServ::Memo *m = mi->GetMemo(i);
+
+ ListFormatter::ListEntry entry;
+ entry["Number"] = (m->GetUnread() ? "* " : " ") + stringify(i + 1);
+ entry["Sender"] = m->GetSender();
+ entry["Date/Time"] = Anope::strftime(m->GetTime(), source.GetAccount());
+ list.AddEntry(entry);
+ }
+ }
+
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+ source.Reply(_("Memos for \002{0}\002:"), ci ? ci->GetName().c_str() : source.GetNick().c_str());
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Lists the memos you currently have."
+ " With \002NEW\002, lists only new (unread) memos."
+ " Unread memos are marked with a \"*\" to the left of the memo number."
+ " You can also specify a list of numbers, as in the example below:\n"
+ "\n"
+ "Example:\n"
+ "\n"
+ " {0} 2-5,7-9\n"
+ " Lists memos numbered 2 through 5 and 7 through 9."));
+ return true;
+ }
+};
+
+class MSList : public Module
+{
+ CommandMSList commandmslist;
+
+ public:
+ MSList(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandmslist(this)
+ {
+
+ }
+};
+
+MODULE_INIT(MSList)
diff --git a/modules/memoserv/main/CMakeLists.txt b/modules/memoserv/main/CMakeLists.txt
new file mode 100644
index 000000000..781f0ef1f
--- /dev/null
+++ b/modules/memoserv/main/CMakeLists.txt
@@ -0,0 +1 @@
+build_subdir(${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/modules/memoserv/main/ignore.cpp b/modules/memoserv/main/ignore.cpp
new file mode 100644
index 000000000..8ac15484f
--- /dev/null
+++ b/modules/memoserv/main/ignore.cpp
@@ -0,0 +1,46 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2015-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "memo.h"
+#include "ignoretype.h"
+
+IgnoreImpl::~IgnoreImpl()
+{
+}
+
+MemoServ::MemoInfo *IgnoreImpl::GetMemoInfo()
+{
+ return Get(&IgnoreType::mi);
+}
+
+void IgnoreImpl::SetMemoInfo(MemoServ::MemoInfo *m)
+{
+ Set(&IgnoreType::mi, m);
+}
+
+Anope::string IgnoreImpl::GetMask()
+{
+ return Get(&IgnoreType::mask);
+}
+
+void IgnoreImpl::SetMask(const Anope::string &mask)
+{
+ Set(&IgnoreType::mask, mask);
+}
+
diff --git a/modules/memoserv/main/ignore.h b/modules/memoserv/main/ignore.h
new file mode 100644
index 000000000..3c632a74e
--- /dev/null
+++ b/modules/memoserv/main/ignore.h
@@ -0,0 +1,39 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2015-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "modules/memoserv.h"
+
+class IgnoreImpl : public MemoServ::Ignore
+{
+ friend class IgnoreType;
+
+ MemoServ::MemoInfo *memoinfo = nullptr;
+ Anope::string mask;
+
+ public:
+ IgnoreImpl(Serialize::TypeBase *type) : MemoServ::Ignore(type) { }
+ IgnoreImpl(Serialize::TypeBase *type, Serialize::ID id) : MemoServ::Ignore(type, id) { }
+ ~IgnoreImpl();
+
+ MemoServ::MemoInfo *GetMemoInfo() override;
+ void SetMemoInfo(MemoServ::MemoInfo *) override;
+
+ Anope::string GetMask() override;
+ void SetMask(const Anope::string &mask) override;
+};
diff --git a/modules/memoserv/main/ignoretype.cpp b/modules/memoserv/main/ignoretype.cpp
new file mode 100644
index 000000000..940214803
--- /dev/null
+++ b/modules/memoserv/main/ignoretype.cpp
@@ -0,0 +1,29 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2015-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "ignoretype.h"
+
+IgnoreType::IgnoreType(Module *me) : Serialize::Type<IgnoreImpl>(me)
+ , mi(this, "mi", &IgnoreImpl::memoinfo, true)
+ , mask(this, "mask", &IgnoreImpl::mask)
+{
+
+}
+
diff --git a/modules/memoserv/main/ignoretype.h b/modules/memoserv/main/ignoretype.h
new file mode 100644
index 000000000..3b8f510a8
--- /dev/null
+++ b/modules/memoserv/main/ignoretype.h
@@ -0,0 +1,29 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2015-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ignore.h"
+
+class IgnoreType : public Serialize::Type<IgnoreImpl>
+{
+ public:
+ Serialize::ObjectField<IgnoreImpl, MemoServ::MemoInfo *> mi;
+ Serialize::Field<IgnoreImpl, Anope::string> mask;
+
+ IgnoreType(Module *);
+};
diff --git a/modules/memoserv/main/memo.cpp b/modules/memoserv/main/memo.cpp
new file mode 100644
index 000000000..e148040f4
--- /dev/null
+++ b/modules/memoserv/main/memo.cpp
@@ -0,0 +1,85 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2015-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "memotype.h"
+
+MemoImpl::~MemoImpl()
+{
+}
+
+MemoServ::MemoInfo *MemoImpl::GetMemoInfo()
+{
+ return Get(&MemoType::mi);
+}
+
+void MemoImpl::SetMemoInfo(MemoServ::MemoInfo *mi)
+{
+ Set(&MemoType::mi, mi);
+}
+
+time_t MemoImpl::GetTime()
+{
+ return Get(&MemoType::time);
+}
+
+void MemoImpl::SetTime(const time_t &t)
+{
+ Set(&MemoType::time, t);
+}
+
+Anope::string MemoImpl::GetSender()
+{
+ return Get(&MemoType::sender);
+}
+
+void MemoImpl::SetSender(const Anope::string &s)
+{
+ Set(&MemoType::sender, s);
+}
+
+Anope::string MemoImpl::GetText()
+{
+ return Get(&MemoType::text);
+}
+
+void MemoImpl::SetText(const Anope::string &t)
+{
+ Set(&MemoType::text, t);
+}
+
+bool MemoImpl::GetUnread()
+{
+ return Get(&MemoType::unread);
+}
+
+void MemoImpl::SetUnread(const bool &b)
+{
+ Set(&MemoType::unread, b);
+}
+
+bool MemoImpl::GetReceipt()
+{
+ return Get(&MemoType::receipt);
+}
+
+void MemoImpl::SetReceipt(const bool &b)
+{
+ Set(&MemoType::receipt, b);
+}
+
diff --git a/modules/memoserv/main/memo.h b/modules/memoserv/main/memo.h
new file mode 100644
index 000000000..5eac706db
--- /dev/null
+++ b/modules/memoserv/main/memo.h
@@ -0,0 +1,53 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2015-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "modules/memoserv.h"
+
+class MemoImpl : public MemoServ::Memo
+{
+ friend class MemoType;
+
+ MemoServ::MemoInfo *memoinfo = nullptr;
+ Anope::string text, sender;
+ time_t time = 0;
+ bool unread = false, receipt = false;
+
+ public:
+ MemoImpl(Serialize::TypeBase *type) : MemoServ::Memo(type) { }
+ MemoImpl(Serialize::TypeBase *type, Serialize::ID id) : MemoServ::Memo(type, id) { }
+ ~MemoImpl();
+
+ MemoServ::MemoInfo *GetMemoInfo() override;
+ void SetMemoInfo(MemoServ::MemoInfo *) override;
+
+ time_t GetTime() override;
+ void SetTime(const time_t &) override;
+
+ Anope::string GetSender() override;
+ void SetSender(const Anope::string &) override;
+
+ Anope::string GetText() override;
+ void SetText(const Anope::string &) override;
+
+ bool GetUnread() override;
+ void SetUnread(const bool &) override;
+
+ bool GetReceipt() override;
+ void SetReceipt(const bool &) override;
+};
diff --git a/modules/memoserv/main/memoinfo.cpp b/modules/memoserv/main/memoinfo.cpp
new file mode 100644
index 000000000..6fa8df8b1
--- /dev/null
+++ b/modules/memoserv/main/memoinfo.cpp
@@ -0,0 +1,82 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2015-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "memoinfotype.h"
+
+MemoServ::Memo *MemoInfoImpl::GetMemo(unsigned index)
+{
+ auto memos = GetMemos();
+ return index >= memos.size() ? nullptr : memos[index];
+}
+
+unsigned MemoInfoImpl::GetIndex(MemoServ::Memo *m)
+{
+ auto memos = GetMemos();
+ for (unsigned i = 0; i < memos.size(); ++i)
+ if (this->GetMemo(i) == m)
+ return i;
+ return -1; // XXX wtf?
+}
+
+void MemoInfoImpl::Del(unsigned index)
+{
+ delete GetMemo(index);
+}
+
+bool MemoInfoImpl::HasIgnore(User *u)
+{
+ for (MemoServ::Ignore *ign : GetIgnores())
+ {
+ const Anope::string &mask = ign->GetMask();
+ if (u->nick.equals_ci(mask) || (u->Account() && u->Account()->GetDisplay().equals_ci(mask)) || Anope::Match(u->GetMask(), mask))
+ return true;
+ }
+ return false;
+}
+
+Serialize::Object *MemoInfoImpl::GetOwner()
+{
+ return Get(&MemoInfoType::owner);
+}
+
+void MemoInfoImpl::SetOwner(Serialize::Object *o)
+{
+ Set(&MemoInfoType::owner, o);
+}
+
+int16_t MemoInfoImpl::GetMemoMax()
+{
+ return Get(&MemoInfoType::memomax);
+}
+
+void MemoInfoImpl::SetMemoMax(const int16_t &i)
+{
+ Set(&MemoInfoType::memomax, i);
+}
+
+std::vector<MemoServ::Memo *> MemoInfoImpl::GetMemos()
+{
+ return GetRefs<MemoServ::Memo *>();
+}
+
+std::vector<MemoServ::Ignore *> MemoInfoImpl::GetIgnores()
+{
+ return GetRefs<MemoServ::Ignore *>();
+}
+
diff --git a/modules/memoserv/main/memoinfo.h b/modules/memoserv/main/memoinfo.h
new file mode 100644
index 000000000..79deb496b
--- /dev/null
+++ b/modules/memoserv/main/memoinfo.h
@@ -0,0 +1,47 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2015-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "modules/memoserv.h"
+
+class MemoInfoImpl : public MemoServ::MemoInfo
+{
+ friend class MemoInfoType;
+
+ Serialize::Object *owner = nullptr;
+ int16_t memomax = 0;
+
+ public:
+ MemoInfoImpl(Serialize::TypeBase *type) : MemoServ::MemoInfo(type) { }
+ MemoInfoImpl(Serialize::TypeBase *type, Serialize::ID id) : MemoServ::MemoInfo(type, id) { }
+
+ MemoServ::Memo *GetMemo(unsigned index) override;
+ unsigned GetIndex(MemoServ::Memo *m) override;
+ void Del(unsigned index) override;
+ bool HasIgnore(User *u) override;
+
+ Serialize::Object *GetOwner() override;
+ void SetOwner(Serialize::Object *) override;
+
+ int16_t GetMemoMax() override;
+ void SetMemoMax(const int16_t &) override;
+
+ std::vector<MemoServ::Memo *> GetMemos() override;
+ std::vector<MemoServ::Ignore *> GetIgnores() override;
+};
+
diff --git a/modules/memoserv/main/memoinfotype.cpp b/modules/memoserv/main/memoinfotype.cpp
new file mode 100644
index 000000000..3447de5c8
--- /dev/null
+++ b/modules/memoserv/main/memoinfotype.cpp
@@ -0,0 +1,29 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2015-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "memoinfotype.h"
+
+MemoInfoType::MemoInfoType(Module *me) : Serialize::Type<MemoInfoImpl>(me)
+ , owner(this, "owner", &MemoInfoImpl::owner, true)
+ , memomax(this, "memomax", &MemoInfoImpl::memomax)
+{
+
+}
+
diff --git a/modules/memoserv/main/memoinfotype.h b/modules/memoserv/main/memoinfotype.h
new file mode 100644
index 000000000..97af31917
--- /dev/null
+++ b/modules/memoserv/main/memoinfotype.h
@@ -0,0 +1,29 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2015-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "memoinfo.h"
+
+class MemoInfoType : public Serialize::Type<MemoInfoImpl>
+{
+ public:
+ Serialize::ObjectField<MemoInfoImpl, Serialize::Object *> owner;
+ Serialize::Field<MemoInfoImpl, int16_t> memomax;
+
+ MemoInfoType(Module *);
+};
diff --git a/modules/memoserv/main/memoserv.cpp b/modules/memoserv/main/memoserv.cpp
new file mode 100644
index 000000000..a8e9b4e81
--- /dev/null
+++ b/modules/memoserv/main/memoserv.cpp
@@ -0,0 +1,305 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2011-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/nickserv/update.h"
+#include "modules/help.h"
+#include "modules/botserv/bot.h"
+#include "modules/memoserv.h"
+#include "memotype.h"
+#include "memoinfotype.h"
+#include "ignoretype.h"
+
+class MemoServCore : public Module, public MemoServ::MemoServService
+ , public EventHook<NickServ::Event::NickRegister>
+ , public EventHook<Event::ChanRegistered>
+ , public EventHook<Event::BotDelete>
+ , public EventHook<Event::NickIdentify>
+ , public EventHook<Event::JoinChannel>
+ , public EventHook<Event::UserAway>
+ , public EventHook<Event::NickUpdate>
+ , public EventHook<Event::Help>
+{
+ Reference<ServiceBot> MemoServ;
+
+ MemoInfoType memoinfo_type;
+ MemoType memo_type;
+ IgnoreType ignore_type;
+
+ bool SendMemoMail(NickServ::Account *nc, MemoServ::MemoInfo *mi, MemoServ::Memo *m)
+ {
+ Anope::string subject = Language::Translate(nc, Config->GetBlock("mail")->Get<Anope::string>("memo_subject").c_str()),
+ message = Language::Translate(Config->GetBlock("mail")->Get<Anope::string>("memo_message").c_str());
+
+ subject = subject.replace_all_cs("%n", nc->GetDisplay());
+ subject = subject.replace_all_cs("%s", m->GetSender());
+ subject = subject.replace_all_cs("%d", stringify(mi->GetIndex(m) + 1));
+ subject = subject.replace_all_cs("%t", m->GetText());
+ subject = subject.replace_all_cs("%N", Config->GetBlock("networkinfo")->Get<Anope::string>("networkname"));
+
+ message = message.replace_all_cs("%n", nc->GetDisplay());
+ message = message.replace_all_cs("%s", m->GetSender());
+ message = message.replace_all_cs("%d", stringify(mi->GetIndex(m) + 1));
+ message = message.replace_all_cs("%t", m->GetText());
+ message = message.replace_all_cs("%N", Config->GetBlock("networkinfo")->Get<Anope::string>("networkname"));
+
+ return Mail::Send(nc, subject, message);
+ }
+
+ public:
+ MemoServCore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, PSEUDOCLIENT | VENDOR)
+ , MemoServ::MemoServService(this)
+
+ , EventHook<NickServ::Event::NickRegister>(this)
+ , EventHook<Event::ChanRegistered>(this)
+ , EventHook<Event::BotDelete>(this)
+ , EventHook<Event::NickIdentify>(this)
+ , EventHook<Event::JoinChannel>(this)
+ , EventHook<Event::UserAway>(this)
+ , EventHook<Event::NickUpdate>(this)
+ , EventHook<Event::Help>(this)
+
+ , memoinfo_type(this)
+ , memo_type(this)
+ , ignore_type(this)
+ {
+ MemoServ::service = this;
+ }
+
+ ~MemoServCore()
+ {
+ MemoServ::service = nullptr;
+ }
+
+ MemoResult Send(const Anope::string &source, const Anope::string &target, const Anope::string &message, bool force) override
+ {
+ bool ischan, isregistered;
+ MemoServ::MemoInfo *mi = GetMemoInfo(target, ischan, isregistered, true);
+
+ if (mi == NULL)
+ return MEMO_INVALID_TARGET;
+
+ User *sender = User::Find(source, true);
+ if (sender != NULL && !sender->HasPriv("memoserv/no-limit") && !force)
+ {
+ time_t send_delay = Config->GetModule("memoserv")->Get<time_t>("senddelay");
+ if (send_delay > 0 && sender->lastmemosend + send_delay > Anope::CurTime)
+ return MEMO_TOO_FAST;
+ else if (!mi->GetMemoMax())
+ return MEMO_TARGET_FULL;
+ else if (mi->GetMemoMax() > 0 && mi->GetMemos().size() >= static_cast<unsigned>(mi->GetMemoMax()))
+ return MEMO_TARGET_FULL;
+ else if (mi->HasIgnore(sender))
+ return MEMO_SUCCESS;
+ }
+
+ if (sender != NULL)
+ sender->lastmemosend = Anope::CurTime;
+
+ MemoServ::Memo *m = Serialize::New<MemoServ::Memo *>();
+ m->SetMemoInfo(mi);
+ m->SetSender(source);
+ m->SetTime(Anope::CurTime);
+ m->SetText(message);
+ m->SetUnread(true);
+
+ EventManager::Get()->Dispatch(&MemoServ::Event::MemoSend::OnMemoSend, source, target, mi, m);
+
+ if (ischan)
+ {
+ ChanServ::Channel *ci = ChanServ::Find(target);
+
+ if (ci->c)
+ {
+ for (Channel::ChanUserList::iterator it = ci->c->users.begin(), it_end = ci->c->users.end(); it != it_end; ++it)
+ {
+ ChanUserContainer *cu = it->second;
+
+ if (ci->AccessFor(cu->user).HasPriv("MEMO"))
+ {
+ if (cu->user->Account() && cu->user->Account()->HasFieldS("MEMO_RECEIVE"))
+ cu->user->SendMessage(*MemoServ, _("There is a new memo on channel \002{0}\002. Type \002{1}{2} READ {3} {4}\002 to read it."), ci->GetName(), Config->StrictPrivmsg, MemoServ->nick, ci->GetName(), mi->GetMemos().size()); // XXX
+ }
+ }
+ }
+ }
+ else
+ {
+ NickServ::Account *nc = NickServ::FindNick(target)->GetAccount();
+
+ if (nc->HasFieldS("MEMO_RECEIVE"))
+ for (User *u : nc->users)
+ u->SendMessage(*MemoServ, _("You have a new memo from \002{0}\002. Type \002{1}{2} READ {3}\002 to read it."), source, Config->StrictPrivmsg, MemoServ->nick, mi->GetMemos().size());//XXX
+
+ /* let's get out the mail if set in the nickcore - certus */
+ if (nc->HasFieldS("MEMO_MAIL"))
+ SendMemoMail(nc, mi, m);
+ }
+
+ return MEMO_SUCCESS;
+ }
+
+ void Check(User *u) override
+ {
+ NickServ::Account *nc = u->Account();
+ if (!nc || !nc->GetMemos())
+ return;
+
+ auto memos = nc->GetMemos()->GetMemos();
+ unsigned i = 0, end = memos.size(), newcnt = 0;
+ for (; i < end; ++i)
+ if (memos[i]->GetUnread())
+ ++newcnt;
+ if (newcnt > 0)
+ u->SendMessage(*MemoServ, newcnt == 1 ? _("You have 1 new memo.") : _("You have %d new memos."), newcnt);
+ if (nc->GetMemos()->GetMemoMax() > 0 && memos.size() >= static_cast<unsigned>(nc->GetMemos()->GetMemoMax()))
+ {
+ if (memos.size() > static_cast<unsigned>(nc->GetMemos()->GetMemoMax()))
+ u->SendMessage(*MemoServ, _("You are over your maximum number of memos (%d). You will be unable to receive any new memos until you delete some of your current ones."), nc->GetMemos()->GetMemoMax());
+ else
+ u->SendMessage(*MemoServ, _("You have reached your maximum number of memos (%d). You will be unable to receive any new memos until you delete some of your current ones."), nc->GetMemos()->GetMemoMax());
+ }
+ }
+
+ MemoServ::MemoInfo *GetMemoInfo(const Anope::string &target, bool &is_registered, bool &ischan, bool create) override
+ {
+ if (!target.empty() && target[0] == '#')
+ {
+ ischan = true;
+ ChanServ::Channel *ci = ChanServ::Find(target);
+ if (ci != NULL)
+ {
+ is_registered = true;
+ if (create && !ci->GetMemos())
+ {
+ MemoServ::MemoInfo *mi = Serialize::New<MemoServ::MemoInfo *>();
+ mi->SetOwner(ci);
+ }
+ return ci->GetMemos();
+ }
+ else
+ is_registered = false;
+ }
+ else
+ {
+ ischan = false;
+ NickServ::Nick *na = NickServ::FindNick(target);
+ if (na != NULL)
+ {
+ is_registered = true;
+ if (create && !na->GetAccount()->GetMemos())
+ {
+ MemoServ::MemoInfo *mi = Serialize::New<MemoServ::MemoInfo *>();
+ mi->SetOwner(na->GetAccount());
+ }
+ return na->GetAccount()->GetMemos();
+ }
+ else
+ is_registered = false;
+ }
+
+ return NULL;
+ }
+
+ void OnReload(Configuration::Conf *conf) override
+ {
+ const Anope::string &msnick = conf->GetModule(this)->Get<Anope::string>("client");
+
+ if (msnick.empty())
+ throw ConfigException(Module::name + ": <client> must be defined");
+
+ ServiceBot *bi = ServiceBot::Find(msnick, true);
+ if (!bi)
+ throw ConfigException(Module::name + ": no bot named " + msnick);
+
+ MemoServ = bi;
+ }
+
+ void OnNickRegister(User *, NickServ::Nick *na, const Anope::string &) override
+ {
+ MemoServ::MemoInfo *mi = Serialize::New<MemoServ::MemoInfo *>();
+ mi->SetOwner(na->GetAccount());
+ mi->SetMemoMax(Config->GetModule(this)->Get<int>("maxmemos"));
+ }
+
+ void OnChanRegistered(ChanServ::Channel *ci) override
+ {
+ MemoServ::MemoInfo *mi = Serialize::New<MemoServ::MemoInfo *>();
+ mi->SetOwner(ci);
+ mi->SetMemoMax(Config->GetModule(this)->Get<int>("maxmemos"));
+ }
+
+ void OnBotDelete(ServiceBot *bi) override
+ {
+ if (bi == MemoServ)
+ MemoServ = NULL;
+ }
+
+ void OnNickIdentify(User *u) override
+ {
+ this->Check(u);
+ }
+
+ void OnJoinChannel(User *u, Channel *c) override
+ {
+ if (u->server && u->server->IsSynced() && c->ci && c->ci->GetMemos() && !c->ci->GetMemos()->GetMemos().empty() && c->ci->AccessFor(u).HasPriv("MEMO"))
+ {
+ if (c->ci->GetMemos()->GetMemos().size() == 1)
+ u->SendMessage(*MemoServ, _("There is \002{0}\002 memo on channel \002{1}\002."), c->ci->GetMemos()->GetMemos().size(), c->ci->GetName());
+ else
+ u->SendMessage(*MemoServ, _("There are \002{0}\002 memos on channel \002{1}\002."), c->ci->GetMemos()->GetMemos().size(), c->ci->GetName());
+ }
+ }
+
+ void OnUserAway(User *u, const Anope::string &message) override
+ {
+ if (message.empty())
+ this->Check(u);
+ }
+
+ void OnNickUpdate(User *u) override
+ {
+ this->Check(u);
+ }
+
+ EventReturn OnPreHelp(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ if (!params.empty() || source.c || source.service != *MemoServ)
+ return EVENT_CONTINUE;
+ source.Reply(_("\002%s\002 is a utility allowing IRC users to send short\n"
+ "messages to other IRC users, whether they are online at\n"
+ "the time or not, or to channels(*). Both the sender's\n"
+ "nickname and the target nickname or channel must be\n"
+ "registered in order to send a memo.\n"
+ "%s's commands include:"), MemoServ->nick.c_str(), MemoServ->nick.c_str());
+ return EVENT_CONTINUE;
+ }
+
+ void OnPostHelp(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ if (!params.empty() || source.c || source.service != *MemoServ)
+ return;
+ source.Reply(_(" \n"
+ "Type \002%s%s HELP \037command\037\002 for help on any of the\n"
+ "above commands."), Config->StrictPrivmsg.c_str(), MemoServ->nick.c_str());
+ }
+};
+
+MODULE_INIT(MemoServCore)
+
diff --git a/modules/memoserv/main/memotype.cpp b/modules/memoserv/main/memotype.cpp
new file mode 100644
index 000000000..13898de77
--- /dev/null
+++ b/modules/memoserv/main/memotype.cpp
@@ -0,0 +1,33 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2015-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "memotype.h"
+
+MemoType::MemoType(Module *me) : Serialize::Type<MemoImpl>(me)
+ , mi(this, "mi", &MemoImpl::memoinfo, true)
+ , time(this, "time", &MemoImpl::time)
+ , sender(this, "sender", &MemoImpl::sender)
+ , text(this, "text", &MemoImpl::text)
+ , unread(this, "unread", &MemoImpl::unread)
+ , receipt(this, "receipt", &MemoImpl::receipt)
+{
+
+}
+
diff --git a/modules/memoserv/main/memotype.h b/modules/memoserv/main/memotype.h
new file mode 100644
index 000000000..b9b136976
--- /dev/null
+++ b/modules/memoserv/main/memotype.h
@@ -0,0 +1,32 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2015-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "memo.h"
+
+class MemoType : public Serialize::Type<MemoImpl>
+{
+ public:
+ Serialize::ObjectField<MemoImpl, MemoServ::MemoInfo *> mi;
+ Serialize::Field<MemoImpl, time_t> time;
+ Serialize::Field<MemoImpl, Anope::string> sender;
+ Serialize::Field<MemoImpl, Anope::string> text;
+ Serialize::Field<MemoImpl, bool> unread, receipt;
+
+ MemoType(Module *);
+};
diff --git a/modules/memoserv/read.cpp b/modules/memoserv/read.cpp
new file mode 100644
index 000000000..62d6cd4d2
--- /dev/null
+++ b/modules/memoserv/read.cpp
@@ -0,0 +1,217 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/memoserv.h"
+
+static void rsend_notify(CommandSource &source, MemoServ::MemoInfo *mi, MemoServ::Memo *m, const Anope::string &targ)
+{
+ /* Only send receipt if memos are allowed */
+ if (MemoServ::service && !Anope::ReadOnly)
+ {
+ /* Get nick alias for sender */
+ NickServ::Nick *na = NickServ::FindNick(m->GetSender());
+
+ if (!na)
+ return;
+
+ /* Get nick core for sender */
+ NickServ::Account *nc = na->GetAccount();
+
+ if (!nc)
+ return;
+
+ /* Text of the memo varies if the recipient was a
+ nick or channel */
+ Anope::string text = Anope::printf(Language::Translate(na->GetAccount(), _("\002[auto-memo]\002 The memo you sent to \002%s\002 has been viewed.")), targ.c_str());
+
+ /* Send notification */
+ MemoServ::service->Send(source.GetNick(), m->GetSender(), text, true);
+
+ /* Notify recipient of the memo that a notification has
+ been sent to the sender */
+ source.Reply(_("A notification memo has been sent to \002{0}\002 informing him/her you have read his/her memo."), nc->GetDisplay());
+ }
+
+ /* Remove receipt flag from the original memo */
+ m->SetReceipt(false);
+}
+
+class CommandMSRead : public Command
+{
+ static void DoRead(CommandSource &source, MemoServ::MemoInfo *mi, ChanServ::Channel *ci, unsigned index)
+ {
+ MemoServ::Memo *m = mi->GetMemo(index);
+ if (!m)
+ return;
+
+ if (ci)
+ source.Reply(_("Memo \002{0}\002 from \002{1}\002 (\002{2}\002)."), index + 1, m->GetSender(), Anope::strftime(m->GetTime(), source.GetAccount()));
+ else
+ source.Reply(_("Memo \002{0}\002 from \002{1}\002 (\002{2}\002)."), index + 1, m->GetSender(), Anope::strftime(m->GetTime(), source.GetAccount()));
+
+ ServiceBot *bi;
+ Anope::string cmd;
+ if (Command::FindCommandFromService("memoserv/del", bi, cmd))
+ {
+ if (ci)
+ source.Reply(_("To delete, use \002{0}{1} {2} {3} {4}\002"), Config->StrictPrivmsg, bi->nick, cmd, ci->GetName(), index + 1);
+ else
+ source.Reply(_("To delete, use \002{0}{1} {2} {3}\002"), Config->StrictPrivmsg, bi->nick, cmd, index + 1);
+ }
+
+ source.Reply(m->GetText());
+ m->SetUnread(false);
+
+ /* Check if a receipt notification was requested */
+ if (m->GetReceipt())
+ rsend_notify(source, mi, m, ci ? ci->GetName() : source.GetNick());
+ }
+
+ public:
+ CommandMSRead(Module *creator) : Command(creator, "memoserv/read", 1, 2)
+ {
+ this->SetDesc(_("Read a memo or memos"));
+ this->SetSyntax(_("[\037channel\037] {\037num\037 | \037list\037 | LAST | NEW}"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+
+ MemoServ::MemoInfo *mi;
+ ChanServ::Channel *ci = NULL;
+ Anope::string numstr = params[0], chan;
+
+ if (!numstr.empty() && numstr[0] == '#')
+ {
+ chan = numstr;
+ numstr = params.size() > 1 ? params[1] : "";
+
+ ci = ChanServ::Find(chan);
+ if (!ci)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
+ return;
+ }
+
+ if (!source.AccessFor(ci).HasPriv("MEMO"))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "MEMO", ci->GetName());
+ return;
+ }
+
+ mi = ci->GetMemos();
+ }
+ else
+ mi = source.nc->GetMemos();
+
+ if (numstr.empty() || (!numstr.equals_ci("LAST") && !numstr.equals_ci("NEW") && numstr.find_first_not_of("0123456789.,-") != Anope::string::npos))
+ {
+ this->OnSyntaxError(source, numstr);
+ return;
+ }
+
+ if (!mi)
+ return;
+
+ auto memos = mi->GetMemos();
+
+ if (memos.empty())
+ {
+ if (!chan.empty())
+ source.Reply(_("\002{0}\002 has no memos."), chan);
+ else
+ source.Reply(_("You have no memos."));
+ return;
+ }
+
+ int i, end;
+
+ if (numstr.equals_ci("NEW"))
+ {
+ int readcount = 0;
+ for (i = 0, end = memos.size(); i < end; ++i)
+ if (mi->GetMemo(i)->GetUnread())
+ {
+ DoRead(source, mi, ci, i);
+ ++readcount;
+ }
+ if (!readcount)
+ {
+ if (!chan.empty())
+ source.Reply(_("\002{0}\002 has no new memos."), chan);
+ else
+ source.Reply(_("You have no new memos."));
+ }
+ }
+ else if (numstr.equals_ci("LAST"))
+ {
+ for (i = 0, end = memos.size() - 1; i < end; ++i);
+ DoRead(source, mi, ci, i);
+ }
+ else /* number[s] */
+ {
+ bool shown = false;
+
+ NumberList(numstr, false,
+ [&](unsigned int number)
+ {
+ if (!number || number > memos.size())
+ return;
+
+ shown = true;
+ DoRead(source, mi, ci, number - 1);
+ },
+ []{});
+
+ if (!shown)
+ source.Reply(_("No memos to display."));
+ }
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Sends you the text of the memos specified."
+ " If LAST is given, sends you the memo you most recently received."
+ " If NEW is given, sends you all of your new memos."
+ " Otherwise, sends you memo number \037num\037."
+ " You can also give a list of numbers, as in the example:\n"
+ "\n"
+ "Example:\n"
+ "\n"
+ " {0} 2-5,7-9\n"
+ " Displays memos numbered 2 through 5 and 7 through 9."),
+ source.command);
+ return true;
+ }
+};
+
+class MSRead : public Module
+{
+ CommandMSRead commandmsread;
+
+ public:
+ MSRead(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandmsread(this)
+ {
+
+ }
+};
+
+MODULE_INIT(MSRead)
diff --git a/modules/memoserv/rsend.cpp b/modules/memoserv/rsend.cpp
new file mode 100644
index 000000000..2656ffd3a
--- /dev/null
+++ b/modules/memoserv/rsend.cpp
@@ -0,0 +1,114 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/memoserv.h"
+
+class CommandMSRSend : public Command
+{
+ public:
+ CommandMSRSend(Module *creator) : Command(creator, "memoserv/rsend", 2, 2)
+ {
+ this->SetDesc(_("Sends a memo and requests a read receipt"));
+ this->SetSyntax(_("{\037nick\037 | \037channel\037} \037memo-text\037"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+#warning "this is completely disabled"
+#if 0
+ if (!MemoServ::service)
+ return;
+
+ if (Anope::ReadOnly && !source.IsOper())
+ {
+ source.Reply(_("Sorry, memo sending is temporarily disabled."));
+ return;
+ }
+
+ const Anope::string &nick = params[0];
+ const Anope::string &text = params[1];
+ const NickServ::Nick *na = NULL;
+
+ /* prevent user from rsend to themselves */
+ if ((na = NickServ::FindNick(nick)) && na->GetAccount() == source.GetAccount())
+ {
+ source.Reply(_("You can not request a receipt when sending a memo to yourself."));
+ return;
+ }
+
+ if (Config->GetModule(this->GetOwner())->Get<bool>("operonly") && !source.IsServicesOper())
+ source.Reply(_("Access denied. This command is for operators only."));
+ else
+ {
+ MemoServ::MemoServService::MemoResult result = MemoServ::service->Send(source.GetNick(), nick, text);
+ if (result == MemoServ::MemoServService::MEMO_INVALID_TARGET)
+ source.Reply(_("\002{0}\002 isn't registered."), nick);
+ else if (result == MemoServ::MemoServService::MEMO_TOO_FAST)
+ source.Reply(_("Please wait \002{0}\002 seconds before using the \002{1}\002 command again."), Config->GetModule("memoserv")->Get<time_t>("senddelay"), source.command);
+ else if (result == MemoServ::MemoServService::MEMO_TARGET_FULL)
+ source.Reply(_("Sorry, \002{0}\002 currently has too many memos and cannot receive more."), nick);
+ else
+ {
+ source.Reply(_("Memo sent to \002{0}\002."), nick);
+
+ bool ischan, isregistered;
+ MemoServ::MemoInfo *mi = MemoServ::service->GetMemoInfo(nick, ischan, isregistered, false);
+ if (mi == NULL)
+ throw CoreException("NULL mi in ms_rsend");
+ MemoServ::Memo *m = (mi->memos->size() ? mi->GetMemo(mi->memos->size() - 1) : NULL);
+ if (m != NULL)
+ m->receipt = true;
+ }
+ }
+#endif
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+#if 0
+ this->SendSyntax(source);
+ source.Reply(" ");
+ source.Reply(_("Sends the named \037nick\037 or \037channel\037 a memo containing\n"
+ "\037memo-text\037. When sending to a nickname, the recipient will\n"
+ "receive a notice that he/she has a new memo. The target\n"
+ "nickname/channel must be registered.\n"
+ "Once the memo is read by its recipient, an automatic notification\n"
+ "memo will be sent to the sender informing him/her that the memo\n"
+ "has been read."));
+ return true;
+#endif
+ }
+};
+
+class MSRSend : public Module
+{
+ CommandMSRSend commandmsrsend;
+
+ public:
+ MSRSend(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandmsrsend(this)
+ {
+ if (!MemoServ::service)
+ throw ModuleException("No MemoServ!");
+ throw ModuleException("XXX");
+ }
+};
+
+MODULE_INIT(MSRSend)
diff --git a/modules/memoserv/send.cpp b/modules/memoserv/send.cpp
new file mode 100644
index 000000000..f06e62bab
--- /dev/null
+++ b/modules/memoserv/send.cpp
@@ -0,0 +1,89 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/memoserv.h"
+
+class CommandMSSend : public Command
+{
+ public:
+ CommandMSSend(Module *creator) : Command(creator, "memoserv/send", 2, 2)
+ {
+ this->SetDesc(_("Send a memo to a nick or channel"));
+ this->SetSyntax(_("{\037user\037 | \037channel\037} \037memo-text\037"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ if (!MemoServ::service)
+ return;
+
+ const Anope::string &nick = params[0];
+ const Anope::string &text = params[1];
+
+ if (Anope::ReadOnly && !source.IsOper())
+ {
+ source.Reply(_("Sorry, memo sending is temporarily disabled."));
+ return;
+ }
+
+ if (source.GetAccount()->HasFieldS("UNCONFIRMED"))
+ {
+ source.Reply(_("You must confirm your account before you may send a memo."));
+ return;
+ }
+
+ MemoServ::MemoServService::MemoResult result = MemoServ::service->Send(source.GetNick(), nick, text);
+ if (result == MemoServ::MemoServService::MEMO_SUCCESS)
+ {
+ source.Reply(_("Memo sent to \002%s\002."), nick.c_str());
+ Log(LOG_COMMAND, source, this) << "to send a memo to " << nick;
+ }
+ else if (result == MemoServ::MemoServService::MEMO_INVALID_TARGET)
+ source.Reply(_("\002{0}\002 is not a registered unforbidden nick or channel."), nick);
+ else if (result == MemoServ::MemoServService::MEMO_TOO_FAST)
+ source.Reply(_("Please wait \002{0}\002 seconds before using the \002{1}\002 command again."), Config->GetModule("memoserv")->Get<time_t>("senddelay"), source.command);
+ else if (result == MemoServ::MemoServService::MEMO_TARGET_FULL)
+ source.Reply(_("Sorry, \002{0}\002 currently has too many memos and cannot receive more."), nick);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Sends the named \037user\037 or \037channel\037 a memo containing \037memo-text\037."
+ " The recipient will receive a notice that they have a new memo."
+ " The target user or channel must be registered."));
+ return true;
+ }
+};
+
+class MSSend : public Module
+{
+ CommandMSSend commandmssend;
+
+ public:
+ MSSend(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandmssend(this)
+ {
+
+ if (!MemoServ::service)
+ throw ModuleException("No MemoServ!");
+ }
+};
+
+MODULE_INIT(MSSend)
diff --git a/modules/memoserv/sendall.cpp b/modules/memoserv/sendall.cpp
new file mode 100644
index 000000000..9ab2a649e
--- /dev/null
+++ b/modules/memoserv/sendall.cpp
@@ -0,0 +1,68 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/memoserv.h"
+
+class CommandMSSendAll : public Command
+{
+ public:
+ CommandMSSendAll(Module *creator) : Command(creator, "memoserv/sendall", 1, 1)
+ {
+ this->SetDesc(_("Send a memo to all registered users"));
+ this->SetSyntax(_("\037memo-text\037"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ if (!MemoServ::service)
+ return;
+
+ const Anope::string &text = params[0];
+
+ Log(LOG_ADMIN, source, this) << "to send " << text;
+
+ for (NickServ::Account *nc : NickServ::service->GetAccountList())
+ if (nc != source.nc)
+ MemoServ::service->Send(source.GetNick(), nc->GetDisplay(), text);
+
+ source.Reply(_("A mass memo has been sent to all registered users."));
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Sends all registered users a memo containing \037memo-text\037."));
+ return true;
+ }
+};
+
+class MSSendAll : public Module
+{
+ CommandMSSendAll commandmssendall;
+
+ public:
+ MSSendAll(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandmssendall(this)
+ {
+ if (!MemoServ::service)
+ throw ModuleException("No MemoServ!");
+ }
+};
+
+MODULE_INIT(MSSendAll)
diff --git a/modules/memoserv/set.cpp b/modules/memoserv/set.cpp
new file mode 100644
index 000000000..72194d518
--- /dev/null
+++ b/modules/memoserv/set.cpp
@@ -0,0 +1,322 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+
+class CommandMSSet : public Command
+{
+ private:
+ void DoNotify(CommandSource &source, const std::vector<Anope::string> &params, MemoServ::MemoInfo *mi)
+ {
+ const Anope::string &param = params[1];
+ NickServ::Account *nc = source.nc;
+ ServiceBot *MemoServ = Config->GetClient("MemoServ");
+
+ if (!MemoServ)
+ return;
+
+ if (param.equals_ci("ON"))
+ {
+ nc->SetS<bool>("MEMO_SIGNON", true);
+ nc->SetS<bool>("MEMO_RECEIVE", true);
+ source.Reply(_("\002{0}\002 will now notify you of memos when you log on and when they are sent to you."), MemoServ->nick);
+ }
+ else if (param.equals_ci("LOGON"))
+ {
+ nc->SetS<bool>("MEMO_SIGNON", true);
+ nc->UnsetS<bool>("MEMO_RECEIVE");
+ source.Reply(_("\002{0}\002 will now notify you of memos when you log on or unset /AWAY."), MemoServ->nick);
+ }
+ else if (param.equals_ci("NEW"))
+ {
+ nc->UnsetS<bool>("MEMO_SIGNON");
+ nc->SetS<bool>("MEMO_RECEIVE", true);
+ source.Reply(_("\002{0}\002 will now notify you of memos when they are sent to you."), MemoServ->nick);
+ }
+ else if (param.equals_ci("MAIL"))
+ {
+ if (!nc->GetEmail().empty())
+ {
+ nc->SetS<bool>("MEMO_MAIL", true);
+ source.Reply(_("You will now be informed about new memos via email."));
+ }
+ else
+ source.Reply(_("There's no email address set for your nick."));
+ }
+ else if (param.equals_ci("NOMAIL"))
+ {
+ nc->UnsetS<bool>("MEMO_MAIL");
+ source.Reply(_("You will no longer be informed via email."));
+ }
+ else if (param.equals_ci("OFF"))
+ {
+ nc->UnsetS<bool>("MEMO_SIGNON");
+ nc->UnsetS<bool>("MEMO_RECEIVE");
+ nc->UnsetS<bool>("MEMO_MAIL");
+ source.Reply(_("\002{0}\002 will not send you any notification of memos."), MemoServ->nick);
+ }
+ else
+ this->OnSyntaxError(source, "");
+ }
+
+ void DoLimit(CommandSource &source, const std::vector<Anope::string> &params, MemoServ::MemoInfo *mi)
+ {
+
+ Anope::string p1 = params[1];
+ Anope::string p2 = params.size() > 2 ? params[2] : "";
+ Anope::string p3 = params.size() > 3 ? params[3] : "";
+ Anope::string user, chan;
+ int16_t limit;
+ NickServ::Account *nc = source.nc;
+ ChanServ::Channel *ci = NULL;
+ bool is_servadmin = source.HasPriv("memoserv/set-limit");
+
+ if (p1[0] == '#')
+ {
+ chan = p1;
+ p1 = p2;
+ p2 = p3;
+ p3 = params.size() > 4 ? params[4] : "";
+
+ ci = ChanServ::Find(chan);
+ if (!ci)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
+ return;
+ }
+
+ if (!is_servadmin && !source.AccessFor(ci).HasPriv("MEMO"))
+ {
+ source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "MEMO", ci->GetName());
+ return;
+ }
+ mi = ci->GetMemos();
+ }
+ if (is_servadmin)
+ {
+ if (!p2.empty() && !p2.equals_ci("HARD") && chan.empty())
+ {
+ NickServ::Nick *na;
+ if (!(na = NickServ::FindNick(p1)))
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), p1);
+ return;
+ }
+ user = p1;
+ mi = na->GetAccount()->GetMemos();
+ nc = na->GetAccount();
+ p1 = p2;
+ p2 = p3;
+ }
+ else if (p1.empty() || (!p1.is_pos_number_only() && !p1.equals_ci("NONE")) || (!p2.empty() && !p2.equals_ci("HARD")))
+ {
+ this->OnSyntaxError(source, "");
+ return;
+ }
+ if (!chan.empty())
+ {
+ if (!p2.empty())
+ ci->SetS<bool>("MEMO_HARDMAX", true);
+ else
+ ci->UnsetS<bool>("MEMO_HARDMAX");
+ }
+ else
+ {
+ if (!p2.empty())
+ nc->SetS<bool>("MEMO_HARDMAX", true);
+ else
+ nc->UnsetS<bool>("MEMO_HARDMAX");
+ }
+ limit = -1;
+ try
+ {
+ limit = convertTo<int16_t>(p1);
+ }
+ catch (const ConvertException &) { }
+ }
+ else
+ {
+ if (p1.empty() || !p2.empty() || !isdigit(p1[0]))
+ {
+ this->OnSyntaxError(source, "");
+ return;
+ }
+ if (!chan.empty() && ci->HasFieldS("MEMO_HARDMAX"))
+ {
+ source.Reply(_("The memo limit for \002{0}\002 may not be changed."), chan);
+ return;
+ }
+ if (chan.empty() && nc->HasFieldS("MEMO_HARDMAX"))
+ {
+ source.Reply(_("You are not permitted to change your memo limit."));
+ return;
+ }
+ int max_memos = Config->GetModule("memoserv")->Get<int>("maxmemos");
+ limit = -1;
+ try
+ {
+ limit = convertTo<int16_t>(p1);
+ }
+ catch (const ConvertException &) { }
+ /* The first character is a digit, but we could still go negative
+ * from overflow... watch out! */
+ if (limit < 0 || (max_memos > 0 && limit > max_memos))
+ {
+ if (!chan.empty())
+ source.Reply(_("You cannot set the memo limit for \002{0}\002 higher than \002{1}\002."), chan, max_memos);
+ else
+ source.Reply(_("You cannot set your memo limit higher than \002{0}\002."), max_memos);
+ return;
+ }
+ }
+ mi->SetMemoMax(limit);
+ if (limit > 0)
+ {
+ if (chan.empty() && nc == source.nc)
+ source.Reply(_("Your memo limit has been set to \002{0}\002."), limit);
+ else
+ source.Reply(_("Memo limit for \002{0}\002 set to \002{1}\002."), !chan.empty() ? chan : user, limit);
+ }
+ else if (!limit)
+ {
+ if (chan.empty() && nc == source.nc)
+ source.Reply(_("You will no longer be able to receive memos."));
+ else
+ source.Reply(_("Memo limit for \002{0}\002 set to \0020\002."), !chan.empty() ? chan : user);
+ }
+ else
+ {
+ if (chan.empty() && nc == source.nc)
+ source.Reply(_("Your memo limit has been disabled."));
+ else
+ source.Reply(_("Memo limit \002disabled\002 for \002{0}\002."), !chan.empty() ? chan : user);
+ }
+ }
+ public:
+ CommandMSSet(Module *creator) : Command(creator, "memoserv/set", 2, 5)
+ {
+ this->SetDesc(_("Set options related to memos"));
+ this->SetSyntax(_("\037option\037 \037parameters\037"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &cmd = params[0];
+ MemoServ::MemoInfo *mi = source.nc->GetMemos();
+
+ if (Anope::ReadOnly)
+ source.Reply(_("Sorry, memo option setting is temporarily disabled."));
+ else if (cmd.equals_ci("NOTIFY"))
+ return this->DoNotify(source, params, mi);
+ else if (cmd.equals_ci("LIMIT"))
+ return this->DoLimit(source, params, mi);
+ else
+ this->OnSyntaxError(source, "");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ if (subcommand.empty())
+ {
+ CommandInfo *help = source.service->FindCommand("generic/help");
+ if (!help)
+ return false;
+ source.Reply(_("Sets various memo options. \037option\037 can be one of:\n"
+ "\n"
+ " NOTIFY Changes when you will be notified about\n"
+ " new memos (only for nicknames)\n"
+ " LIMIT Sets the maximum number of memos you can\n"
+ " receive\n"
+ "\n"
+ "Type \002{0}{1} {2} {3} \037option\037\002 for more information on a specific option."),
+ Config->StrictPrivmsg, source.service->nick, help->cname, source.command);
+ }
+ else if (subcommand.equals_ci("NOTIFY"))
+ source.Reply(_("Syntax: \002NOTIFY {ON | LOGON | NEW | MAIL | NOMAIL | OFF}\002\n"
+ "\n"
+ "Changes when you will be notified about new memos:\n"
+ "\n"
+ " ON You will be notified of memos when you log on,\n"
+ " when you unset /AWAY, and when they are sent\n"
+ " to you.\n"
+ "\n"
+ " LOGON You will only be notified of memos when you log\n"
+ " on or when you unset /AWAY.\n"
+ "\n"
+ " NEW You will only be notified of memos when they\n"
+ " are sent to you.\n"
+ "\n"
+ " MAIL You will be notified of memos by email as well as\n"
+ " any other settings you have.\n"
+ "\n"
+ " NOMAIL You will not be notified of memos by email.\n"
+ "\n"
+ " OFF You will not receive any notification of memos.\n"
+ "\n"
+ "\002ON\002 is essentially \002LOGON\002 and \002NEW\002 combined."));
+ else if (subcommand.equals_ci("LIMIT"))
+ {
+ int max_memos = Config->GetModule("memoserv")->Get<int>("maxmemos");
+ if (source.IsServicesOper())
+ source.Reply(_("Syntax: \002LIMIT [\037user\037 | \037channel\037] {\037limit\037 | NONE} [HARD]\002\n"
+ "\n"
+ "Sets the maximum number of memos a user or channel is allowed to have."
+ " Setting the limit to 0 prevents the user from receiving any memos; setting it to \002NONE\002 allows the user to receive and keep as many memos as they want."
+ " If you do not give a nickname or channel, your own limit is set.\n"
+ "\n"
+ "Adding \002HARD\002 prevents the user from changing the limit."
+ " Not adding \002HARD\002 has the opposite effect, allowing the user to change the limit, even if a previous limit was set.\n"
+ " \n"
+ "This use of the \002{0} LIMIT\002 command is limited to \002Services Operators\002."
+ " Other users may only enter a limit for themselves or a channel on which they have the \002MEMO\002 privilege on, may not remove their limit, may not set a limit above {1}, and may not set a hard limit."),
+ source.command, max_memos);
+ else
+ source.Reply(_("Syntax: \002LIMIT [\037channel\037] \037limit\037\002\n"
+ "\n"
+ "Sets the maximum number of memos you, or the given channel, are allowed to have."
+ " If you set this to 0, no one will be able to send any memos to you."
+ "However, you cannot set this any higher than {0}."), max_memos);
+ }
+ else
+ return false;
+
+ return true;
+ }
+};
+
+class MSSet : public Module
+{
+ CommandMSSet commandmsset;
+ Serialize::Field<NickServ::Account, bool> memo_signon, memo_receive, memo_mail, memo_hardmax_nick;
+ Serialize::Field<ChanServ::Channel, bool> memo_hardmax_channel;
+
+ public:
+ MSSet(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandmsset(this)
+ , memo_signon(this, "MEMO_SIGNON")
+ , memo_receive(this, "MEMO_RECEIVE")
+ , memo_mail(this, "MEMO_MAIL")
+ , memo_hardmax_nick(this, "MEMO_HARDMAX")
+ , memo_hardmax_channel(this, "MEMO_HARDMAX")
+ {
+
+ }
+};
+
+MODULE_INIT(MSSet)
diff --git a/modules/memoserv/staff.cpp b/modules/memoserv/staff.cpp
new file mode 100644
index 000000000..45883bbb6
--- /dev/null
+++ b/modules/memoserv/staff.cpp
@@ -0,0 +1,65 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/memoserv.h"
+
+class CommandMSStaff : public Command
+{
+ public:
+ CommandMSStaff(Module *creator) : Command(creator, "memoserv/staff", 1, 1)
+ {
+ this->SetDesc(_("Send a memo to all opers/admins"));
+ this->SetSyntax(_("\037memo-text\037"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ if (!MemoServ::service)
+ return;
+
+ const Anope::string &text = params[0];
+
+ for (NickServ::Account *nc : NickServ::service->GetAccountList())
+ if (source.nc != nc && nc->IsServicesOper())
+ MemoServ::service->Send(source.GetNick(), nc->GetDisplay(), text, true);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Sends all services staff a memo containing \037memo-text\037."));
+
+ return true;
+ }
+};
+
+class MSStaff : public Module
+{
+ CommandMSStaff commandmsstaff;
+
+ public:
+ MSStaff(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandmsstaff(this)
+ {
+ if (!MemoServ::service)
+ throw ModuleException("No MemoServ!");
+ }
+};
+
+MODULE_INIT(MSStaff)
diff --git a/modules/nickserv/CMakeLists.txt b/modules/nickserv/CMakeLists.txt
new file mode 100644
index 000000000..cd225a94d
--- /dev/null
+++ b/modules/nickserv/CMakeLists.txt
@@ -0,0 +1 @@
+build_modules(${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/modules/nickserv/access.cpp b/modules/nickserv/access.cpp
new file mode 100644
index 000000000..bff710cc4
--- /dev/null
+++ b/modules/nickserv/access.cpp
@@ -0,0 +1,272 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/nickserv.h"
+#include "modules/nickserv/access.h"
+
+class NickAccessImpl : public NickAccess
+{
+ friend class NickAccessType;
+
+ NickServ::Account *account = nullptr;
+ Anope::string mask;
+
+ public:
+ NickAccessImpl(Serialize::TypeBase *type) : NickAccess(type) { }
+ NickAccessImpl(Serialize::TypeBase *type, Serialize::ID id) : NickAccess(type, id) { }
+
+ NickServ::Account *GetAccount() override;
+ void SetAccount(NickServ::Account *) override;
+
+ Anope::string GetMask() override;
+ void SetMask(const Anope::string &) override;
+};
+
+class NickAccessType : public Serialize::Type<NickAccessImpl>
+{
+ public:
+ Serialize::ObjectField<NickAccessImpl, NickServ::Account *> account;
+ Serialize::Field<NickAccessImpl, Anope::string> mask;
+
+ NickAccessType(Module *creator) : Serialize::Type<NickAccessImpl>(creator)
+ , account(this, "account", &NickAccessImpl::account, true)
+ , mask(this, "mask", &NickAccessImpl::mask)
+ {
+ }
+};
+
+NickServ::Account *NickAccessImpl::GetAccount()
+{
+ return Get(&NickAccessType::account);
+}
+
+void NickAccessImpl::SetAccount(NickServ::Account *acc)
+{
+ Set(&NickAccessType::account, acc);
+}
+
+Anope::string NickAccessImpl::GetMask()
+{
+ return Get(&NickAccessType::mask);
+}
+
+void NickAccessImpl::SetMask(const Anope::string &m)
+{
+ Set(&NickAccessType::mask, m);
+}
+
+class CommandNSAccess : public Command
+{
+ private:
+ void DoAdd(CommandSource &source, NickServ::Account *nc, const Anope::string &mask)
+ {
+ if (mask.empty())
+ {
+ this->OnSyntaxError(source, "ADD");
+ return;
+ }
+
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ std::vector<NickAccess *> access = nc->GetRefs<NickAccess *>();
+
+ if (access.size() >= Config->GetModule(this->GetOwner())->Get<unsigned>("accessmax", "32"))
+ {
+ source.Reply(_("Sorry, the maximum of \002{0}\002 access entries has been reached."), Config->GetModule(this->GetOwner())->Get<unsigned>("accessmax"));
+ return;
+ }
+
+ for (NickAccess *a : access)
+ if (a->GetMask().equals_ci(mask))
+ {
+ source.Reply(_("Mask \002{0}\002 already present on the access list of \002{1}\002."), mask, nc->GetDisplay());
+ return;
+ }
+
+ NickAccess *a = Serialize::New<NickAccess *>();
+ a->SetAccount(nc);
+ a->SetMask(mask);
+
+ Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to ADD mask " << mask << " to " << nc->GetDisplay();
+ source.Reply(_("\002{0}\002 added to the access list of \002{1}\002."), mask, nc->GetDisplay());
+ }
+
+ void DoDel(CommandSource &source, NickServ::Account *nc, const Anope::string &mask)
+ {
+ if (mask.empty())
+ {
+ this->OnSyntaxError(source, "DEL");
+ return;
+ }
+
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ for (NickAccess *a : nc->GetRefs<NickAccess *>())
+ if (a->GetMask().equals_ci(mask))
+ {
+ a->Delete();
+ Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to DELETE mask " << mask << " from " << nc->GetDisplay();
+ source.Reply(_("\002{0}\002 deleted from the access list of \002{1}\002."), mask, nc->GetDisplay());
+ return;
+ }
+
+
+ source.Reply(_("\002{0}\002 not found on the access list of \002{1}\002."), mask, nc->GetDisplay());
+ }
+
+ void DoList(CommandSource &source, NickServ::Account *nc, const Anope::string &mask)
+ {
+ std::vector<NickAccess *> access = nc->GetRefs<NickAccess *>();
+ if (access.empty())
+ {
+ source.Reply(_("The access list of \002{0}\002 is empty."), nc->GetDisplay());
+ return;
+ }
+
+ source.Reply(_("Access list for \002{0}\002:"), nc->GetDisplay());
+ for (NickAccess *a : access)
+ {
+ if (!mask.empty() && !Anope::Match(a->GetMask(), mask))
+ continue;
+
+ source.Reply(" {0}", a->GetMask());
+ }
+ }
+ public:
+ CommandNSAccess(Module *creator) : Command(creator, "nickserv/access", 1, 3)
+ {
+ this->SetDesc(_("Modify the list of authorized addresses"));
+ this->SetSyntax(_("ADD [\037nickname\037] \037mask\037"));
+ this->SetSyntax(_("DEL [\037nickname\037] \037mask\037"));
+ this->SetSyntax(_("LIST [\037nickname\037]"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &cmd = params[0];
+ Anope::string nick, mask;
+
+ if (cmd.equals_ci("LIST"))
+ nick = params.size() > 1 ? params[1] : "";
+ else
+ {
+ nick = params.size() == 3 ? params[1] : "";
+ mask = params.size() > 1 ? params[params.size() - 1] : "";
+ }
+
+ NickServ::Account *nc;
+ if (!nick.empty() && source.HasPriv("nickserv/access"))
+ {
+ NickServ::Nick *na = NickServ::FindNick(nick);
+ if (na == NULL)
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), nick);
+ return;
+ }
+
+ if (Config->GetModule("nickserv")->Get<bool>("secureadmins", "yes") && source.GetAccount() != na->GetAccount() && na->GetAccount()->IsServicesOper() && !cmd.equals_ci("LIST"))
+ {
+ source.Reply(_("You may view but not modify the access list of other Services Operators."));
+ return;
+ }
+
+ nc = na->GetAccount();
+ }
+ else
+ nc = source.nc;
+
+ if (!mask.empty() && (mask.find('@') == Anope::string::npos || mask.find('!') != Anope::string::npos))
+ {
+ source.Reply(_("Mask must be in the form \037user\037@\037host\037."));
+ source.Reply(_("\002%s%s HELP %s\002 for more information."), Config->StrictPrivmsg, source.service->nick, source.command); // XXX
+ }
+ else if (cmd.equals_ci("LIST"))
+ return this->DoList(source, nc, mask);
+ else if (nc->HasFieldS("NS_SUSPENDED"))
+ source.Reply(_("\002{0}\002 is suspended."), nc->GetDisplay());
+ else if (cmd.equals_ci("ADD"))
+ return this->DoAdd(source, nc, mask);
+ else if (cmd.equals_ci("DEL"))
+ return this->DoDel(source, nc, mask);
+ else
+ this->OnSyntaxError(source, "");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Modifies or displays the access list for your account."
+ " The access list is a list of addresses that {1} uses to recognize you."
+ " If you match one of the hosts on the access list, services will not force you to change your nickname if the \002KILL\002 option is set."
+ " Furthermore, if the \002SECURE\002 option is disabled, services will recognize you just based on your hostmask, without having to supply a password."
+ " To gain access to channels when only recognized by your hostmask, the channel must too have the \002SECURE\002 option off."
+ " Services Operators may provide \037nickname\037 to modify other user's access lists.\n"
+ "\n"
+ "Examples:\n"
+ " \n"
+ " {command} ADD anyone@*.bepeg.com\n"
+ " Allows access to user \"anyone\" from any machine in the \"bepeg.com\" domain.\n"
+ "\n"
+ " {command} DEL anyone@*.bepeg.com\n"
+ " Reverses the previous command.\n"
+ "\n"
+ " {command} LIST\n"
+ " Displays the current access list."),
+ source.command, source.service->nick);
+ return true;
+ }
+};
+
+class NSAccess : public Module
+ , public EventHook<NickServ::Event::NickRegister>
+{
+ CommandNSAccess commandnsaccess;
+ NickAccessType nick_type;
+
+ public:
+ NSAccess(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , EventHook<NickServ::Event::NickRegister>(this)
+ , commandnsaccess(this)
+ , nick_type(this)
+ {
+ }
+
+ void OnNickRegister(User *u, NickServ::Nick *na, const Anope::string &) override
+ {
+ if (u && Config->GetModule(this)->Get<bool>("addaccessonreg"))
+ {
+ NickAccess *a = Serialize::New<NickAccess *>();
+ a->SetAccount(na->GetAccount());
+ a->SetMask(u->Mask());
+
+ u->SendMessage(Config->GetClient("NickServ"),
+ _("\002{0}\002 has been registered under your hostmask: \002{1}\002"), na->GetNick(), a->GetMask());
+ }
+ }
+};
+
+MODULE_INIT(NSAccess)
diff --git a/modules/nickserv/ajoin.cpp b/modules/nickserv/ajoin.cpp
new file mode 100644
index 000000000..98d93e8fe
--- /dev/null
+++ b/modules/nickserv/ajoin.cpp
@@ -0,0 +1,396 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2011-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "module.h"
+#include "modules/nickserv/ajoin.h"
+
+class AutoJoinImpl : public AutoJoin
+{
+ friend class AutoJoinType;
+
+ NickServ::Account *account = nullptr;
+ Anope::string channel, key;
+
+ public:
+ AutoJoinImpl(Serialize::TypeBase *type) : AutoJoin(type) { }
+ AutoJoinImpl(Serialize::TypeBase *type, Serialize::ID id) : AutoJoin(type, id) { }
+
+ NickServ::Account *GetOwner() override;
+ void SetOwner(NickServ::Account *acc) override;
+
+ Anope::string GetChannel() override;
+ void SetChannel(const Anope::string &c) override;
+
+ Anope::string GetKey() override;
+ void SetKey(const Anope::string &k) override;
+};
+
+class AutoJoinType : public Serialize::Type<AutoJoinImpl>
+{
+ public:
+ Serialize::ObjectField<AutoJoinImpl, NickServ::Account *> owner;
+ Serialize::Field<AutoJoinImpl, Anope::string> channel, key;
+
+ AutoJoinType(Module *me) : Serialize::Type<AutoJoinImpl>(me)
+ , owner(this, "owner", &AutoJoinImpl::account, true)
+ , channel(this, "channel", &AutoJoinImpl::channel)
+ , key(this, "key", &AutoJoinImpl::key)
+ {
+ }
+};
+
+NickServ::Account *AutoJoinImpl::GetOwner()
+{
+ return Get(&AutoJoinType::owner);
+}
+
+void AutoJoinImpl::SetOwner(NickServ::Account *acc)
+{
+ Set(&AutoJoinType::owner, acc);
+}
+
+Anope::string AutoJoinImpl::GetChannel()
+{
+ return Get(&AutoJoinType::channel);
+}
+
+void AutoJoinImpl::SetChannel(const Anope::string &c)
+{
+ Set(&AutoJoinType::channel, c);
+}
+
+Anope::string AutoJoinImpl::GetKey()
+{
+ return Get(&AutoJoinType::key);
+}
+
+void AutoJoinImpl::SetKey(const Anope::string &k)
+{
+ Set(&AutoJoinType::key, k);
+}
+
+class CommandNSAJoin : public Command
+{
+ void DoList(CommandSource &source, NickServ::Account *nc)
+ {
+ std::vector<AutoJoin *> channels = nc->GetRefs<AutoJoin *>();
+
+ if (channels.empty())
+ {
+ source.Reply(_("The auto join list of \002{0}\002 is empty."), nc->GetDisplay());
+ return;
+ }
+
+ ListFormatter list(source.GetAccount());
+ list.AddColumn(_("Number")).AddColumn(_("Channel")).AddColumn(_("Key"));
+ for (unsigned i = 0; i < channels.size(); ++i)
+ {
+ AutoJoin *aj = channels[i];
+ ListFormatter::ListEntry entry;
+ entry["Number"] = stringify(i + 1);
+ entry["Channel"] = aj->GetChannel();
+ entry["Key"] = aj->GetKey();
+ list.AddEntry(entry);
+ }
+
+ source.Reply(_("Auto join list of \002{0}\002:"), nc->GetDisplay());
+
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+ }
+
+ void DoAdd(CommandSource &source, NickServ::Account *nc, const Anope::string &chans, const Anope::string &keys)
+ {
+ std::vector<AutoJoin *> channels = nc->GetRefs<AutoJoin *>();
+
+ Anope::string addedchans;
+ Anope::string alreadyadded;
+ Anope::string invalidkey;
+ commasepstream ksep(keys, true);
+ commasepstream csep(chans);
+ for (Anope::string chan, key; csep.GetToken(chan);)
+ {
+ ksep.GetToken(key);
+
+ unsigned i = 0;
+ for (; i < channels.size(); ++i)
+ if (channels[i]->GetChannel().equals_ci(chan))
+ break;
+
+ if (channels.size() >= Config->GetModule(this->GetOwner())->Get<unsigned>("ajoinmax"))
+ {
+ source.Reply(_("Sorry, the maximum of \002{0}\002 auto join entries has been reached."), Config->GetModule(this->GetOwner())->Get<unsigned>("ajoinmax"));
+ return;
+ }
+
+ if (i != channels.size())
+ alreadyadded += chan + ", ";
+ else if (IRCD->IsChannelValid(chan) == false)
+ source.Reply(_("\002{0}\002 isn't a valid channel."), chan);
+ else
+ {
+ Channel *c = Channel::Find(chan);
+ Anope::string k;
+ if (c && c->GetParam("KEY", k) && key != k)
+ {
+ invalidkey += chan + ", ";
+ continue;
+ }
+
+ AutoJoin *entry = Serialize::New<AutoJoin *>();
+ entry->SetOwner(nc);
+ entry->SetChannel(chan);
+ entry->SetKey(key);
+
+ addedchans += chan + ", ";
+ }
+ }
+
+ if (!alreadyadded.empty())
+ {
+ alreadyadded = alreadyadded.substr(0, alreadyadded.length() - 2);
+ source.Reply(_("\002{0}\002 is already on the auto join list of \002{1}\002."), alreadyadded, nc->GetDisplay());
+ }
+
+ if (!invalidkey.empty())
+ {
+ invalidkey = invalidkey.substr(0, invalidkey.length() - 2);
+ source.Reply(_("\002{0}\002 had an invalid key specified, and was ignored."), invalidkey);
+ }
+
+ if (addedchans.empty())
+ return;
+
+ addedchans = addedchans.substr(0, addedchans.length() - 2);
+ Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to ADD channel " << addedchans << " to " << nc->GetDisplay();
+ source.Reply(_("\002{0}\002 added to the auto join list of \002{1}\002."), addedchans, nc->GetDisplay());
+ }
+
+ void DoDel(CommandSource &source, NickServ::Account *nc, const Anope::string &chans)
+ {
+ std::vector<AutoJoin *> channels = nc->GetRefs<AutoJoin *>();
+ Anope::string delchans;
+ Anope::string notfoundchans;
+ commasepstream sep(chans);
+
+ for (Anope::string chan; sep.GetToken(chan);)
+ {
+ unsigned i = 0;
+ for (; i < channels.size(); ++i)
+ if (channels[i]->GetChannel().equals_ci(chan))
+ break;
+
+ if (i == channels.size())
+ notfoundchans += chan + ", ";
+ else
+ {
+ delete channels[i];
+ delchans += chan + ", ";
+ }
+ }
+
+ if (!notfoundchans.empty())
+ {
+ notfoundchans = notfoundchans.substr(0, notfoundchans.length() - 2);
+ source.Reply(_("\002{0}\002 was not found on the auto join list of \002{1}\002."), notfoundchans, nc->GetDisplay());
+ }
+
+ if (delchans.empty())
+ return;
+
+ delchans = delchans.substr(0, delchans.length() - 2);
+ Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to DELETE channel " << delchans << " from " << nc->GetDisplay();
+ source.Reply(_("\002{0}\002 was removed from the auto join list of \002{1}\002."), delchans, nc->GetDisplay());
+ }
+
+ public:
+ CommandNSAJoin(Module *creator) : Command(creator, "nickserv/ajoin", 1, 4)
+ {
+ this->SetDesc(_("Manage your auto join list"));
+ this->SetSyntax(_("ADD [\037nickname\037] \037channel\037 [\037key\037]"));
+ this->SetSyntax(_("DEL [\037nickname\037] \037channel\037"));
+ this->SetSyntax(_("LIST [\037nickname\037]"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &cmd = params[0];
+ Anope::string nick, param, param2;
+
+ if (cmd.equals_ci("LIST"))
+ nick = params.size() > 1 ? params[1] : "";
+ else
+ nick = (params.size() > 2 && IRCD->IsChannelValid(params[2])) ? params[1] : "";
+
+ NickServ::Account *nc;
+ if (!nick.empty() && !source.HasCommand("nickserv/ajoin"))
+ {
+ NickServ::Nick *na = NickServ::FindNick(nick);
+ if (na == NULL)
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), nick);
+ return;
+ }
+
+ nc = na->GetAccount();
+ param = params.size() > 2 ? params[2] : "";
+ param2 = params.size() > 3 ? params[3] : "";
+ }
+ else
+ {
+ nc = source.nc;
+ param = params.size() > 1 ? params[1] : "";
+ param2 = params.size() > 2 ? params[2] : "";
+ }
+
+ if (cmd.equals_ci("LIST"))
+ return this->DoList(source, nc);
+ else if (nc->HasFieldS("NS_SUSPENDED"))
+ source.Reply(_("\002{0}\002 isn't registered."), nc->GetDisplay());
+ else if (param.empty())
+ this->OnSyntaxError(source, "");
+ else if (Anope::ReadOnly)
+ source.Reply(_("Services are in read-only mode."));
+ else if (cmd.equals_ci("ADD"))
+ return this->DoAdd(source, nc, param, param2);
+ else if (cmd.equals_ci("DEL"))
+ return this->DoDel(source, nc, param);
+ else
+ this->OnSyntaxError(source, "");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("This command manages your auto join list."
+ " When you identify you will automatically join the channels on your auto join list."
+ " Services Operators may provide \037nickname\037 to modify other users' auto join lists."));
+ return true;
+ }
+};
+
+class NSAJoin : public Module
+ , public EventHook<Event::UserLogin>
+{
+ CommandNSAJoin commandnsajoin;
+ AutoJoinType ajtype;
+
+ public:
+ NSAJoin(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , EventHook<Event::UserLogin>(this)
+ , commandnsajoin(this)
+ , ajtype(this)
+ {
+
+ if (!IRCD || !IRCD->CanSVSJoin)
+ throw ModuleException("Your IRCd does not support SVSJOIN");
+
+ }
+
+ void OnUserLogin(User *u) override
+ {
+ ServiceBot *NickServ = Config->GetClient("NickServ");
+ if (!NickServ)
+ return;
+
+ std::vector<AutoJoin *> channels = u->Account()->GetRefs<AutoJoin *>();
+ if (channels.empty())
+ return;
+
+ /* Set +r now, so we can ajoin users into +R channels */
+ ModeManager::ProcessModes();
+
+ for (AutoJoin *entry : channels)
+ {
+ Channel *c = Channel::Find(entry->GetChannel());
+ ChanServ::Channel *ci;
+
+ if (c)
+ ci = c->ci;
+ else
+ ci = ChanServ::Find(entry->GetChannel());
+
+ bool need_invite = false;
+ Anope::string key = entry->GetKey();
+ ChanServ::AccessGroup u_access;
+
+ if (ci != NULL)
+ {
+ if (ci->HasFieldS("CS_SUSPENDED"))
+ continue;
+ u_access = ci->AccessFor(u);
+ }
+ if (c != NULL)
+ {
+ if (c->FindUser(u) != NULL)
+ continue;
+ else if (c->HasMode("OPERONLY") && !u->HasMode("OPER"))
+ continue;
+ else if (c->HasMode("ADMINONLY") && !u->HasMode("ADMIN"))
+ continue;
+ else if (c->HasMode("SSL") && !(u->HasMode("SSL") || u->HasExtOK("ssl")))
+ continue;
+ else if (c->MatchesList(u, "BAN") == true && c->MatchesList(u, "EXCEPT") == false)
+ need_invite = true;
+ else if (c->HasMode("INVITE") && c->MatchesList(u, "INVITEOVERRIDE") == false)
+ need_invite = true;
+
+ if (c->HasMode("KEY"))
+ {
+ Anope::string k;
+ if (c->GetParam("KEY", k))
+ {
+ if (u_access.HasPriv("GETKEY"))
+ key = k;
+ else if (key != k)
+ need_invite = true;
+ }
+ }
+ if (c->HasMode("LIMIT"))
+ {
+ Anope::string l;
+ if (c->GetParam("LIMIT", l))
+ {
+ try
+ {
+ unsigned limit = convertTo<unsigned>(l);
+ if (c->users.size() >= limit)
+ need_invite = true;
+ }
+ catch (const ConvertException &) { }
+ }
+ }
+ }
+
+ if (need_invite && c != NULL)
+ {
+ if (!u_access.HasPriv("INVITE"))
+ continue;
+ IRCD->SendInvite(NickServ, c, u);
+ }
+
+ IRCD->SendSVSJoin(NickServ, u, entry->GetChannel(), key);
+ }
+ }
+};
+
+MODULE_INIT(NSAJoin)
diff --git a/modules/nickserv/alist.cpp b/modules/nickserv/alist.cpp
new file mode 100644
index 000000000..3c8b573b0
--- /dev/null
+++ b/modules/nickserv/alist.cpp
@@ -0,0 +1,142 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "module.h"
+
+class CommandNSAList : public Command
+{
+ static bool ChannelSort(ChanServ::Channel *ci1, ChanServ::Channel *ci2)
+ {
+ return ci::less()(ci1->GetName(), ci2->GetName());
+ }
+
+ public:
+ CommandNSAList(Module *creator) : Command(creator, "nickserv/alist", 0, 2)
+ {
+ this->SetDesc(_("List channels you have access on"));
+ this->SetSyntax(_("[\037nickname\037]"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ Anope::string nick = source.GetNick();
+ NickServ::Account *nc = source.nc;
+
+ if (params.size() && source.HasPriv("nickserv/alist"))
+ {
+ nick = params[0];
+ NickServ::Nick *na = NickServ::FindNick(nick);
+ if (!na)
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), nick);
+ return;
+ }
+ nc = na->GetAccount();
+ }
+
+ ListFormatter list(source.GetAccount());
+ int chan_count = 0;
+
+ list.AddColumn(_("Number")).AddColumn(_("Channel")).AddColumn(_("Access")).AddColumn(_("Description"));
+
+ std::vector<ChanServ::Channel *> chans = nc->GetRefs<ChanServ::Channel *>();
+ std::sort(chans.begin(), chans.end(), ChannelSort);
+ for (ChanServ::Channel *ci : chans)
+ {
+ ListFormatter::ListEntry entry;
+
+ if (ci->GetFounder() == nc)
+ {
+ ++chan_count;
+ entry["Number"] = stringify(chan_count);
+ entry["Channel"] = (ci->HasFieldS("CS_NO_EXPIRE") ? "!" : "") + ci->GetName();
+ entry["Access"] = Language::Translate(source.GetAccount(), _("Founder"));
+ entry["Description"] = ci->GetDesc();
+ list.AddEntry(entry);
+ continue;
+ }
+
+ if (ci->GetSuccessor() == nc)
+ {
+ ++chan_count;
+ entry["Number"] = stringify(chan_count);
+ entry["Channel"] = (ci->HasFieldS("CS_NO_EXPIRE") ? "!" : "") + ci->GetName();
+ entry["Access"] = Language::Translate(source.GetAccount(), _("Successor"));
+ entry["Description"] = ci->GetDesc();
+ list.AddEntry(entry);
+ continue;
+ }
+
+ ChanServ::AccessGroup access = ci->AccessFor(nc, false);
+ if (access.empty())
+ continue;
+
+ ++chan_count;
+
+ entry["Number"] = stringify(chan_count);
+ entry["Channel"] = (ci->HasFieldS("CS_NO_EXPIRE") ? "!" : "") + ci->GetName();
+ for (unsigned j = 0; j < access.size(); ++j)
+ entry["Access"] = entry["Access"] + ", " + access[j]->AccessSerialize();
+ entry["Access"] = entry["Access"].substr(2);
+ entry["Description"] = ci->GetDesc();
+ list.AddEntry(entry);
+ }
+
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+ if (!chan_count)
+ {
+ source.Reply(_("\002{0}\002 has no access in any channels."), nc->GetDisplay());
+ }
+ else
+ {
+ source.Reply(_("Channels that \002{0}\002 has access on:"), nc->GetDisplay());
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+
+ source.Reply(_("End of list - \002{0}\002 channels shown."), chan_count);
+ }
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Lists all channels you have access on.\n"
+ " \n"
+ "Channels that have the \037NOEXPIRE\037 option set will be prefixed by an exclamation mark. The nickname parameter is limited to Services Operators"));
+
+ return true;
+ }
+};
+
+class NSAList : public Module
+{
+ CommandNSAList commandnsalist;
+
+ public:
+ NSAList(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandnsalist(this)
+ {
+
+ }
+};
+
+MODULE_INIT(NSAList)
diff --git a/modules/nickserv/cert.cpp b/modules/nickserv/cert.cpp
new file mode 100644
index 000000000..c7bd2a207
--- /dev/null
+++ b/modules/nickserv/cert.cpp
@@ -0,0 +1,391 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2011-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "module.h"
+#include "modules/nickserv/cert.h"
+#include "modules/nickserv.h"
+
+static Anope::hash_map<NickServ::Account *> certmap;
+
+class CertServiceImpl : public CertService
+{
+ public:
+ CertServiceImpl(Module *o) : CertService(o) { }
+
+ NickServ::Account* FindAccountFromCert(const Anope::string &cert) override
+ {
+ Anope::hash_map<NickServ::Account *>::iterator it = certmap.find(cert);
+ if (it != certmap.end())
+ return it->second;
+ return NULL;
+ }
+
+ bool Matches(User *u, NickServ::Account *nc) override
+ {
+ std::vector<NSCertEntry *> cl = nc->GetRefs<NSCertEntry *>();
+ return !u->fingerprint.empty() && FindCert(cl, u->fingerprint);
+ }
+
+ NSCertEntry *FindCert(const std::vector<NSCertEntry *> &cl, const Anope::string &certfp) override
+ {
+ for (NSCertEntry *e : cl)
+ if (e->GetCert() == certfp)
+ return e;
+ return nullptr;
+ }
+};
+
+class NSCertEntryImpl : public NSCertEntry
+{
+ friend class NSCertEntryType;
+
+ NickServ::Account *account = nullptr;
+ Anope::string cert;
+
+ public:
+ NSCertEntryImpl(Serialize::TypeBase *type) : NSCertEntry(type) { }
+ NSCertEntryImpl(Serialize::TypeBase *type, Serialize::ID id) : NSCertEntry(type, id) { }
+ ~NSCertEntryImpl();
+
+ NickServ::Account *GetAccount() override;
+ void SetAccount(NickServ::Account *) override;
+
+ Anope::string GetCert() override;
+ void SetCert(const Anope::string &) override;
+};
+
+class NSCertEntryType : public Serialize::Type<NSCertEntryImpl>
+{
+ public:
+ struct Account : Serialize::ObjectField<NSCertEntryImpl, NickServ::Account *>
+ {
+ using Serialize::ObjectField<NSCertEntryImpl, NickServ::Account *>::ObjectField;
+
+ void SetField(NSCertEntryImpl *s, NickServ::Account *acc) override
+ {
+ const Anope::string &cert = s->GetCert();
+ if (!cert.empty())
+ certmap.erase(cert);
+
+ Serialize::ObjectField<NSCertEntryImpl, NickServ::Account *>::SetField(s, acc);
+
+ if (!cert.empty() && s->GetAccount())
+ certmap[cert] = acc;
+ }
+ } nc;
+
+ struct Mask : Serialize::Field<NSCertEntryImpl, Anope::string>
+ {
+ using Serialize::Field<NSCertEntryImpl, Anope::string>::Field;
+
+ void SetField(NSCertEntryImpl *s, const Anope::string &m) override
+ {
+ const Anope::string &old = GetField(s);
+ if (!old.empty())
+ certmap.erase(old);
+
+ Serialize::Field<NSCertEntryImpl, Anope::string>::SetField(s, m);
+
+ if (!m.empty() && s->GetAccount())
+ certmap[m] = s->GetAccount();
+ }
+ } mask;
+
+ NSCertEntryType(Module *me) : Serialize::Type<NSCertEntryImpl>(me)
+ , nc(this, "nc", &NSCertEntryImpl::account, true)
+ , mask(this, "mask", &NSCertEntryImpl::cert)
+ {
+ }
+};
+
+NSCertEntryImpl::~NSCertEntryImpl()
+{
+ const Anope::string &old = GetCert();
+ if (!old.empty())
+ certmap.erase(old);
+}
+
+NickServ::Account *NSCertEntryImpl::GetAccount()
+{
+ return Get<NickServ::Account *>(&NSCertEntryType::nc);
+}
+
+void NSCertEntryImpl::SetAccount(NickServ::Account *nc)
+{
+ Set(&NSCertEntryType::nc, nc);
+}
+
+Anope::string NSCertEntryImpl::GetCert()
+{
+ return Get<Anope::string>(&NSCertEntryType::mask);
+}
+
+void NSCertEntryImpl::SetCert(const Anope::string &mask)
+{
+ Set(&NSCertEntryType::mask, mask);
+}
+
+class CommandNSCert : public Command
+{
+ NSCertEntry *FindCert(const std::vector<NSCertEntry *> &cl, const Anope::string &certfp)
+ {
+ for (NSCertEntry *e : cl)
+ if (e->GetCert() == certfp)
+ return e;
+ return nullptr;
+ }
+
+ void DoAdd(CommandSource &source, NickServ::Account *nc, Anope::string certfp)
+ {
+ std::vector<NSCertEntry *> cl = nc->GetRefs<NSCertEntry *>();
+ unsigned max = Config->GetModule(this->GetOwner())->Get<unsigned>("max", "5");
+
+ if (cl.size() >= max)
+ {
+ source.Reply(_("Sorry, the maximum of \002{0}\002 certificate entries has been reached."), max);
+ return;
+ }
+
+ if (source.GetAccount() == nc)
+ {
+ User *u = source.GetUser();
+
+ if (!u || u->fingerprint.empty())
+ {
+ source.Reply(_("You are not using a client certificate."));
+ return;
+ }
+
+ certfp = u->fingerprint;
+ }
+
+ if (FindCert(cl, certfp))
+ {
+ source.Reply(_("Fingerprint \002{0}\002 already present on the certificate list of \002{0}\002."), certfp, nc->GetDisplay());
+ return;
+ }
+
+ if (certmap.find(certfp) != certmap.end())
+ {
+ source.Reply(_("Fingerprint \002{0}\002 is already in use."), certfp);
+ return;
+ }
+
+ NSCertEntry *e = Serialize::New<NSCertEntry *>();
+ e->SetAccount(nc);
+ e->SetCert(certfp);
+
+ // XXX fire events
+
+ Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to ADD certificate fingerprint " << certfp << " to " << nc->GetDisplay();
+ source.Reply(_("\002{0}\002 added to the certificate list of \002{1}\002."), certfp, nc->GetDisplay());
+ }
+
+ void DoDel(CommandSource &source, NickServ::Account *nc, Anope::string certfp)
+ {
+ std::vector<NSCertEntry *> cl = nc->GetRefs<NSCertEntry *>();
+
+ if (certfp.empty())
+ {
+ User *u = source.GetUser();
+ if (u)
+ certfp = u->fingerprint;
+ }
+
+ if (certfp.empty())
+ {
+ this->OnSyntaxError(source, "DEL");
+ return;
+ }
+
+ NSCertEntry *cert = FindCert(cl, certfp);
+ if (!cert)
+ {
+ source.Reply(_("\002{0}\002 not found on the certificate list of \002{1}\002."), certfp, nc->GetDisplay());
+ return;
+ }
+
+ cert->Delete();
+
+ Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to DELETE certificate fingerprint " << certfp << " from " << nc->GetDisplay();
+ source.Reply(_("\002{0}\002 deleted from the access list of \002{1}\002."), certfp, nc->GetDisplay());
+ }
+
+ void DoList(CommandSource &source, NickServ::Account *nc)
+ {
+ std::vector<NSCertEntry *> cl = nc->GetRefs<NSCertEntry *>();
+
+ if (cl.empty())
+ {
+ source.Reply(_("The certificate list of \002{0}\002 is empty."), nc->GetDisplay());
+ return;
+ }
+
+ source.Reply(_("Certificate list for \002{0}\002:"), nc->GetDisplay());
+ for (NSCertEntry *e : cl)
+ source.Reply(" {0}", e->GetCert());
+ }
+
+ public:
+ CommandNSCert(Module *creator) : Command(creator, "nickserv/cert", 1, 3)
+ {
+ this->SetDesc(_("Modify the nickname client certificate list"));
+ this->SetSyntax(_("ADD [\037nickname\037] [\037fingerprint\037]"));
+ this->SetSyntax(_("DEL [\037nickname\037] \037fingerprint\037"));
+ this->SetSyntax(_("LIST [\037nickname\037]"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &cmd = params[0];
+ Anope::string nick, certfp;
+
+ if (cmd.equals_ci("LIST"))
+ nick = params.size() > 1 ? params[1] : "";
+ else
+ {
+ nick = params.size() == 3 ? params[1] : "";
+ certfp = params.size() > 1 ? params[params.size() - 1] : "";
+ }
+
+ NickServ::Account *nc;
+ if (!nick.empty() && source.HasPriv("nickserv/access"))
+ {
+ NickServ::Nick *na = NickServ::FindNick(nick);
+ if (na == NULL)
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), nick);
+ return;
+ }
+
+ if (Config->GetModule("nickserv")->Get<bool>("secureadmins", "yes") && source.GetAccount() != na->GetAccount() && na->GetAccount()->IsServicesOper() && !cmd.equals_ci("LIST"))
+ {
+ source.Reply(_("You may view, but not modify, the certificate list of other Services Operators."));
+ return;
+ }
+
+ nc = na->GetAccount();
+ }
+ else
+ nc = source.nc;
+
+ if (cmd.equals_ci("LIST"))
+ return this->DoList(source, nc);
+ else if (nc->HasFieldS("NS_SUSPENDED"))
+ source.Reply(_("\002{0}\002 is suspended."), nc->GetDisplay());
+ else if (Anope::ReadOnly)
+ source.Reply(_("Services are in read-only mode."));
+ else if (cmd.equals_ci("ADD"))
+ return this->DoAdd(source, nc, certfp);
+ else if (cmd.equals_ci("DEL"))
+ return this->DoDel(source, nc, certfp);
+ else
+ this->OnSyntaxError(source, "");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Modifies or displays the certificate list for your account."
+ "If you connect to IRC and provide a client certificate with a matching fingerprint in the certificate list, you will be automatically identified to services."
+ " Services Operators may provide \037nickname\037 to modify other users' certificate lists.\n"
+ "\n"
+ "Examples:\n"
+ "\n"
+ " {0} ADD\n"
+ " Adds your current fingerprint to the certificate list and automatically identifies you when you connect to IRC using this certificate.\n"
+ "\n"
+ " {0} DEL <fingerprint>\n"
+ " Removes \"<fingerprint>\" from your certificate list."));
+ return true;
+ }
+};
+
+class NSCert : public Module
+ , public EventHook<Event::Fingerprint>
+ , public EventHook<NickServ::Event::NickValidate>
+{
+ CommandNSCert commandnscert;
+ CertServiceImpl cs;
+
+ public:
+ NSCert(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , EventHook<Event::Fingerprint>(this)
+ , EventHook<NickServ::Event::NickValidate>(this)
+ , commandnscert(this)
+ , cs(this)
+ {
+ if (!IRCD || !IRCD->CanCertFP)
+ throw ModuleException("Your IRCd does not support ssl client certificates");
+ }
+
+ void OnFingerprint(User *u) override
+ {
+ ServiceBot *NickServ = Config->GetClient("NickServ");
+ if (!NickServ || u->IsIdentified())
+ return;
+
+ NickServ::Account *nc = cs.FindAccountFromCert(u->fingerprint);
+ if (!nc || nc->HasFieldS("NS_SUSPENDED"))
+ return;
+
+ unsigned int maxlogins = Config->GetModule("ns_identify")->Get<unsigned int>("maxlogins");
+ if (maxlogins && nc->users.size() >= maxlogins)
+ {
+ u->SendMessage(NickServ, _("Account \002{0}\002 has already reached the maximum number of simultaneous logins ({1})."), nc->GetDisplay(), maxlogins);
+ return;
+ }
+
+ NickServ::Nick *na = NickServ::FindNick(u->nick);
+ if (na && na->GetAccount() == nc)
+ u->Identify(na);
+ else
+ u->Login(nc);
+
+ u->SendMessage(NickServ, _("SSL certificate fingerprint accepted, you are now identified to \002%s\002."), nc->GetDisplay().c_str());
+ Log(NickServ) << u->GetMask() << " automatically identified for account " << nc->GetDisplay() << " via SSL certificate fingerprint";
+ }
+
+ EventReturn OnNickValidate(User *u, NickServ::Nick *na) override
+ {
+ if (u->fingerprint.empty())
+ return EVENT_CONTINUE;
+
+ if (cs.Matches(u, na->GetAccount()))
+ {
+ ServiceBot *NickServ = Config->GetClient("NickServ");
+
+ unsigned int maxlogins = Config->GetModule("ns_identify")->Get<unsigned int>("maxlogins");
+ if (maxlogins && na->GetAccount()->users.size() >= maxlogins)
+ {
+ u->SendMessage(NickServ, _("Account \002{0}\002 has already reached the maximum number of simultaneous logins ({1})."), na->GetAccount()->GetDisplay(), maxlogins);
+ return EVENT_CONTINUE;
+ }
+
+ u->Identify(na);
+ u->SendMessage(NickServ, _("SSL certificate fingerprint accepted, you are now identified."));
+ Log(NickServ) << u->GetMask() << " automatically identified for account " << na->GetAccount()->GetDisplay() << " via SSL certificate fingerprint";
+ return EVENT_ALLOW;
+ }
+
+ return EVENT_CONTINUE;
+ }
+};
+
+MODULE_INIT(NSCert)
diff --git a/modules/nickserv/drop.cpp b/modules/nickserv/drop.cpp
new file mode 100644
index 000000000..22d3b59fb
--- /dev/null
+++ b/modules/nickserv/drop.cpp
@@ -0,0 +1,96 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "module.h"
+#include "modules/nickserv/drop.h"
+
+class CommandNSDrop : public Command
+{
+ public:
+ CommandNSDrop(Module *creator) : Command(creator, "nickserv/drop", 1, 1)
+ {
+ this->SetSyntax(_("\037nickname\037"));
+ this->SetDesc(_("Cancel the registration of a nickname"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &nick = params[0];
+
+ if (Anope::ReadOnly && !source.HasPriv("nickserv/drop"))
+ {
+ source.Reply(_("Sorry, nickname de-registration is temporarily disabled."));
+ return;
+ }
+
+ NickServ::Nick *na = NickServ::FindNick(nick);
+ if (!na)
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), nick);
+ return;
+ }
+
+ bool is_mine = source.GetAccount() == na->GetAccount();
+
+ if (!is_mine && !source.HasPriv("nickserv/drop"))
+ {
+ source.Reply(_("Access denied. You do not have the correct operator privileges to drop other user's nicknames."));
+ return;
+ }
+
+ if (Config->GetModule("nickserv")->Get<bool>("secureadmins", "yes") && !is_mine && na->GetAccount()->IsServicesOper())
+ {
+ source.Reply(_("You may not drop other Services Operators' nicknames."));
+ return;
+ }
+
+ EventManager::Get()->Dispatch(&Event::NickDrop::OnNickDrop, source, na);
+
+ Log(!is_mine ? LOG_ADMIN : LOG_COMMAND, source, this) << "to drop nickname " << na->GetNick() << " (group: " << na->GetAccount()->GetDisplay() << ") (email: " << (!na->GetAccount()->GetEmail().empty() ? na->GetAccount()->GetEmail() : "none") << ")";
+ na->Delete();
+
+ source.Reply(_("\002{0}\002 has been dropped."), nick);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Unregisters \037nickname\037. Once your nickname is dropped you may lose all of your access and channels that you may own. Any other user will be free to register \037nickname\037."));
+ if (!source.HasPriv("nickserv/drop"))
+ source.Reply(_("You may drop any nickname within your group."));
+ else
+ source.Reply(_("As a Services Operator, you may drop any nick."));
+
+ return true;
+ }
+};
+
+class NSDrop : public Module
+{
+ CommandNSDrop commandnsdrop;
+
+ public:
+ NSDrop(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandnsdrop(this)
+ {
+
+ }
+};
+
+MODULE_INIT(NSDrop)
diff --git a/modules/nickserv/getemail.cpp b/modules/nickserv/getemail.cpp
new file mode 100644
index 000000000..0a996e7b0
--- /dev/null
+++ b/modules/nickserv/getemail.cpp
@@ -0,0 +1,70 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+
+class CommandNSGetEMail : public Command
+{
+ public:
+ CommandNSGetEMail(Module *creator) : Command(creator, "nickserv/getemail", 1, 1)
+ {
+ this->SetDesc(_("Matches and returns all users that registered using given email"));
+ this->SetSyntax(_("\037email\037"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &email = params[0];
+ int j = 0;
+
+ Log(LOG_ADMIN, source, this) << "on " << email;
+
+ for (NickServ::Account *nc : NickServ::service->GetAccountList())
+ if (!nc->GetEmail().empty() && Anope::Match(nc->GetEmail(), email))
+ {
+ ++j;
+ source.Reply(_("Email matched: \002{0}\002 (\002{1}\002) to \002{2}\002."), nc->GetDisplay(), nc->GetEmail(), email);
+ }
+
+ if (j <= 0)
+ {
+ source.Reply(_("There are no accounts with an email that matches \002{0}\002."), email);
+ }
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Returns the matching accounts whose email address is \037email\037."));
+ return true;
+ }
+};
+
+class NSGetEMail : public Module
+{
+ CommandNSGetEMail commandnsgetemail;
+
+ public:
+ NSGetEMail(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandnsgetemail(this)
+ {
+
+ }
+};
+
+MODULE_INIT(NSGetEMail)
diff --git a/modules/nickserv/group.cpp b/modules/nickserv/group.cpp
new file mode 100644
index 000000000..7c0f41809
--- /dev/null
+++ b/modules/nickserv/group.cpp
@@ -0,0 +1,389 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/nickserv/cert.h"
+#include "modules/nickserv/group.h"
+
+class NSGroupRequestListener : public NickServ::IdentifyRequestListener
+{
+ CommandSource source;
+ Command *cmd;
+ Anope::string nick;
+ Reference<NickServ::Nick> target;
+
+ public:
+ NSGroupRequestListener(CommandSource &src, Command *c, const Anope::string &n, NickServ::Nick *targ) : source(src), cmd(c), nick(n), target(targ) { }
+
+ void OnSuccess(NickServ::IdentifyRequest *) override
+ {
+ if (!source.GetUser() || source.GetUser()->nick != nick || !target)
+ return;
+
+ User *u = source.GetUser();
+ NickServ::Nick *na = NickServ::FindNick(nick);
+ /* If the nick is already registered, drop it. */
+ if (na)
+ {
+ EventManager::Get()->Dispatch(&Event::ChangeCoreDisplay::OnChangeCoreDisplay, na->GetAccount(), u->nick);
+ delete na;
+ }
+
+ na = Serialize::New<NickServ::Nick *>();
+ na->SetNick(nick);
+ na->SetAccount(target->GetAccount());
+ na->SetLastUsermask(u->GetIdent() + "@" + u->GetDisplayedHost());
+ na->SetLastRealname(u->realname);
+ na->SetLastSeen(Anope::CurTime);
+ na->SetTimeRegistered(Anope::CurTime);
+
+ u->Login(target->GetAccount());
+ EventManager::Get()->Dispatch(&Event::NickGroup::OnNickGroup, u, target);
+
+ Log(LOG_COMMAND, source, cmd) << "to make " << nick << " join group of " << target->GetNick() << " (" << target->GetAccount()->GetDisplay() << ") (email: " << (!target->GetAccount()->GetEmail().empty() ? target->GetAccount()->GetEmail() : "none") << ")";
+ source.Reply(_("You are now in the group of \002{0}\002."), target->GetNick());
+
+ u->lastnickreg = Anope::CurTime;
+
+ }
+
+ void OnFail(NickServ::IdentifyRequest *) override
+ {
+ if (!source.GetUser())
+ return;
+
+ Log(LOG_COMMAND, source, cmd) << "and failed to group to " << target->GetNick();
+ source.Reply(_("Password incorrect."));
+ source.GetUser()->BadPassword();
+ }
+};
+
+class CommandNSGroup : public Command
+{
+ ServiceReference<CertService> certservice;
+
+ public:
+ CommandNSGroup(Module *creator) : Command(creator, "nickserv/group", 0, 2)
+ {
+ this->SetDesc(_("Join a group"));
+ this->SetSyntax(_("\037[target]\037 \037[password]\037"));
+ this->AllowUnregistered(true);
+ this->RequireUser(true);
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ User *u = source.GetUser();
+
+ Anope::string nick;
+ if (params.empty())
+ {
+ NickServ::Account* core = u->Account();
+ if (core)
+ nick = core->GetDisplay();
+ }
+ else
+ nick = params[0];
+
+ if (nick.empty())
+ {
+ this->SendSyntax(source);
+ return;
+ }
+
+ const Anope::string &pass = params.size() > 1 ? params[1] : "";
+
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Sorry, nickname grouping is temporarily disabled."));
+ return;
+ }
+
+ if (!IRCD->IsNickValid(u->nick))
+ {
+ source.Reply(_("\002{0}\002 may not be registered."), u->nick);
+ return;
+ }
+
+ if (Config->GetModule("nickserv")->Get<bool>("restrictopernicks"))
+ for (Oper *o : Serialize::GetObjects<Oper *>())
+ {
+ if (!u->HasMode("OPER") && u->nick.find_ci(o->GetName()) != Anope::string::npos)
+ {
+ source.Reply(_("\002{0}\002 may not be registered because it is too similar to an operator nick."), u->nick);
+ return;
+ }
+ }
+
+ NickServ::Nick *target, *na = NickServ::FindNick(u->nick);
+ const Anope::string &guestnick = Config->GetModule("nickserv")->Get<Anope::string>("guestnickprefix", "Guest");
+ time_t reg_delay = Config->GetModule("nickserv")->Get<time_t>("regdelay");
+ unsigned maxaliases = Config->GetModule(this->GetOwner())->Get<unsigned>("maxaliases");
+ if (!(target = NickServ::FindNick(nick)))
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), nick);
+ return;
+ }
+
+ if (Anope::CurTime < u->lastnickreg + reg_delay)
+ {
+ source.Reply(_("Please wait \002{0}\002 seconds before using the \002{1}\002 command again."), (reg_delay + u->lastnickreg) - Anope::CurTime, source.command);
+ return;
+ }
+
+ if (target->GetAccount()->HasFieldS("NS_SUSPENDED"))
+ {
+ Log(LOG_COMMAND, source, this) << "and tried to group to suspended nick " << target->GetNick();
+ source.Reply(_("\002{0}\002 is suspended."), target->GetNick());
+ return;
+ }
+
+ if (na && Config->GetModule(this->GetOwner())->Get<bool>("nogroupchange"))
+ {
+ source.Reply(_("Your nick is already registered."));
+ return;
+ }
+
+ if (na && target->GetAccount() == na->GetAccount())
+ {
+ source.Reply(_("You are already a member of the group of \002{0}\002."), target->GetNick());
+ return;
+ }
+
+ if (na && na->GetAccount() != u->Account())
+ {
+ source.Reply(_("\002{0}\002 is already registered."), na->GetNick());
+ return;
+ }
+
+ if (na && Config->GetModule(this->GetOwner())->Get<bool>("nogroupchange"))
+ {
+ source.Reply(_("You are already registered."));
+ return;
+ }
+
+ if (maxaliases && target->GetAccount()->GetRefs<NickServ::Nick *>().size() >= maxaliases && !target->GetAccount()->IsServicesOper())
+ {
+ source.Reply(_("There are too many nicknames in your group."));
+ return;
+ }
+
+ if (u->nick.length() <= guestnick.length() + 7 &&
+ u->nick.length() >= guestnick.length() + 1 &&
+ !u->nick.find_ci(guestnick) && !u->nick.substr(guestnick.length()).find_first_not_of("1234567890"))
+ {
+ source.Reply(_("\002{0}\002 may not be registered."), u->nick);
+ return;
+ }
+
+ bool ok = false;
+ if (!na && u->Account() == target->GetAccount())
+ ok = true;
+
+ if (certservice && certservice->Matches(u, target->GetAccount()))
+ ok = true;
+
+ if (ok == false && !pass.empty())
+ {
+ NickServ::IdentifyRequest *req = NickServ::service->CreateIdentifyRequest(new NSGroupRequestListener(source, this, u->nick, target), this->GetOwner(), target->GetAccount()->GetDisplay(), pass);
+ EventManager::Get()->Dispatch(&Event::CheckAuthentication::OnCheckAuthentication, source.GetUser(), req);
+ req->Dispatch();
+ }
+ else
+ {
+ NSGroupRequestListener req(source, this, u->nick, target);
+
+ if (ok)
+ req.OnSuccess(nullptr);
+ else
+ req.OnFail(nullptr);
+ }
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("This command makes your nickname join the \037target\037 nickname's group."
+ " \037password\037 is the password of the target nickname.\n"
+ "\n"
+ "Nicknames in the same group share channel privileges, memos, and most settings, including password."
+ "\n"
+ "You may be able to use this command even if you have not registered your nick yet."
+ " If your nick is already registered, you'll need to identify yourself before using this command."));
+ return true;
+ }
+};
+
+class CommandNSUngroup : public Command
+{
+ public:
+ CommandNSUngroup(Module *creator) : Command(creator, "nickserv/ungroup", 0, 1)
+ {
+ this->SetDesc(_("Remove a nick from a group"));
+ this->SetSyntax(_("[\037nickname\037]"));
+ this->RequireUser(true);
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ User *u = source.GetUser();
+ Anope::string nick = !params.empty() ? params[0] : "";
+ NickServ::Nick *na = NickServ::FindNick(!nick.empty() ? nick : u->nick);
+
+ if (u->Account()->GetRefs<NickServ::Nick *>().size() == 1)
+ {
+ source.Reply(_("Your nickname is not grouped to anything, so you can't ungroup it."));
+ return;
+ }
+
+ if (!na)
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), !nick.empty() ? nick : u->nick);
+ return;
+ }
+
+ if (na->GetAccount() != u->Account())
+ {
+ source.Reply(_("\002{0}\002 is not in your group."), na->GetNick());
+ return;
+ }
+
+
+ NickServ::Account *oldcore = na->GetAccount();
+
+ if (na->GetNick().equals_ci(oldcore->GetDisplay()))
+ oldcore->SetDisplay(oldcore->GetRef<NickServ::Nick *>());
+
+ NickServ::Account *nc = Serialize::New<NickServ::Account *>();
+ nc->SetDisplay(na->GetNick());
+ na->SetAccount(nc);
+
+ nc->SetPassword(oldcore->GetPassword());
+ if (!oldcore->GetEmail().empty())
+ nc->SetEmail(oldcore->GetEmail());
+ nc->SetLanguage(oldcore->GetLanguage());
+
+ source.Reply(_("\002{0}\002 has been ungrouped from \002{1}\002."), na->GetNick(), oldcore->GetDisplay());
+
+ User *user = User::Find(na->GetNick(), true);
+ if (user)
+ /* The user on the nick who was ungrouped may be identified to the old group, set -r */
+ user->RemoveMode(source.service, "REGISTERED");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("This command ungroups your nickname, or if given, the specificed \037nickname\037, from the group it is in."
+ " The ungrouped nick keeps its registration time, password, email, and language. Everything else is reset."
+ " You may not ungroup yourself if there is only one nickname in your group."));
+ return true;
+ }
+};
+
+class CommandNSGList : public Command
+{
+ public:
+ CommandNSGList(Module *creator) : Command(creator, "nickserv/glist", 0, 1)
+ {
+ this->SetDesc(_("Lists all nicknames in your group"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &nick = !params.empty() ? params[0] : "";
+ NickServ::Account *nc;
+
+ if (!nick.empty() && source.IsServicesOper())
+ {
+ NickServ::Nick *na = NickServ::FindNick(nick);
+ if (!na)
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), nick);
+ return;
+ }
+
+ nc = na->GetAccount();
+ }
+ else
+ nc = source.GetAccount();
+
+ ListFormatter list(source.GetAccount());
+ list.AddColumn(_("Nick")).AddColumn(_("Expires"));
+ time_t nickserv_expire = Config->GetModule("nickserv")->Get<time_t>("expire", "21d"),
+ unconfirmed_expire = Config->GetModule("nickserv")->Get<time_t>("unconfirmedexpire", "1d");
+ for (NickServ::Nick *na2 : nc->GetRefs<NickServ::Nick *>())
+ {
+ Anope::string expires;
+ if (na2->HasFieldS("NS_NO_EXPIRE"))
+ expires = _("Does not expire");
+ else if (!nickserv_expire || Anope::NoExpire)
+ ;
+ else if (na2->GetAccount()->HasFieldS("UNCONFIRMED") && unconfirmed_expire)
+ expires = Anope::strftime(na2->GetTimeRegistered() + unconfirmed_expire, source.GetAccount());
+ else
+ expires = Anope::strftime(na2->GetLastSeen() + nickserv_expire, source.GetAccount());
+
+ ListFormatter::ListEntry entry;
+ entry["Nick"] = na2->GetNick();
+ entry["Expires"] = expires;
+ list.AddEntry(entry);
+ }
+
+ source.Reply(nc != source.GetAccount() ? _("List of nicknames in the group of \002%s\002:") : _("List of nicknames in your group:"), nc->GetDisplay().c_str());
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+
+ source.Reply(_("%d nickname(s) in the group."), replies.size());
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ if (source.IsServicesOper())
+ source.Reply(_("Without a parameter, lists all nicknames that are in your group.\n"
+ "\n"
+ "With a parameter, lists all nicknames that are in the group of the given nick.\n"
+ "Specifying a nick is limited to \002Services Operators\002."),
+ source.command);
+ else
+ source.Reply(_("Lists all nicknames in your group."));
+
+ return true;
+ }
+};
+
+class NSGroup : public Module
+{
+ CommandNSGroup commandnsgroup;
+ CommandNSUngroup commandnsungroup;
+ CommandNSGList commandnsglist;
+
+ public:
+ NSGroup(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandnsgroup(this)
+ , commandnsungroup(this)
+ , commandnsglist(this)
+ {
+ if (Config->GetModule("nickserv")->Get<bool>("nonicknameownership"))
+ throw ModuleException(modname + " can not be used with options:nonicknameownership enabled");
+ }
+};
+
+MODULE_INIT(NSGroup)
diff --git a/modules/nickserv/identify.cpp b/modules/nickserv/identify.cpp
new file mode 100644
index 000000000..13e578b69
--- /dev/null
+++ b/modules/nickserv/identify.cpp
@@ -0,0 +1,133 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+
+class NSIdentifyRequestListener : public NickServ::IdentifyRequestListener
+{
+ CommandSource source;
+ Command *cmd;
+
+ public:
+ NSIdentifyRequestListener(CommandSource &s, Command *c) : source(s), cmd(c) { }
+
+ void OnSuccess(NickServ::IdentifyRequest *req) override
+ {
+ if (!source.GetUser())
+ return;
+
+ User *u = source.GetUser();
+ NickServ::Nick *na = NickServ::FindNick(req->GetAccount());
+
+ if (!na)
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), req->GetAccount());
+ return;
+ }
+
+ if (u->IsIdentified())
+ Log(LOG_COMMAND, source, cmd) << "to log out of account " << u->Account()->GetDisplay();
+
+ Log(LOG_COMMAND, source, cmd) << "and identified for account " << na->GetAccount()->GetDisplay();
+ source.Reply(_("Password accepted - you are now recognized as \002{0}\002."), na->GetAccount()->GetDisplay());
+ u->Identify(na);
+ }
+
+ void OnFail(NickServ::IdentifyRequest *req) override
+ {
+ if (!source.GetUser())
+ return;
+
+ bool accountexists = NickServ::FindNick(req->GetAccount()) != NULL;
+ Log(LOG_COMMAND, source, cmd) << "and failed to identify to" << (accountexists ? " " : " nonexistent ") << "account " << req->GetAccount();
+ if (accountexists)
+ {
+ source.Reply(_("Password incorrect."));
+ source.GetUser()->BadPassword();
+ }
+ else
+ source.Reply("\002{0}\002 isn't registered.", req->GetAccount());
+ }
+};
+
+class CommandNSIdentify : public Command
+{
+ public:
+ CommandNSIdentify(Module *creator) : Command(creator, "nickserv/identify", 1, 2)
+ {
+ this->SetDesc(_("Identify yourself with your password"));
+ this->SetSyntax(_("[\037account\037] \037password\037"));
+ this->AllowUnregistered(true);
+ this->RequireUser(true);
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ User *u = source.GetUser();
+
+ const Anope::string &nick = params.size() == 2 ? params[0] : u->nick;
+ Anope::string pass = params[params.size() - 1];
+
+ NickServ::Nick *na = NickServ::FindNick(nick);
+ if (na && na->GetAccount()->HasFieldS("NS_SUSPENDED"))
+ {
+ source.Reply(_("\002{0}\002 is suspended."), na->GetNick());
+ return;
+ }
+
+ if (u->Account() && na && u->Account() == na->GetAccount())
+ {
+ source.Reply(_("You are already identified."));
+ return;
+ }
+
+ unsigned int maxlogins = Config->GetModule(this->GetOwner())->Get<unsigned int>("maxlogins");
+ if (na && maxlogins && na->GetAccount()->users.size() >= maxlogins)
+ {
+ source.Reply(_("Account \002{0}\002 has already reached the maximum number of simultaneous logins ({1})."), na->GetAccount()->GetDisplay(), maxlogins);
+ return;
+ }
+
+ NickServ::IdentifyRequest *req = NickServ::service->CreateIdentifyRequest(new NSIdentifyRequestListener(source, this), this->GetOwner(), na ? na->GetAccount()->GetDisplay() : nick, pass);
+ EventManager::Get()->Dispatch(&Event::CheckAuthentication::OnCheckAuthentication, u, req);
+
+ req->Dispatch();
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Logs you in to account \037account\037. If no \037account\037 is given, your current nickname is used."
+ " Many commands require you to authenticate with this command before you use them. \037password\037 should be the same one you registered with."));
+ return true;
+ }
+};
+
+class NSIdentify : public Module
+{
+ CommandNSIdentify commandnsidentify;
+
+ public:
+ NSIdentify(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
+ commandnsidentify(this)
+ {
+
+ }
+};
+
+MODULE_INIT(NSIdentify)
diff --git a/modules/nickserv/info.cpp b/modules/nickserv/info.cpp
new file mode 100644
index 000000000..593cfa116
--- /dev/null
+++ b/modules/nickserv/info.cpp
@@ -0,0 +1,284 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/nickserv/info.h"
+#include "modules/nickserv/set.h"
+
+class CommandNSInfo : public Command
+{
+ public:
+ CommandNSInfo(Module *creator) : Command(creator, "nickserv/info", 0, 2)
+ {
+ this->SetDesc(_("Displays information about a given nickname"));
+ this->SetSyntax(_("[\037nickname\037]"));
+ this->AllowUnregistered(true);
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+
+ const Anope::string &nick = params.size() ? params[0] : (source.nc ? source.nc->GetDisplay() : source.GetNick());
+ NickServ::Nick *na = NickServ::FindNick(nick);
+ bool has_auspex = source.HasPriv("nickserv/auspex");
+
+ if (!na)
+ {
+ if (ServiceBot::Find(nick, true))
+ source.Reply(_("\002{0}\002 is part of this Network's Services."), nick);
+ else
+ source.Reply(_("\002{0}\002 isn't registered."), nick);
+ return;
+ }
+
+ bool nick_online = false, show_hidden = false;
+
+ /* Is the real owner of the nick we're looking up online? -TheShadow */
+ User *u2 = User::Find(na->GetNick(), true);
+ if (u2 && u2->Account() == na->GetAccount())
+ {
+ nick_online = true;
+ na->SetLastSeen(Anope::CurTime);
+ }
+
+ if (has_auspex || na->GetAccount() == source.GetAccount())
+ show_hidden = true;
+
+ source.Reply(_("\002{0}\002 is \002{1}\002"), na->GetNick(), na->GetLastRealname());
+
+ if (na->GetAccount()->HasFieldS("UNCONFIRMED"))
+ source.Reply(_("\002{0}\002 has not confirmed their account."), na->GetNick());
+
+ if (na->GetAccount()->IsServicesOper() && (show_hidden || !na->GetAccount()->HasFieldS("HIDE_STATUS")))
+ source.Reply(_("\002{0}\002 is a Services Operator of type \002{1}\002."), na->GetNick(), na->GetAccount()->o->GetType()->GetName());
+
+ InfoFormatter info(source.nc);
+
+ if (nick_online)
+ {
+ bool shown = false;
+ if (show_hidden && !na->GetLastRealhost().empty())
+ {
+ info[_("Online from")] = na->GetLastRealhost();
+ shown = true;
+ }
+ if ((show_hidden || !na->GetAccount()->HasFieldS("HIDE_MASK")) && (!shown || na->GetLastUsermask() != na->GetLastRealhost()))
+ info[_("Online from")] = na->GetLastUsermask();
+ else
+ source.Reply(_("\002{0}\002 is currently online."), na->GetNick());
+ }
+ else
+ {
+ Anope::string shown;
+ if (show_hidden || !na->GetAccount()->HasFieldS("HIDE_MASK"))
+ {
+ info[_("Last seen address")] = na->GetLastUsermask();
+ shown = na->GetLastUsermask();
+ }
+
+ if (show_hidden && !na->GetLastRealhost().empty() && na->GetLastRealhost() != shown)
+ info[_("Last seen address")] = na->GetLastRealhost();
+ }
+
+ info[_("Registered")] = Anope::strftime(na->GetTimeRegistered(), source.GetAccount());
+
+ if (!nick_online)
+ info[_("Last seen")] = Anope::strftime(na->GetLastSeen(), source.GetAccount());
+
+ if (!na->GetLastQuit().empty() && (show_hidden || !na->GetAccount()->HasFieldS("HIDE_QUIT")))
+ info[_("Last quit message")] = na->GetLastQuit();
+
+ if (!na->GetAccount()->GetEmail().empty() && (show_hidden || !na->GetAccount()->HasFieldS("HIDE_EMAIL")))
+ info[_("Email address")] = na->GetAccount()->GetEmail();
+
+ if (show_hidden)
+ {
+ if (na->HasVhost())
+ {
+ if (IRCD->CanSetVIdent && !na->GetVhostIdent().empty())
+ info[_("VHost")] = na->GetVhostIdent() + "@" + na->GetVhostHost();
+ else
+ info[_("VHost")] = na->GetVhostHost();
+ }
+ }
+
+ EventManager::Get()->Dispatch(&Event::NickInfo::OnNickInfo, source, na, info, show_hidden);
+
+ std::vector<Anope::string> replies;
+ info.Process(replies);
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ this->SendSyntax(source);
+ source.Reply(" ");
+ source.Reply(_("Displays information about the given nickname, such as\n"
+ "the nick's owner, last seen address and time, and nick\n"
+ "options. If no nick is given, and you are identified,\n"
+ "your account name is used, else your current nickname is\n"
+ "used."));
+
+ return true;
+ }
+};
+
+
+class CommandNSSetHide : public Command
+{
+ public:
+ CommandNSSetHide(Module *creator, const Anope::string &sname = "nickserv/set/hide", size_t min = 2) : Command(creator, sname, min, min + 1)
+ {
+ this->SetDesc(_("Hide certain pieces of nickname information"));
+ this->SetSyntax("{EMAIL | STATUS | USERMASK | QUIT} {ON | OFF}");
+ }
+
+ void Run(CommandSource &source, const Anope::string &user, const Anope::string &param, const Anope::string &arg)
+ {
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ NickServ::Nick *na = NickServ::FindNick(user);
+ if (!na)
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), user);
+ return;
+ }
+ NickServ::Account *nc = na->GetAccount();
+
+ EventReturn MOD_RESULT = EventManager::Get()->Dispatch(&Event::SetNickOption::OnSetNickOption, source, this, nc, param);
+ if (MOD_RESULT == EVENT_STOP)
+ return;
+
+ const char *onmsg, *offmsg, *flag;
+
+ if (param.equals_ci("EMAIL"))
+ {
+ flag = "HIDE_EMAIL";
+ onmsg = _("The \002e-mail address\002 of \002{0}\002 will now be \002hidden\002.");
+ offmsg = _("The \002e-mail address\002 of \002{0}\002 will now be \002shown\002.");
+ }
+ else if (param.equals_ci("USERMASK"))
+ {
+ flag = "HIDE_MASK";
+ onmsg = _("The \002last seen host mask\002 of \002{0}\002 will now be \002hidden\002.");
+ offmsg = _("The \002last seen host mask\002 of \002{0}\002 will now be \002shown\002.");
+ }
+ else if (param.equals_ci("STATUS"))
+ {
+ flag = "HIDE_STATUS";
+ onmsg = _("The \002services operator status\002 of \002{0}\002 will now be \002hidden\002.");
+ offmsg = _("The \002services operator status\002 of \002{0}\002 will now be \002shown\002.");
+ }
+ else if (param.equals_ci("QUIT"))
+ {
+ flag = "HIDE_QUIT";
+ onmsg = _("The \002last quit message\002 of \002{0}\002 will now be \002hidden\002.");
+ offmsg = _("The \002last quit message\002 of \002{0}\002 will now be \002shown\002.");
+ }
+ else
+ {
+ this->OnSyntaxError(source, "HIDE");
+ return;
+ }
+
+ if (arg.equals_ci("ON"))
+ {
+ Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to change hide " << param.upper() << " to " << arg.upper() << " for " << nc->GetDisplay();
+ nc->SetS<bool>(flag, true);
+ source.Reply(onmsg, nc->GetDisplay(), source.service->nick);
+ }
+ else if (arg.equals_ci("OFF"))
+ {
+ Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to change hide " << param.upper() << " to " << arg.upper() << " for " << nc->GetDisplay();
+ nc->UnsetS<bool>(flag);
+ source.Reply(offmsg, nc->GetDisplay(), source.service->nick);
+ }
+ else
+ this->OnSyntaxError(source, "HIDE");
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ this->Run(source, source.nc->GetDisplay(), params[0], params[1]);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
+ {
+ source.Reply(_("Allows you to prevent certain pieces of information from being displayed when someone does a %s \002INFO\002 on you." //XXX
+ " You can hide the e-mail address (\002EMAIL\002), last seen hostmask (\002USERMASK\002), the services access status (\002STATUS\002) and last quit message (\002QUIT\002)."
+ "The second parameter specifies whether the information should\n"
+ "be displayed (\002OFF\002) or hidden (\002ON\002)."), source.service->nick);
+ return true;
+ }
+};
+
+class CommandNSSASetHide : public CommandNSSetHide
+{
+ public:
+ CommandNSSASetHide(Module *creator) : CommandNSSetHide(creator, "nickserv/saset/hide", 3)
+ {
+ this->SetSyntax(_("\037nickname\037 {EMAIL | STATUS | USERMASK | QUIT} {ON | OFF}"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ this->ClearSyntax();
+ this->Run(source, params[0], params[1], params[2]);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
+ {
+ source.Reply(_("Allows you to prevent certain pieces of information from being displayed when someone does a %s \002INFO\002 on the \037nickname\037. "
+ " You can hide the e-mail address (\002EMAIL\002), last seen hostmask (\002USERMASK\002), the services access status (\002STATUS\002) and last quit message (\002QUIT\002)."
+ " The second parameter specifies whether the information should be displayed (\002OFF\002) or hidden (\002ON\002)."),
+ source.service->nick);
+ return true;
+ }
+};
+
+class NSInfo : public Module
+{
+ CommandNSInfo commandnsinfo;
+
+ CommandNSSetHide commandnssethide;
+ CommandNSSASetHide commandnssasethide;
+
+ Serialize::Field<NickServ::Account, bool> hide_email, hide_usermask, hide_status, hide_quit;
+
+ public:
+ NSInfo(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandnsinfo(this)
+ , commandnssethide(this)
+ , commandnssasethide(this)
+ , hide_email(this, "HIDE_EMAIL")
+ , hide_usermask(this, "HIDE_MASK")
+ , hide_status(this, "HIDE_STATUS")
+ , hide_quit(this, "HIDE_QUIT")
+ {
+
+ }
+};
+
+MODULE_INIT(NSInfo)
diff --git a/modules/nickserv/list.cpp b/modules/nickserv/list.cpp
new file mode 100644
index 000000000..4b4a12d36
--- /dev/null
+++ b/modules/nickserv/list.cpp
@@ -0,0 +1,299 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/nickserv/info.h"
+#include "modules/nickserv/set.h"
+
+class CommandNSList : public Command
+{
+ public:
+ CommandNSList(Module *creator) : Command(creator, "nickserv/list", 1, 2)
+ {
+ this->SetDesc(_("List all registered nicknames that match a given pattern"));
+ this->SetSyntax(_("\037pattern\037 [SUSPENDED] [NOEXPIRE] [UNCONFIRMED]"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+
+ Anope::string pattern = params[0];
+ const NickServ::Account *mync;
+ unsigned nnicks;
+ bool is_servadmin = source.HasCommand("nickserv/list");
+ int count = 0, from = 0, to = 0;
+ bool suspended, nsnoexpire, unconfirmed;
+ unsigned listmax = Config->GetModule(this->GetOwner())->Get<unsigned>("listmax", "50");
+
+ suspended = nsnoexpire = unconfirmed = false;
+
+ if (pattern[0] == '#')
+ {
+ Anope::string n1, n2;
+ sepstream(pattern.substr(1), '-').GetToken(n1, 0);
+ sepstream(pattern, '-').GetToken(n2, 1);
+ try
+ {
+ from = convertTo<int>(n1);
+ to = convertTo<int>(n2);
+ }
+ catch (const ConvertException &)
+ {
+ source.Reply(_("Incorrect range specified. The correct syntax is \002#\037from\037-\037to\037\002."));
+ return;
+ }
+
+ pattern = "*";
+ }
+
+ nnicks = 0;
+
+ if (is_servadmin && params.size() > 1)
+ {
+ Anope::string keyword;
+ spacesepstream keywords(params[1]);
+ while (keywords.GetToken(keyword))
+ {
+ if (keyword.equals_ci("NOEXPIRE"))
+ nsnoexpire = true;
+ if (keyword.equals_ci("SUSPENDED"))
+ suspended = true;
+ if (keyword.equals_ci("UNCONFIRMED"))
+ unconfirmed = true;
+ }
+ }
+
+ mync = source.nc;
+ ListFormatter list(source.GetAccount());
+
+ list.AddColumn(_("Nick")).AddColumn(_("Last usermask"));
+
+ // XXX wtf
+ Anope::map<NickServ::Nick *> ordered_map;
+ for (NickServ::Nick *na : NickServ::service->GetNickList())
+ ordered_map[na->GetNick()] = na;
+
+ for (Anope::map<NickServ::Nick *>::const_iterator it = ordered_map.begin(), it_end = ordered_map.end(); it != it_end; ++it)
+ {
+ NickServ::Nick *na = it->second;
+
+ /* Don't show private nicks to non-services admins. */
+ if (na->GetAccount()->HasFieldS("NS_PRIVATE") && !is_servadmin && na->GetAccount() != mync)
+ continue;
+ else if (nsnoexpire && !na->HasFieldS("NS_NO_EXPIRE"))
+ continue;
+ else if (suspended && !na->GetAccount()->HasFieldS("NS_SUSPENDED"))
+ continue;
+ else if (unconfirmed && !na->GetAccount()->HasFieldS("UNCONFIRMED"))
+ continue;
+
+ /* We no longer compare the pattern against the output buffer.
+ * Instead we build a nice nick!user@host buffer to compare.
+ * The output is then generated separately. -TheShadow */
+ Anope::string buf = Anope::printf("%s!%s", na->GetNick().c_str(), !na->GetLastUsermask().empty() ? na->GetLastUsermask().c_str() : "*@*");
+ if (na->GetNick().equals_ci(pattern) || Anope::Match(buf, pattern, false, true))
+ {
+ if (((count + 1 >= from && count + 1 <= to) || (!from && !to)) && ++nnicks <= listmax)
+ {
+ bool isnoexpire = false;
+ if (is_servadmin && na->HasFieldS("NS_NO_EXPIRE"))
+ isnoexpire = true;
+
+ ListFormatter::ListEntry entry;
+ entry["Nick"] = (isnoexpire ? "!" : "") + na->GetNick();
+ if (na->GetAccount()->HasFieldS("HIDE_MASK") && !is_servadmin && na->GetAccount() != mync)
+ entry["Last usermask"] = Language::Translate(source.GetAccount(), _("[Hostname hidden]"));
+ else if (na->GetAccount()->HasFieldS("NS_SUSPENDED"))
+ entry["Last usermask"] = Language::Translate(source.GetAccount(), _("[Suspended]"));
+ else if (na->GetAccount()->HasFieldS("UNCONFIRMED"))
+ entry["Last usermask"] = Language::Translate(source.GetAccount(), _("[Unconfirmed]"));
+ else
+ entry["Last usermask"] = na->GetLastUsermask();
+ list.AddEntry(entry);
+ }
+ ++count;
+ }
+ }
+
+ source.Reply(_("List of entries matching \002{0}\002:"), pattern);
+
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+
+ source.Reply(_("End of list - \002{0}\002/\002{1}\002 matches shown."), nnicks > listmax ? listmax : nnicks, nnicks);
+ return;
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Lists all registered nicknames which match the given pattern, in \037nick!user@host\037 format."
+ " Nicks with the \002PRIVATE\002 option set will only be displayed to Services Operators with the proper access."
+ " Nicks with the \002NOEXPIRE\002 option set will have a \002!\002 prefixed to the nickname for Services Operators to see.\n"
+ "\n"
+ "Note that a preceding '#' specifies a range.\n"
+ "\n"
+ "If the SUSPENDED, UNCONFIRMED or NOEXPIRE options are given, only\n"
+ "nicks which, respectively, are SUSPENDED, UNCONFIRMED or have the\n"
+ "NOEXPIRE flag set will be displayed. If multiple options are\n"
+ "given, all nicks matching at least one option will be displayed.\n"
+ "Note that these options are limited to \037Services Operators\037.\n"
+ "\n"
+ "Examples:\n"
+ "\n"
+ " {0} *!joeuser@foo.com\n"
+ " Lists all registered nicks owned by joeuser@foo.com.\n"
+ "\n"
+ " {0} *Bot*!*@*\n"
+ " Lists all registered nicks with \002Bot\002 in their names (case insensitive).\n"
+ "\n"
+ " {0} * NOEXPIRE\n"
+ " Lists all registered nicks which have been set to not expire.\n"
+ "\n"
+ " {0} #51-100\n"
+ " Lists all registered nicks within the given range (51-100)."));
+
+ const Anope::string &regexengine = Config->GetBlock("options")->Get<Anope::string>("regexengine");
+ if (!regexengine.empty())
+ {
+ source.Reply(" ");
+ source.Reply(_("Regex matches are also supported using the {0} engine. Enclose your pattern in // if this is desired."), regexengine);
+ }
+
+ return true;
+ }
+};
+
+
+class CommandNSSetPrivate : public Command
+{
+ public:
+ CommandNSSetPrivate(Module *creator, const Anope::string &sname = "nickserv/set/private", size_t min = 1) : Command(creator, sname, min, min + 1)
+ {
+ this->SetDesc(_("Prevent your account from appearing in the LIST command"));
+ this->SetSyntax("{ON | OFF}");
+ }
+
+ void Run(CommandSource &source, const Anope::string &user, const Anope::string &param)
+ {
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ NickServ::Nick *na = NickServ::FindNick(user);
+ if (!na)
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), user);
+ return;
+ }
+ NickServ::Account *nc = na->GetAccount();
+
+ EventReturn MOD_RESULT = EventManager::Get()->Dispatch(&Event::SetNickOption::OnSetNickOption, source, this, nc, param);
+ if (MOD_RESULT == EVENT_STOP)
+ return;
+
+ if (param.equals_ci("ON"))
+ {
+ Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to enable private for " << nc->GetDisplay();
+ nc->SetS<bool>("NS_PRIVATE", true);
+ source.Reply(_("Private option is now \002on\002 for \002{0}\002."), nc->GetDisplay());
+ }
+ else if (param.equals_ci("OFF"))
+ {
+ Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to disable private for " << nc->GetDisplay();
+ nc->UnsetS<bool>("NS_PRIVATE");
+ source.Reply(_("Private option is now \002off\002 for \002{0}\002."), nc->GetDisplay());
+ }
+ else
+ this->OnSyntaxError(source, "PRIVATE");
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ this->Run(source, source.nc->GetDisplay(), params[0]);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
+ {
+ source.Reply(_("Turns the privacy option on or off for your account."
+ " When \002PRIVATE\002 is set, your account will not appear in the account list."
+ " However, anyone who knows your account can still request information about it."));
+ return true;
+ }
+};
+
+class CommandNSSASetPrivate : public CommandNSSetPrivate
+{
+ public:
+ CommandNSSASetPrivate(Module *creator) : CommandNSSetPrivate(creator, "nickserv/saset/private", 2)
+ {
+ this->ClearSyntax();
+ this->SetSyntax(_("\037account\037 {ON | OFF}"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ this->Run(source, params[0], params[1]);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
+ {
+ source.Reply(_("Turns the privacy option on or off for \037account\037."
+ " When \002PRIVATE\002 is set, the account will not appear in the account list."
+ " However, anyone who knows your account can still request information about it."));
+ return true;
+ }
+};
+
+
+class NSList : public Module
+ , public EventHook<Event::NickInfo>
+{
+ CommandNSList commandnslist;
+
+ CommandNSSetPrivate commandnssetprivate;
+ CommandNSSASetPrivate commandnssasetprivate;
+
+ Serialize::Field<NickServ::Account, bool> priv;
+
+ public:
+ NSList(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , EventHook<Event::NickInfo>(this)
+ , commandnslist(this)
+ , commandnssetprivate(this)
+ , commandnssasetprivate(this)
+ , priv(this, "NS_PRIVATE")
+ {
+ }
+
+ void OnNickInfo(CommandSource &source, NickServ::Nick *na, InfoFormatter &info, bool show_all) override
+ {
+ if (!show_all)
+ return;
+
+ if (priv.HasExt(na->GetAccount()))
+ info.AddOption(_("Private"));
+ }
+};
+
+MODULE_INIT(NSList)
diff --git a/modules/nickserv/logout.cpp b/modules/nickserv/logout.cpp
new file mode 100644
index 000000000..6a9d119ad
--- /dev/null
+++ b/modules/nickserv/logout.cpp
@@ -0,0 +1,109 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/nickserv.h"
+
+class CommandNSLogout : public Command
+{
+ public:
+ CommandNSLogout(Module *creator) : Command(creator, "nickserv/logout", 0, 2)
+ {
+ this->SetDesc(_("Reverses the effect of the IDENTIFY command"));
+ this->SetSyntax(_("[\037nickname\037 [REVALIDATE]]"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+
+ const Anope::string &nick = !params.empty() ? params[0] : "";
+ const Anope::string &param = params.size() > 1 ? params[1] : "";
+
+ if (!source.IsServicesOper() && !nick.empty())
+ {
+ this->OnSyntaxError(source, "");
+ return;
+ }
+
+ User *u2 = !nick.empty() ? User::Find(nick, true) : source.GetUser();
+ if (!u2)
+ {
+ source.Reply(_("\002{0}\002 isn't currently online."), !nick.empty() ? nick : source.GetNick());
+ return;
+ }
+
+ if (!nick.empty() && u2->IsServicesOper())
+ {
+ source.Reply(_("You can't logout \002{0}\002, they are a Services Operator."), nick);
+ return;
+ }
+
+#warning "revalidate"
+#if 0
+ if (!nick.empty() && !param.empty() && param.equals_ci("REVALIDATE") && NickServ::service)
+ NickServ::service->Validate(u2);
+#endif
+
+ u2->super_admin = false; /* Dont let people logout and remain a SuperAdmin */
+ Log(LOG_COMMAND, source, this) << "to logout " << u2->nick;
+
+ if (!nick.empty())
+ source.Reply(_("\002{0}\002 has been logged out."), nick);
+ else
+ source.Reply(_("You have been logged out."));
+
+ IRCD->SendLogout(u2);
+ u2->RemoveMode(source.service, "REGISTERED");
+ u2->Logout();
+
+ /* Send out an event */
+ EventManager::Get()->Dispatch(&Event::NickLogout::OnNickLogout, u2);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Logs you out of your account"));
+ #if 0
+ source.Reply(_("Without a parameter, reverses the effect of the \002IDENTIFY\002\n"
+ "command, i.e. make you not recognized as the real owner of the nick\n"
+ "anymore. Note, however, that you won't be asked to reidentify\n"
+ "yourself.\n"
+ " \n"
+ "With a parameter, does the same for the given nick. If you\n"
+ "specify \002REVALIDATE\002 as well, Services will ask the given nick\n"
+ "to re-identify. This is limited to \002Services Operators\002."));
+ #endif
+
+ return true;
+ }
+};
+
+class NSLogout : public Module
+{
+ CommandNSLogout commandnslogout;
+
+ public:
+ NSLogout(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandnslogout(this)
+ {
+
+ }
+};
+
+MODULE_INIT(NSLogout)
diff --git a/modules/nickserv/main/CMakeLists.txt b/modules/nickserv/main/CMakeLists.txt
new file mode 100644
index 000000000..781f0ef1f
--- /dev/null
+++ b/modules/nickserv/main/CMakeLists.txt
@@ -0,0 +1 @@
+build_subdir(${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/modules/nickserv/main/account.cpp b/modules/nickserv/main/account.cpp
new file mode 100644
index 000000000..1c2266e59
--- /dev/null
+++ b/modules/nickserv/main/account.cpp
@@ -0,0 +1,136 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2015-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "accounttype.h"
+#include "modules/nickserv/access.h"
+
+AccountImpl::~AccountImpl()
+{
+ NickServ::nickcore_map& map = NickServ::service->GetAccountMap();
+ map.erase(this->GetDisplay());
+}
+
+void AccountImpl::Delete()
+{
+ EventManager::Get()->Dispatch(&Event::DelCore::OnDelCore, this);
+
+ for (unsigned i = users.size(); i > 0; --i)
+ users[i - 1]->Logout();
+
+ return Serialize::Object::Delete();
+}
+
+Anope::string AccountImpl::GetDisplay()
+{
+ return Get<Anope::string>(&AccountType::display);
+}
+
+void AccountImpl::SetDisplay(const Anope::string &disp)
+{
+ Set(&AccountType::display, disp);
+}
+
+Anope::string AccountImpl::GetPassword()
+{
+ return Get(&AccountType::pass);
+}
+
+void AccountImpl::SetPassword(const Anope::string &pass)
+{
+ Set(&AccountType::pass, pass);
+}
+
+Anope::string AccountImpl::GetEmail()
+{
+ return Get(&AccountType::email);
+}
+
+void AccountImpl::SetEmail(const Anope::string &email)
+{
+ Set(&AccountType::email, email);
+}
+
+Anope::string AccountImpl::GetLanguage()
+{
+ return Get(&AccountType::language);
+}
+
+void AccountImpl::SetLanguage(const Anope::string &lang)
+{
+ Set(&AccountType::language, lang);
+}
+
+MemoServ::MemoInfo *AccountImpl::GetMemos()
+{
+ return GetRef<MemoServ::MemoInfo *>();
+}
+
+void AccountImpl::SetDisplay(NickServ::Nick *na)
+{
+ if (na->GetAccount() != this || na->GetNick() == this->GetDisplay())
+ return;
+
+ EventManager::Get()->Dispatch(&Event::ChangeCoreDisplay::OnChangeCoreDisplay, this, na->GetNick());
+
+ NickServ::nickcore_map& map = NickServ::service->GetAccountMap();
+
+ /* Remove the core from the list */
+ map.erase(this->GetDisplay());
+
+ this->SetDisplay(na->GetNick());
+
+ NickServ::Account* &nc = map[this->GetDisplay()];
+ if (nc)
+ Log(LOG_DEBUG) << "Duplicate account " << this->GetDisplay() << " in nickcore table?";
+
+ nc = this;
+}
+
+bool AccountImpl::IsServicesOper() const
+{
+ return this->o != NULL;
+}
+
+bool AccountImpl::IsOnAccess(User *u)
+{
+ Anope::string buf = u->GetIdent() + "@" + u->host, buf2, buf3;
+ if (!u->vhost.empty())
+ buf2 = u->GetIdent() + "@" + u->vhost;
+ if (!u->GetCloakedHost().empty())
+ buf3 = u->GetIdent() + "@" + u->GetCloakedHost();
+
+ for (NickAccess *access : GetRefs<NickAccess *>())
+ {
+ Anope::string a = access->GetMask();
+ if (Anope::Match(buf, a) || (!buf2.empty() && Anope::Match(buf2, a)) || (!buf3.empty() && Anope::Match(buf3, a)))
+ return true;
+ }
+ return false;
+}
+
+unsigned int AccountImpl::GetChannelCount()
+{
+ unsigned int i = 0;
+ for (ChanServ::Channel *c : GetRefs<ChanServ::Channel *>())
+ if (c->GetFounder() == this)
+ ++i;
+ return i;
+}
+
diff --git a/modules/nickserv/main/account.h b/modules/nickserv/main/account.h
new file mode 100644
index 000000000..461a3d865
--- /dev/null
+++ b/modules/nickserv/main/account.h
@@ -0,0 +1,58 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2015-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "modules/nickserv.h"
+
+class AccountImpl : public NickServ::Account
+{
+ friend class AccountType;
+
+ Anope::string display, password, email, language;
+
+ public:
+ AccountImpl(Serialize::TypeBase *type) : NickServ::Account(type) { }
+ AccountImpl(Serialize::TypeBase *type, Serialize::ID id) : NickServ::Account(type, id) { }
+ ~AccountImpl();
+ void Delete() override;
+
+ Anope::string GetDisplay() override;
+ void SetDisplay(const Anope::string &) override;
+
+ Anope::string GetPassword() override;
+ void SetPassword(const Anope::string &) override;
+
+ Anope::string GetEmail() override;
+ void SetEmail(const Anope::string &) override;
+
+ Anope::string GetLanguage() override;
+ void SetLanguage(const Anope::string &) override;
+
+ MemoServ::MemoInfo *GetMemos() override;
+
+ void SetDisplay(NickServ::Nick *na) override;
+ bool IsServicesOper() const override;
+ /*void AddAccess(const Anope::string &entry) override;
+ Anope::string GetAccess(unsigned entry) const override;
+ unsigned GetAccessCount() const override;
+ bool FindAccess(const Anope::string &entry) override;
+ void EraseAccess(const Anope::string &entry) override;
+ void ClearAccess() override;*/
+ bool IsOnAccess(User *u) override;
+ unsigned int GetChannelCount() override;
+};
diff --git a/modules/nickserv/main/accounttype.cpp b/modules/nickserv/main/accounttype.cpp
new file mode 100644
index 000000000..16abea415
--- /dev/null
+++ b/modules/nickserv/main/accounttype.cpp
@@ -0,0 +1,60 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2015-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "accounttype.h"
+
+AccountType::AccountType(Module *me) : Serialize::Type<AccountImpl>(me)
+ , display(this, "display", &AccountImpl::display)
+ , pass(this, "pass", &AccountImpl::password)
+ , email(this, "email", &AccountImpl::email)
+ , language(this, "language", &AccountImpl::language)
+{
+
+}
+
+void AccountType::Display::SetField(AccountImpl *acc, const Anope::string &disp)
+{
+ NickServ::nickcore_map& map = NickServ::service->GetAccountMap();
+
+ if (!acc->GetDisplay().empty())
+ map.erase(acc->GetDisplay());
+
+ Serialize::Field<AccountImpl, Anope::string>::SetField(acc, disp);
+
+ if (!disp.empty())
+ map[disp] = acc;
+
+ acc->o = Oper::Find(disp);
+}
+
+NickServ::Account *AccountType::FindAccount(const Anope::string &acc)
+{
+ Serialize::ID id;
+ EventReturn result = EventManager::Get()->Dispatch(&Event::SerializeEvents::OnSerializeFind, this, &this->display, acc, id);
+ if (result == EVENT_ALLOW)
+ return RequireID(id);
+
+ NickServ::nickcore_map &map = NickServ::service->GetAccountMap();
+ auto it = map.find(acc);
+ if (it != map.end())
+ return it->second;
+ return nullptr;
+}
+
diff --git a/modules/nickserv/main/accounttype.h b/modules/nickserv/main/accounttype.h
new file mode 100644
index 000000000..4cdba74b0
--- /dev/null
+++ b/modules/nickserv/main/accounttype.h
@@ -0,0 +1,42 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2015-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "account.h"
+
+class AccountType : public Serialize::Type<AccountImpl>
+{
+ public:
+ /* Name of the account */
+ struct Display : Serialize::Field<AccountImpl, Anope::string>
+ {
+ using Serialize::Field<AccountImpl, Anope::string>::Field;
+
+ void SetField(AccountImpl *s, const Anope::string &) override;
+ } display;
+ /* User password in form of hashm:data */
+ Serialize::Field<AccountImpl, Anope::string> pass;
+ Serialize::Field<AccountImpl, Anope::string> email;
+ /* Locale name of the language of the user. Empty means default language */
+ Serialize::Field<AccountImpl, Anope::string> language;
+
+
+ AccountType(Module *);
+
+ NickServ::Account *FindAccount(const Anope::string &nick);
+};
diff --git a/modules/nickserv/main/identifyrequest.cpp b/modules/nickserv/main/identifyrequest.cpp
new file mode 100644
index 000000000..a99db9441
--- /dev/null
+++ b/modules/nickserv/main/identifyrequest.cpp
@@ -0,0 +1,81 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2015-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "identifyrequest.h"
+
+IdentifyRequestImpl::IdentifyRequestImpl(NickServ::IdentifyRequestListener *li, Module *o, const Anope::string &acc, const Anope::string &pass) : NickServ::IdentifyRequest(li, o, acc, pass)
+{
+ std::set<NickServ::IdentifyRequest *> &requests = NickServ::service->GetIdentifyRequests();
+ requests.insert(this);
+}
+
+IdentifyRequestImpl::~IdentifyRequestImpl()
+{
+ std::set<NickServ::IdentifyRequest *> &requests = NickServ::service->GetIdentifyRequests();
+ requests.erase(this);
+ delete l;
+}
+
+void IdentifyRequestImpl::Hold(Module *m)
+{
+ holds.insert(m);
+}
+
+void IdentifyRequestImpl::Release(Module *m)
+{
+ holds.erase(m);
+ if (holds.empty() && dispatched)
+ {
+ if (!success)
+ l->OnFail(this);
+ delete this;
+ }
+}
+
+void IdentifyRequestImpl::Success(Module *m)
+{
+ if (!success)
+ {
+ l->OnSuccess(this);
+ success = true;
+ }
+}
+
+void IdentifyRequestImpl::Dispatch()
+{
+ if (holds.empty())
+ {
+ if (!success)
+ l->OnFail(this);
+ delete this;
+ }
+ else
+ dispatched = true;
+}
+
+void IdentifyRequestImpl::Unload(Module *m)
+{
+ if (this->GetOwner() != m)
+ return;
+
+ if (!success)
+ l->OnFail(this);
+ delete this;
+}
+
diff --git a/modules/nickserv/main/identifyrequest.h b/modules/nickserv/main/identifyrequest.h
new file mode 100644
index 000000000..6670b5244
--- /dev/null
+++ b/modules/nickserv/main/identifyrequest.h
@@ -0,0 +1,33 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2015-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "modules/nickserv.h"
+
+class IdentifyRequestImpl : public NickServ::IdentifyRequest
+{
+ public:
+ IdentifyRequestImpl(NickServ::IdentifyRequestListener *, Module *o, const Anope::string &acc, const Anope::string &pass);
+ virtual ~IdentifyRequestImpl();
+
+ void Hold(Module *m) override;
+ void Release(Module *m) override;
+ void Success(Module *m) override;
+ void Dispatch() override;
+ void Unload(Module *);
+};
diff --git a/modules/nickserv/main/mode.cpp b/modules/nickserv/main/mode.cpp
new file mode 100644
index 000000000..864ec2123
--- /dev/null
+++ b/modules/nickserv/main/mode.cpp
@@ -0,0 +1,42 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2015-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "modules/nickserv.h"
+#include "modetype.h"
+
+NickServ::Account *ModeImpl::GetAccount()
+{
+ return Get(&NSModeType::account);
+}
+
+void ModeImpl::SetAccount(NickServ::Account *a)
+{
+ Set(&NSModeType::account, a);
+}
+
+Anope::string ModeImpl::GetMode()
+{
+ return Get(&NSModeType::mode);
+}
+
+void ModeImpl::SetMode(const Anope::string &m)
+{
+ Set(&NSModeType::mode, m);
+}
+
diff --git a/modules/nickserv/main/mode.h b/modules/nickserv/main/mode.h
new file mode 100644
index 000000000..a8db73b7e
--- /dev/null
+++ b/modules/nickserv/main/mode.h
@@ -0,0 +1,37 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2015-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+class ModeImpl : public NickServ::Mode
+{
+ friend class NSModeType;
+
+ NickServ::Account *account = nullptr;
+ Anope::string mode;
+
+ public:
+ ModeImpl(Serialize::TypeBase *type) : NickServ::Mode(type) { }
+ ModeImpl(Serialize::TypeBase *type, Serialize::ID id) : NickServ::Mode(type, id) { }
+
+ NickServ::Account *GetAccount() override;
+ void SetAccount(NickServ::Account *) override;
+
+ Anope::string GetMode() override;
+ void SetMode(const Anope::string &) override;
+};
+
diff --git a/modules/nickserv/main/modetype.h b/modules/nickserv/main/modetype.h
new file mode 100644
index 000000000..8bc8c7f7f
--- /dev/null
+++ b/modules/nickserv/main/modetype.h
@@ -0,0 +1,33 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2015-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "mode.h"
+
+class NSModeType : public Serialize::Type<ModeImpl>
+{
+ public:
+ Serialize::ObjectField<ModeImpl, NickServ::Account *> account;
+ Serialize::Field<ModeImpl, Anope::string> mode;
+
+ NSModeType(Module *creator) : Serialize::Type<ModeImpl>(creator)
+ , account(this, "account", &ModeImpl::account, true)
+ , mode(this, "mode", &ModeImpl::mode)
+ {
+ }
+};
diff --git a/modules/nickserv/main/nick.cpp b/modules/nickserv/main/nick.cpp
new file mode 100644
index 000000000..3fc1e6b05
--- /dev/null
+++ b/modules/nickserv/main/nick.cpp
@@ -0,0 +1,199 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2015-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "nicktype.h"
+
+NickImpl::~NickImpl()
+{
+ /* Remove us from the aliases list */
+ NickServ::nickalias_map &map = NickServ::service->GetNickMap();
+ map.erase(GetNick());
+}
+
+void NickImpl::Delete()
+{
+ EventManager::Get()->Dispatch(&Event::DelNick::OnDelNick, this);
+
+ if (this->GetAccount())
+ {
+ /* Next: see if our core is still useful. */
+ std::vector<NickServ::Nick *> aliases = this->GetAccount()->GetRefs<NickServ::Nick *>();
+
+ auto it = std::find(aliases.begin(), aliases.end(), this);
+ if (it != aliases.end())
+ aliases.erase(it);
+
+ if (aliases.empty())
+ {
+ /* just me */
+ this->GetAccount()->Delete();
+ }
+ else
+ {
+ /* Display updating stuff */
+ if (GetNick().equals_ci(this->GetAccount()->GetDisplay()))
+ this->GetAccount()->SetDisplay(aliases[0]);
+ }
+ }
+
+ return Serialize::Object::Delete();
+}
+
+Anope::string NickImpl::GetNick()
+{
+ return Get<Anope::string>(&NickType::nick);
+}
+
+void NickImpl::SetNick(const Anope::string &nick)
+{
+ Set(&NickType::nick, nick);
+}
+
+Anope::string NickImpl::GetLastQuit()
+{
+ return Get(&NickType::last_quit);
+}
+
+void NickImpl::SetLastQuit(const Anope::string &lq)
+{
+ Set(&NickType::last_quit, lq);
+}
+
+Anope::string NickImpl::GetLastRealname()
+{
+ return Get(&NickType::last_realname);
+}
+
+void NickImpl::SetLastRealname(const Anope::string &lr)
+{
+ Set(&NickType::last_realname, lr);
+}
+
+Anope::string NickImpl::GetLastUsermask()
+{
+ return Get(&NickType::last_usermask);
+}
+
+void NickImpl::SetLastUsermask(const Anope::string &lu)
+{
+ Set(&NickType::last_usermask, lu);
+}
+
+Anope::string NickImpl::GetLastRealhost()
+{
+ return Get(&NickType::last_realhost);
+}
+
+void NickImpl::SetLastRealhost(const Anope::string &lr)
+{
+ Set(&NickType::last_realhost, lr);
+}
+
+time_t NickImpl::GetTimeRegistered()
+{
+ return Get(&NickType::time_registered);
+}
+
+void NickImpl::SetTimeRegistered(const time_t &tr)
+{
+ Set(&NickType::time_registered, tr);
+}
+
+time_t NickImpl::GetLastSeen()
+{
+ return Get(&NickType::last_seen);
+}
+
+void NickImpl::SetLastSeen(const time_t &ls)
+{
+ Set(&NickType::last_seen, ls);
+}
+
+NickServ::Account *NickImpl::GetAccount()
+{
+ return Get(&NickType::nc);
+}
+
+void NickImpl::SetAccount(NickServ::Account *acc)
+{
+ Set(&NickType::nc, acc);
+}
+
+void NickImpl::SetVhost(const Anope::string &ident, const Anope::string &host, const Anope::string &creator, time_t created)
+{
+ Set(&NickType::vhost_ident, ident);
+ Set(&NickType::vhost_host, host);
+ Set(&NickType::vhost_creator, creator);
+ Set(&NickType::vhost_created, created);
+}
+
+void NickImpl::RemoveVhost()
+{
+ Anope::string e;
+ Set(&NickType::vhost_ident, e);
+ Set(&NickType::vhost_host, e);
+ Set(&NickType::vhost_creator, e);
+ Set(&NickType::vhost_created, 0);
+}
+
+bool NickImpl::HasVhost()
+{
+ return !GetVhostHost().empty();
+}
+
+Anope::string NickImpl::GetVhostIdent()
+{
+ return Get(&NickType::vhost_ident);
+}
+
+void NickImpl::SetVhostIdent(const Anope::string &i)
+{
+ Set(&NickType::vhost_ident, i);
+}
+
+Anope::string NickImpl::GetVhostHost()
+{
+ return Get(&NickType::vhost_host);
+}
+
+void NickImpl::SetVhostHost(const Anope::string &h)
+{
+ Set(&NickType::vhost_host, h);
+}
+
+Anope::string NickImpl::GetVhostCreator()
+{
+ return Get(&NickType::vhost_creator);
+}
+
+void NickImpl::SetVhostCreator(const Anope::string &c)
+{
+ Set(&NickType::vhost_creator, c);
+}
+
+time_t NickImpl::GetVhostCreated()
+{
+ return Get(&NickType::vhost_created);
+}
+
+void NickImpl::SetVhostCreated(const time_t &cr)
+{
+ Set(&NickType::vhost_created, cr);
+}
diff --git a/modules/nickserv/main/nick.h b/modules/nickserv/main/nick.h
new file mode 100644
index 000000000..ce0111b59
--- /dev/null
+++ b/modules/nickserv/main/nick.h
@@ -0,0 +1,72 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2015-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+class NickImpl : public NickServ::Nick
+{
+ friend class NickType;
+
+ NickServ::Account *account = nullptr;
+ Anope::string nick, last_quit, last_realname, last_usermask, last_realhost;
+ time_t time_registered = 0, last_seen = 0;
+ Anope::string vhost_ident, vhost_host, vhost_creator;
+ time_t vhost_created = 0;
+
+ public:
+ NickImpl(Serialize::TypeBase *type) : NickServ::Nick(type) { }
+ NickImpl(Serialize::TypeBase *type, Serialize::ID id) : NickServ::Nick(type, id) { }
+ ~NickImpl();
+ void Delete() override;
+
+ Anope::string GetNick() override;
+ void SetNick(const Anope::string &) override;
+
+ Anope::string GetLastQuit() override;
+ void SetLastQuit(const Anope::string &) override;
+
+ Anope::string GetLastRealname() override;
+ void SetLastRealname(const Anope::string &) override;
+
+ Anope::string GetLastUsermask() override;
+ void SetLastUsermask(const Anope::string &) override;
+
+ Anope::string GetLastRealhost() override;
+ void SetLastRealhost(const Anope::string &) override;
+
+ time_t GetTimeRegistered() override;
+ void SetTimeRegistered(const time_t &) override;
+
+ time_t GetLastSeen() override;
+ void SetLastSeen(const time_t &) override;
+
+ NickServ::Account *GetAccount() override;
+ void SetAccount(NickServ::Account *acc) override;
+
+ void SetVhost(const Anope::string &ident, const Anope::string &host, const Anope::string &creator, time_t created = Anope::CurTime) override;
+ void RemoveVhost() override;
+ bool HasVhost() override;
+
+ Anope::string GetVhostIdent() override;
+ void SetVhostIdent(const Anope::string &) override;
+ Anope::string GetVhostHost() override;
+ void SetVhostHost(const Anope::string &) override;
+ Anope::string GetVhostCreator() override;
+ void SetVhostCreator(const Anope::string &) override;
+ time_t GetVhostCreated() override;
+ void SetVhostCreated(const time_t &) override;
+};
diff --git a/modules/nickserv/main/nickserv.cpp b/modules/nickserv/main/nickserv.cpp
new file mode 100644
index 000000000..8f33caf87
--- /dev/null
+++ b/modules/nickserv/main/nickserv.cpp
@@ -0,0 +1,693 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2011-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/nickserv/info.h"
+#include "modules/nickserv/group.h"
+#include "modules/nickserv/update.h"
+#include "modules/help.h"
+#include "modules/nickserv.h"
+#include "identifyrequest.h"
+#include "nicktype.h"
+#include "accounttype.h"
+#include "modetype.h"
+
+class NickServCollide;
+static std::set<NickServCollide *> collides;
+
+/** Timer for colliding nicks to force people off of nicknames
+ */
+class NickServCollide : public Timer
+{
+ NickServ::NickServService *service;
+ Reference<User> u;
+ time_t ts;
+ Reference<NickServ::Nick> na;
+
+ public:
+ NickServCollide(Module *me, NickServ::NickServService *nss, User *user, NickServ::Nick *nick, time_t delay) : Timer(me, delay), service(nss), u(user), ts(user->timestamp), na(nick)
+ {
+ collides.insert(this);
+ }
+
+ ~NickServCollide()
+ {
+ collides.erase(this);
+ }
+
+ NickServ::Nick *GetNick()
+ {
+ return na;
+ }
+
+ User *GetUser()
+ {
+ return u;
+ }
+
+ void Tick(time_t t) override
+ {
+ if (!u || !na)
+ return;
+ /* If they identified or don't exist anymore, don't kill them. */
+ if (u->Account() == na->GetAccount() || u->timestamp > ts)
+ return;
+
+ service->Collide(u, na);
+ }
+};
+
+/** Timer for removing HELD status from nicks.
+ */
+class NickServHeld : public Timer
+{
+ Reference<NickServ::Nick> na;
+ Anope::string nick;
+ public:
+ NickServHeld(Module *me, NickServ::Nick *n, long l) : Timer(me, l), na(n), nick(na->GetNick())
+ {
+ n->SetS<bool>("HELD", true);
+ }
+
+ void Tick(time_t) override
+ {
+ if (na)
+ na->UnsetS<bool>("HELD");
+ }
+};
+
+class NickServRelease;
+static Anope::map<NickServRelease *> NickServReleases;
+
+/** Timer for releasing nicks to be available for use
+ */
+class NickServRelease : public User, public Timer
+{
+ Anope::string nick;
+
+ public:
+ NickServRelease(Module *me, NickServ::Nick *na, time_t delay) : User(na->GetNick(), Config->GetModule("nickserv")->Get<Anope::string>("enforceruser", "user"),
+ Config->GetModule("nickserv")->Get<Anope::string>("enforcerhost", "services.localhost.net"), "", "", Me, "Services Enforcer", Anope::CurTime, "", IRCD->UID_Retrieve(), NULL), Timer(me, delay), nick(na->GetNick())
+ {
+ /* Erase the current release timer and use the new one */
+ Anope::map<NickServRelease *>::iterator nit = NickServReleases.find(this->nick);
+ if (nit != NickServReleases.end())
+ {
+ IRCD->SendQuit(nit->second, "");
+ delete nit->second;
+ }
+
+ NickServReleases.insert(std::make_pair(this->nick, this));
+
+ IRCD->SendClientIntroduction(this);
+ }
+
+ ~NickServRelease()
+ {
+ IRCD->SendQuit(this, "");
+ NickServReleases.erase(this->nick);
+ }
+
+ void Tick(time_t t) override { }
+};
+
+class NickServCore : public Module, public NickServ::NickServService
+ , public EventHook<Event::Shutdown>
+ , public EventHook<Event::Restart>
+ , public EventHook<Event::UserLogin>
+ , public EventHook<Event::DelNick>
+ , public EventHook<Event::DelCore>
+ , public EventHook<Event::ChangeCoreDisplay>
+ , public EventHook<Event::NickIdentify>
+ , public EventHook<Event::NickGroup>
+ , public EventHook<Event::NickUpdate>
+ , public EventHook<Event::UserConnect>
+ , public EventHook<Event::PostUserLogoff>
+ , public EventHook<Event::ServerSync>
+ , public EventHook<Event::UserNickChange>
+ , public EventHook<Event::UserModeSet>
+ , public EventHook<Event::Help>
+ , public EventHook<Event::ExpireTick>
+ , public EventHook<Event::NickInfo>
+ , public EventHook<Event::ModuleUnload>
+ , public EventHook<NickServ::Event::NickRegister>
+ , public EventHook<Event::UserQuit>
+{
+ Reference<ServiceBot> NickServ;
+ std::vector<Anope::string> defaults;
+ ExtensibleItem<bool> held, collided;
+ std::set<NickServ::IdentifyRequest *> identifyrequests;
+ NickServ::nickalias_map NickList;
+ NickServ::nickcore_map AccountList;
+ NickType nick_type;
+ AccountType account_type;
+ NSModeType mode_type;
+
+ void OnCancel(User *u, NickServ::Nick *na)
+ {
+ if (collided.HasExt(na))
+ {
+ collided.Unset(na);
+
+ new NickServHeld(this, na, Config->GetModule("nickserv")->Get<time_t>("releasetimeout", "1m"));
+
+ if (IRCD->CanSVSHold)
+ IRCD->SendSVSHold(na->GetNick(), Config->GetModule("nickserv")->Get<time_t>("releasetimeout", "1m"));
+ else
+ new NickServRelease(this, na, Config->GetModule("nickserv")->Get<time_t>("releasetimeout", "1m"));
+ }
+ }
+
+ public:
+ NickServCore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, PSEUDOCLIENT | VENDOR)
+ , NickServ::NickServService(this)
+
+ , EventHook<Event::Shutdown>(this)
+ , EventHook<Event::Restart>(this)
+ , EventHook<Event::UserLogin>(this)
+ , EventHook<Event::DelNick>(this)
+ , EventHook<Event::DelCore>(this)
+ , EventHook<Event::ChangeCoreDisplay>(this)
+ , EventHook<Event::NickIdentify>(this)
+ , EventHook<Event::NickGroup>(this)
+ , EventHook<Event::NickUpdate>(this)
+ , EventHook<Event::UserConnect>(this)
+ , EventHook<Event::PostUserLogoff>(this)
+ , EventHook<Event::ServerSync>(this)
+ , EventHook<Event::UserNickChange>(this)
+ , EventHook<Event::UserModeSet>(this)
+ , EventHook<Event::Help>(this)
+ , EventHook<Event::ExpireTick>(this)
+ , EventHook<Event::NickInfo>(this)
+ , EventHook<Event::ModuleUnload>(this)
+ , EventHook<NickServ::Event::NickRegister>(this)
+ , EventHook<Event::UserQuit>(this)
+
+ , held(this, "HELD")
+ , collided(this, "COLLIDED")
+ , nick_type(this)
+ , account_type(this)
+ , mode_type(this)
+ {
+ NickServ::service = this;
+ }
+
+ ~NickServCore()
+ {
+ OnShutdown();
+ NickServ::service = nullptr;
+ }
+
+ void OnShutdown() override
+ {
+ /* On shutdown, restart, or mod unload, remove all of our holds for nicks (svshold or qlines)
+ * because some IRCds do not allow us to have these automatically expire
+ */
+ for (NickServ::Nick *na : nick_type.List<NickServ::Nick *>())
+ this->Release(na);
+ }
+
+ void OnRestart() override
+ {
+ OnShutdown();
+ }
+
+ void Validate(User *u) override
+ {
+ NickServ::Nick *na = NickServ::FindNick(u->nick);
+ if (!na)
+ return;
+
+ EventReturn MOD_RESULT = EventManager::Get()->Dispatch(&NickServ::Event::NickValidate::OnNickValidate, u, na);
+ if (MOD_RESULT == EVENT_STOP)
+ {
+ this->Collide(u, na);
+ return;
+ }
+ else if (MOD_RESULT == EVENT_ALLOW)
+ return;
+
+ if (!na->GetAccount()->HasFieldS("NS_SECURE") && u->IsRecognized())
+ {
+ na->SetLastSeen(Anope::CurTime);
+ na->SetLastUsermask(u->GetIdent() + "@" + u->GetDisplayedHost());
+ na->SetLastRealname(u->realname);
+ return;
+ }
+
+ if (Config->GetModule("nickserv")->Get<bool>("nonicknameownership"))
+ return;
+
+ bool on_access = u->IsRecognized(false);
+
+ if (on_access || !na->GetAccount()->HasFieldS("KILL_IMMED"))
+ {
+ if (na->GetAccount()->HasFieldS("NS_SECURE"))
+ u->SendMessage(*NickServ, _("This nickname is registered and protected. If this is your nickname, type \002{0}{1} IDENTIFY \037password\037\002. Otherwise, please choose a different nickname."), Config->StrictPrivmsg, NickServ->nick); // XXX
+ else
+ u->SendMessage(*NickServ, _("This nickname is owned by someone else. If this is your nickname, type \002{0}{1} IDENTIFY \037password\037\002. Otherwise, please choose a different nickname."), Config->StrictPrivmsg, NickServ->nick); // XXX
+ }
+ if (na->GetAccount()->HasFieldS("KILLPROTECT") && !on_access)
+ {
+ if (na->GetAccount()->HasFieldS("KILL_IMMED"))
+ {
+ u->SendMessage(*NickServ, _("This nickname has been registered; you may not use it."));
+ this->Collide(u, na);
+ }
+ else if (na->GetAccount()->HasFieldS("KILL_QUICK"))
+ {
+ time_t killquick = Config->GetModule("nickserv")->Get<time_t>("killquick", "20s");
+ u->SendMessage(*NickServ, _("If you do not change within %s, I will change your nick."), Anope::Duration(killquick, u->Account()).c_str());
+ new NickServCollide(this, this, u, na, killquick);
+ }
+ else
+ {
+ time_t kill = Config->GetModule("nickserv")->Get<time_t>("kill", "60s");
+ u->SendMessage(*NickServ, _("If you do not change within %s, I will change your nick."), Anope::Duration(kill, u->Account()).c_str());
+ new NickServCollide(this, this, u, na, kill);
+ }
+ }
+
+ }
+
+ void OnUserLogin(User *u) override
+ {
+ NickServ::Nick *na = NickServ::FindNick(u->nick);
+ if (na && na->GetAccount() == u->Account() && !Config->GetModule("nickserv")->Get<bool>("nonicknameownership") && !na->GetAccount()->HasFieldS("UNCONFIRMED"))
+ u->SetMode(NickServ, "REGISTERED");
+
+ const Anope::string &modesonid = Config->GetModule(this)->Get<Anope::string>("modesonid");
+ if (!modesonid.empty())
+ u->SetModes(NickServ, "%s", modesonid.c_str());
+ }
+
+ void Collide(User *u, NickServ::Nick *na) override
+ {
+ if (na)
+ collided.Set(na, true);
+
+ if (IRCD->CanSVSNick)
+ {
+ unsigned nicklen = Config->GetBlock("networkinfo")->Get<unsigned>("nicklen");
+ const Anope::string &guestprefix = Config->GetModule("nickserv")->Get<Anope::string>("guestnickprefix", "Guest");
+
+ Anope::string guestnick;
+
+ int i = 0;
+ do
+ {
+ guestnick = guestprefix + stringify(static_cast<uint16_t>(rand()));
+ if (guestnick.length() > nicklen)
+ guestnick = guestnick.substr(0, nicklen);
+ }
+ while (User::Find(guestnick, true) && i++ < 10);
+
+ if (i == 11)
+ u->Kill(*NickServ, "Services nickname-enforcer kill");
+ else
+ {
+ u->SendMessage(*NickServ, _("Your nickname is now being changed to \002%s\002"), guestnick.c_str());
+ IRCD->SendForceNickChange(u, guestnick, Anope::CurTime);
+ }
+ }
+ else
+ u->Kill(*NickServ, "Services nickname-enforcer kill");
+ }
+
+ void Release(NickServ::Nick *na) override
+ {
+ if (held.HasExt(na))
+ {
+ if (IRCD->CanSVSHold)
+ IRCD->SendSVSHoldDel(na->GetNick());
+ else
+ {
+ User *u = User::Find(na->GetNick(), true);
+ if (u && u->server == Me)
+ {
+ u->Quit();
+ }
+ }
+
+ held.Unset(na);
+ }
+ collided.Unset(na); /* clear pending collide */
+ }
+
+ NickServ::IdentifyRequest *CreateIdentifyRequest(NickServ::IdentifyRequestListener *l, Module *o, const Anope::string &acc, const Anope::string &pass) override
+ {
+ return new IdentifyRequestImpl(l, o, acc, pass);
+ }
+
+ std::set<NickServ::IdentifyRequest *>& GetIdentifyRequests() override
+ {
+ return identifyrequests;
+ }
+
+ std::vector<NickServ::Nick *> GetNickList() override
+ {
+ return nick_type.List<NickServ::Nick *>();
+ }
+
+ NickServ::nickalias_map& GetNickMap() override
+ {
+ return NickList;
+ }
+
+ std::vector<NickServ::Account *> GetAccountList() override
+ {
+ return account_type.List<NickServ::Account *>();
+ }
+
+ NickServ::nickcore_map& GetAccountMap() override
+ {
+ return AccountList;
+ }
+
+ NickServ::Nick *FindNick(const Anope::string &nick) override
+ {
+ return nick_type.FindNick(nick);
+ }
+
+ NickServ::Account *FindAccount(const Anope::string &acc) override
+ {
+ return account_type.FindAccount(acc);
+ }
+
+ void OnReload(Configuration::Conf *conf) override
+ {
+ const Anope::string &nsnick = conf->GetModule(this)->Get<Anope::string>("client");
+
+ if (nsnick.empty())
+ throw ConfigException(Module::name + ": <client> must be defined");
+
+ ServiceBot *bi = ServiceBot::Find(nsnick, true);
+ if (!bi)
+ throw ConfigException(Module::name + ": no bot named " + nsnick);
+
+ NickServ = bi;
+
+ spacesepstream(conf->GetModule(this)->Get<Anope::string>("defaults", "ns_secure memo_signon memo_receive")).GetTokens(defaults);
+ if (defaults.empty())
+ {
+ defaults.push_back("NS_SECURE");
+ defaults.push_back("MEMO_SIGNON");
+ defaults.push_back("MEMO_RECEIVE");
+ }
+ else if (defaults[0].equals_ci("none"))
+ defaults.clear();
+ }
+
+ void OnDelNick(NickServ::Nick *na) override
+ {
+ User *u = User::Find(na->GetNick(), true);
+ if (u && u->Account() == na->GetAccount())
+ {
+ IRCD->SendLogout(u);
+ u->RemoveMode(NickServ, "REGISTERED");
+ u->Logout();
+ }
+ }
+
+ void OnDelCore(NickServ::Account *nc) override
+ {
+ Log(NickServ, "nick") << "Deleting nickname group " << nc->GetDisplay();
+
+ /* Clean up this nick core from any users online */
+ for (unsigned int i = nc->users.size(); i > 0; --i)
+ {
+ User *user = nc->users[i - 1];
+ IRCD->SendLogout(user);
+ user->RemoveMode(NickServ, "REGISTERED");
+ user->Logout();
+ EventManager::Get()->Dispatch(&Event::NickLogout::OnNickLogout, user);
+ }
+ }
+
+ void OnChangeCoreDisplay(NickServ::Account *nc, const Anope::string &newdisplay) override
+ {
+ Log(LOG_NORMAL, "nick", NickServ) << "Changing " << nc->GetDisplay() << " nickname group display to " << newdisplay;
+ }
+
+ void OnNickIdentify(User *u) override
+ {
+ Configuration::Block *block = Config->GetModule(this);
+
+ if (block->Get<bool>("modeonid", "yes"))
+
+ for (User::ChanUserList::iterator it = u->chans.begin(), it_end = u->chans.end(); it != it_end; ++it)
+ {
+ ChanUserContainer *cc = it->second;
+ Channel *c = cc->chan;
+ if (c)
+ c->SetCorrectModes(u, true);
+ }
+
+ const Anope::string &modesonid = block->Get<Anope::string>("modesonid");
+ if (!modesonid.empty())
+ u->SetModes(NickServ, "%s", modesonid.c_str());
+
+ if (block->Get<bool>("forceemail", "yes") && u->Account()->GetEmail().empty())
+ {
+ u->SendMessage(*NickServ, _("You must now supply an e-mail for your nick.\n"
+ "This e-mail will allow you to retrieve your password in\n"
+ "case you forget it."));
+ u->SendMessage(*NickServ, _("Type \002%s%s SET EMAIL \037e-mail\037\002 in order to set your e-mail.\n"
+ "Your privacy is respected; this e-mail won't be given to\n"
+ "any third-party person."), Config->StrictPrivmsg.c_str(), NickServ->nick.c_str());
+ }
+
+ for (std::set<NickServCollide *>::iterator it = collides.begin(); it != collides.end(); ++it)
+ {
+ NickServCollide *c = *it;
+ if (c->GetUser() == u && c->GetNick() && c->GetNick()->GetAccount() == u->Account())
+ {
+ delete c;
+ break;
+ }
+ }
+ }
+
+ void OnNickGroup(User *u, NickServ::Nick *target) override
+ {
+ if (!target->GetAccount()->HasFieldS("UNCONFIRMED"))
+ u->SetMode(NickServ, "REGISTERED");
+ }
+
+ void OnNickUpdate(User *u) override
+ {
+ for (User::ChanUserList::iterator it = u->chans.begin(), it_end = u->chans.end(); it != it_end; ++it)
+ {
+ ChanUserContainer *cc = it->second;
+ Channel *c = cc->chan;
+ if (c)
+ c->SetCorrectModes(u, true);
+ }
+ }
+
+ void OnUserConnect(User *u, bool &exempt) override
+ {
+ if (u->Quitting() || !u->server->IsSynced() || u->server->IsULined())
+ return;
+
+ const NickServ::Nick *na = NickServ::FindNick(u->nick);
+
+ const Anope::string &unregistered_notice = Config->GetModule(this)->Get<Anope::string>("unregistered_notice");
+ if (!Config->GetModule("nickserv")->Get<bool>("nonicknameownership") && !unregistered_notice.empty() && !na && !u->Account())
+ u->SendMessage(*NickServ, unregistered_notice.replace_all_cs("%n", u->nick));
+ else if (na && !u->IsIdentified(true))
+ this->Validate(u);
+ }
+
+ void OnPostUserLogoff(User *u) override
+ {
+ NickServ::Nick *na = NickServ::FindNick(u->nick);
+ if (na)
+ OnCancel(u, na);
+ }
+
+ void OnServerSync(Server *s) override
+ {
+ for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it)
+ {
+ User *u = it->second;
+
+ if (u->server == s)
+ {
+ if (u->HasMode("REGISTERED") && !u->IsIdentified(true))
+ u->RemoveMode(NickServ, "REGISTERED");
+ if (!u->IsIdentified())
+ this->Validate(u);
+ }
+ }
+ }
+
+ void OnUserNickChange(User *u, const Anope::string &oldnick) override
+ {
+ NickServ::Nick *old_na = NickServ::FindNick(oldnick), *na = NickServ::FindNick(u->nick);
+ /* If the new nick isn't registered or it's registered and not yours */
+ if (!na || na->GetAccount() != u->Account())
+ {
+ /* Remove +r, but keep an account associated with the user */
+ u->RemoveMode(NickServ, "REGISTERED");
+
+ this->Validate(u);
+ }
+ else
+ {
+ /* Reset +r and re-send account (even though it really should be set at this point) */
+ IRCD->SendLogin(u, na);
+ if (!Config->GetModule("nickserv")->Get<bool>("nonicknameownership") && na->GetAccount() == u->Account() && !na->GetAccount()->HasFieldS("UNCONFIRMED"))
+ u->SetMode(NickServ, "REGISTERED");
+ Log(u, "", NickServ) << u->GetMask() << " automatically identified for group " << u->Account()->GetDisplay();
+ }
+
+ if (!u->nick.equals_ci(oldnick) && old_na)
+ OnCancel(u, old_na);
+ }
+
+ void OnUserModeSet(const MessageSource &setter, User *u, const Anope::string &mname) override
+ {
+ if (u->server->IsSynced() && mname == "REGISTERED" && !u->IsIdentified(true))
+ u->RemoveMode(NickServ, mname);
+ }
+
+ EventReturn OnPreHelp(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ if (!params.empty() || source.c || source.service != *NickServ)
+ return EVENT_CONTINUE;
+ if (!Config->GetModule("nickserv")->Get<bool>("nonicknameownership"))
+ source.Reply(_("\002%s\002 allows you to register a nickname and\n"
+ "prevent others from using it. The following\n"
+ "commands allow for registration and maintenance of\n"
+ "nicknames; to use them, type \002%s%s \037command\037\002.\n"
+ "For more information on a specific command, type\n"
+ "\002%s%s %s \037command\037\002.\n"), NickServ->nick.c_str(), Config->StrictPrivmsg.c_str(), NickServ->nick.c_str(), Config->StrictPrivmsg.c_str(), NickServ->nick.c_str(), source.command.c_str());
+ else
+ source.Reply(_("\002%s\002 allows you to register an account.\n"
+ "The following commands allow for registration and maintenance of\n"
+ "accounts; to use them, type \002%s%s \037command\037\002.\n"
+ "For more information on a specific command, type\n"
+ "\002%s%s %s \037command\037\002.\n"), NickServ->nick.c_str(), Config->StrictPrivmsg.c_str(), NickServ->nick.c_str(), Config->StrictPrivmsg.c_str(), NickServ->nick.c_str(), source.command.c_str());
+ return EVENT_CONTINUE;
+ }
+
+ void OnPostHelp(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ if (!params.empty() || source.c || source.service != *NickServ)
+ return;
+ if (source.IsServicesOper())
+ source.Reply(_(" \n"
+ "Services Operators can also drop any nickname without needing\n"
+ "to identify for the nick, and may view the access list for\n"
+ "any nickname."));
+ time_t nickserv_expire = Config->GetModule(this)->Get<time_t>("expire", "21d");
+ if (nickserv_expire >= 86400)
+ source.Reply(_(" \n"
+ "Accounts that are not used anymore are subject to\n"
+ "the automatic expiration, i.e. they will be deleted\n"
+ "after %d days if not used."), nickserv_expire / 86400);
+ source.Reply(_(" \n"
+ "\002NOTICE:\002 This service is intended to provide a way for\n"
+ "IRC users to ensure their identity is not compromised.\n"
+ "It is \002NOT\002 intended to facilitate \"stealing\" of\n"
+ "nicknames or other malicious actions. Abuse of %s\n"
+ "will result in, at minimum, loss of the abused\n"
+ "nickname(s)."), NickServ->nick.c_str());
+ }
+
+ void OnNickRegister(User *, NickServ::Nick *na, const Anope::string &) override
+ {
+ /* Set default flags */
+ for (unsigned i = 0; i < defaults.size(); ++i)
+ na->GetAccount()->SetS<bool>(defaults[i].upper(), true);
+ }
+
+ void OnUserQuit(User *u, const Anope::string &msg) override
+ {
+ if (u->server && !u->server->GetQuitReason().empty() && Config->GetModule(this)->Get<bool>("hidenetsplitquit"))
+ return;
+
+ /* Update last quit and last seen for the user */
+ NickServ::Nick *na = NickServ::FindNick(u->nick);
+ if (na && !na->GetAccount()->HasFieldS("NS_SUSPENDED") && (u->IsRecognized() || u->IsIdentified(true)))
+ {
+ na->SetLastSeen(Anope::CurTime);
+ na->SetLastQuit(msg);
+ }
+ }
+
+ void OnExpireTick() override
+ {
+ if (Anope::NoExpire || Anope::ReadOnly)
+ return;
+
+ time_t nickserv_expire = Config->GetModule(this)->Get<time_t>("expire", "21d");
+
+ for (NickServ::Nick *na : nick_type.List<NickServ::Nick *>())
+ {
+ User *u = User::Find(na->GetNick(), true);
+ if (u && (u->IsIdentified(true) || u->IsRecognized()))
+ na->SetLastSeen(Anope::CurTime);
+
+ bool expire = false;
+
+ if (nickserv_expire && Anope::CurTime - na->GetLastSeen() >= nickserv_expire)
+ expire = true;
+
+ EventManager::Get()->Dispatch(&NickServ::Event::PreNickExpire::OnPreNickExpire, na, expire);
+
+ if (expire)
+ {
+ Log(LOG_NORMAL, "nickserv/expire", NickServ) << "Expiring nickname " << na->GetNick() << " (group: " << na->GetAccount()->GetDisplay() << ") (e-mail: " << (na->GetAccount()->GetEmail().empty() ? "none" : na->GetAccount()->GetEmail()) << ")";
+ EventManager::Get()->Dispatch(&NickServ::Event::NickExpire::OnNickExpire, na);
+ delete na;
+ }
+ }
+ }
+
+ void OnNickInfo(CommandSource &source, NickServ::Nick *na, InfoFormatter &info, bool show_hidden) override
+ {
+ if (!na->GetAccount()->HasFieldS("UNCONFIRMED"))
+ {
+ time_t nickserv_expire = Config->GetModule(this)->Get<time_t>("expire", "21d");
+ if (!na->HasFieldS("NS_NO_EXPIRE") && nickserv_expire && !Anope::NoExpire && (source.HasPriv("nickserv/auspex") || na->GetLastSeen() != Anope::CurTime))
+ info[_("Expires")] = Anope::strftime(na->GetLastSeen() + nickserv_expire, source.GetAccount());
+ }
+ else
+ {
+ time_t unconfirmed_expire = Config->GetModule(this)->Get<time_t>("unconfirmedexpire", "1d");
+ info[_("Expires")] = Anope::strftime(na->GetTimeRegistered() + unconfirmed_expire, source.GetAccount());
+ }
+ }
+
+ void OnModuleUnload(User *u, Module *m) override
+ {
+ for (std::set<NickServ::IdentifyRequest *>::iterator it = identifyrequests.begin(), it_end = identifyrequests.end(); it != it_end;)
+ {
+ IdentifyRequestImpl *ir = anope_dynamic_static_cast<IdentifyRequestImpl *>(*it);
+ ++it;
+
+ ir->Unload(m);
+ }
+ }
+};
+
+MODULE_INIT(NickServCore)
+
diff --git a/modules/nickserv/main/nicktype.cpp b/modules/nickserv/main/nicktype.cpp
new file mode 100644
index 000000000..465cf2062
--- /dev/null
+++ b/modules/nickserv/main/nicktype.cpp
@@ -0,0 +1,64 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2015-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "nicktype.h"
+
+NickType::NickType(Module *me) : Serialize::Type<NickImpl>(me)
+ , nick(this, "nick", &NickImpl::nick)
+ , last_quit(this, "last_quit", &NickImpl::last_quit)
+ , last_realname(this, "last_realname", &NickImpl::last_realname)
+ , last_usermask(this, "last_usermask", &NickImpl::last_usermask)
+ , last_realhost(this, "last_realhost", &NickImpl::last_realhost)
+ , time_registered(this, "time_registered", &NickImpl::time_registered)
+ , last_seen(this, "last_seen", &NickImpl::last_seen)
+ , vhost_ident(this, "vhost_ident", &NickImpl::vhost_ident)
+ , vhost_host(this, "vhost_host", &NickImpl::vhost_host)
+ , vhost_creator(this, "vhost_creator", &NickImpl::vhost_creator)
+ , vhost_created(this, "vhost_created", &NickImpl::vhost_created)
+ , nc(this, "nc", &NickImpl::account)
+{
+
+}
+
+void NickType::Nick::SetField(NickImpl *na, const Anope::string &value)
+{
+ /* Remove us from the aliases list */
+ NickServ::nickalias_map &map = NickServ::service->GetNickMap();
+ map.erase(GetField(na));
+
+ Serialize::Field<NickImpl, Anope::string>::SetField(na, value);
+
+ map[value] = na;
+}
+
+NickServ::Nick *NickType::FindNick(const Anope::string &n)
+{
+ Serialize::ID id;
+ EventReturn result = EventManager::Get()->Dispatch(&Event::SerializeEvents::OnSerializeFind, this, &this->nick, n, id);
+ if (result == EVENT_ALLOW)
+ return RequireID(id);
+
+ NickServ::nickalias_map &map = NickServ::service->GetNickMap();
+ auto it = map.find(n);
+ if (it != map.end())
+ return it->second;
+ return nullptr;
+}
+
diff --git a/modules/nickserv/main/nicktype.h b/modules/nickserv/main/nicktype.h
new file mode 100644
index 000000000..cd875953a
--- /dev/null
+++ b/modules/nickserv/main/nicktype.h
@@ -0,0 +1,51 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2015-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "nick.h"
+
+class NickType : public Serialize::Type<NickImpl>
+{
+ public:
+ struct Nick : Serialize::Field<NickImpl, Anope::string>
+ {
+ using Serialize::Field<NickImpl, Anope::string>::Field;
+
+ void SetField(NickImpl *s, const Anope::string &value) override;
+ } nick;
+ Serialize::Field<NickImpl, Anope::string> last_quit;
+ Serialize::Field<NickImpl, Anope::string> last_realname;
+ /* Last usermask this nick was seen on, eg user@host */
+ Serialize::Field<NickImpl, Anope::string> last_usermask;
+ /* Last uncloaked usermask, requires nickserv/auspex to see */
+ Serialize::Field<NickImpl, Anope::string> last_realhost;
+ Serialize::Field<NickImpl, time_t> time_registered;
+ Serialize::Field<NickImpl, time_t> last_seen;
+
+ Serialize::Field<NickImpl, Anope::string> vhost_ident;
+ Serialize::Field<NickImpl, Anope::string> vhost_host;
+ Serialize::Field<NickImpl, Anope::string> vhost_creator;
+ Serialize::Field<NickImpl, time_t> vhost_created;
+
+ /* Account this nick is tied to. Multiple nicks can be tied to a single account. */
+ Serialize::ObjectField<NickImpl, NickServ::Account *> nc;
+
+ NickType(Module *);
+
+ NickServ::Nick *FindNick(const Anope::string &nick);
+};
diff --git a/modules/ns_maxemail.cpp b/modules/nickserv/maxemail.cpp
index a3016311a..0660e7b92 100644
--- a/modules/ns_maxemail.cpp
+++ b/modules/nickserv/maxemail.cpp
@@ -1,20 +1,28 @@
-/* ns_maxemail.cpp - Limit the amount of times an email address
- * can be used for a NickServ account.
+/*
+ * Anope IRC Services
*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
*
- * Included in the Anope module pack since Anope 1.7.9
- * Anope Coder: GeniusDex <geniusdex@anope.org>
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
*
- * Please read COPYING and README for further details.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "module.h"
class NSMaxEmail : public Module
+ , public EventHook<Event::PreCommand>
{
- bool clean;
+ bool clean = false;
/* strip dots from username, and remove anything after the first + */
Anope::string CleanMail(const Anope::string &email)
@@ -46,14 +54,14 @@ class NSMaxEmail : public Module
return false;
if (NSEmailMax == 1)
- source.Reply(_("The email address \002%s\002 has reached its usage limit of 1 user."), email.c_str());
+ source.Reply(_("The email address \002{0}\002 has reached its usage limit of \0021\002 user."), email);
else
- source.Reply(_("The email address \002%s\002 has reached its usage limit of %d users."), email.c_str(), NSEmailMax);
+ source.Reply(_("The email address \002{0}\002 has reached its usage limit of \002{1}\002 users."), email, NSEmailMax);
return true;
}
- int CountEmail(const Anope::string &email, NickCore *unc)
+ int CountEmail(const Anope::string &email, NickServ::Account *unc)
{
int count = 0;
@@ -62,13 +70,11 @@ class NSMaxEmail : public Module
Anope::string cleanemail = clean ? CleanMail(email) : email;
- for (nickcore_map::const_iterator it = NickCoreList->begin(), it_end = NickCoreList->end(); it != it_end; ++it)
+ for (NickServ::Account *nc : NickServ::service->GetAccountList())
{
- const NickCore *nc = it->second;
-
- Anope::string cleannc = clean ? CleanMail(nc->email) : nc->email;
+ Anope::string cleannc = clean ? CleanMail(nc->GetEmail()) : nc->GetEmail();
- if (unc != nc && cleanemail == cleannc)
+ if (unc != nc && cleanemail.equals_ci(cleannc))
++count;
}
@@ -77,33 +83,33 @@ class NSMaxEmail : public Module
public:
NSMaxEmail(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
- , clean(false)
+ , EventHook<Event::PreCommand>(this)
{
}
- void OnReload(Configuration::Conf *conf) anope_override
+ void OnReload(Configuration::Conf *conf) override
{
clean = conf->GetModule(this)->Get<bool>("remove_aliases", "true");
}
- EventReturn OnPreCommand(CommandSource &source, Command *command, std::vector<Anope::string> &params) anope_override
+ EventReturn OnPreCommand(CommandSource &source, Command *command, std::vector<Anope::string> &params) override
{
if (source.IsOper())
return EVENT_CONTINUE;
- if (command->name == "nickserv/register")
+ if (command->GetName() == "nickserv/register")
{
if (this->CheckLimitReached(source, params.size() > 1 ? params[1] : ""))
return EVENT_STOP;
}
- else if (command->name == "nickserv/set/email")
+ else if (command->GetName() == "nickserv/set/email")
{
if (this->CheckLimitReached(source, params.size() > 0 ? params[0] : ""))
return EVENT_STOP;
}
- else if (command->name == "nickserv/ungroup" && source.GetAccount())
+ else if (command->GetName() == "nickserv/ungroup" && source.GetAccount())
{
- if (this->CheckLimitReached(source, source.GetAccount()->email))
+ if (this->CheckLimitReached(source, source.GetAccount()->GetEmail()))
return EVENT_STOP;
}
diff --git a/modules/nickserv/recover.cpp b/modules/nickserv/recover.cpp
new file mode 100644
index 000000000..287965373
--- /dev/null
+++ b/modules/nickserv/recover.cpp
@@ -0,0 +1,307 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/nickserv/cert.h"
+#include "modules/nickserv.h"
+
+typedef std::map<Anope::string, ChannelStatus> NSRecoverInfo;
+
+class NSRecoverSvsnick
+{
+ public:
+ Reference<User> from;
+ Anope::string to;
+};
+
+class NSRecoverRequestListener : public NickServ::IdentifyRequestListener
+{
+ CommandSource source;
+ Command *cmd;
+ Anope::string user;
+ Anope::string pass;
+
+ public:
+ NSRecoverRequestListener(CommandSource &src, Command *c, const Anope::string &nick, const Anope::string &p) : source(src), cmd(c), user(nick), pass(p) { }
+
+ void OnSuccess(NickServ::IdentifyRequest *) override
+ {
+ User *u = User::Find(user, true);
+ if (!source.GetUser() || !source.service)
+ return;
+
+ NickServ::Nick *na = NickServ::FindNick(user);
+ if (!na)
+ return;
+
+ Log(LOG_COMMAND, source, cmd) << "for " << na->GetNick();
+
+ /* Nick is being held by us, release it */
+ if (na->HasFieldS("HELD"))
+ {
+ NickServ::service->Release(na);
+ source.Reply(_("Service's hold on \002{0}\002 has been released."), na->GetNick());
+ }
+ else if (!u)
+ {
+ source.Reply(_("No one is using your nick, and services are not holding it."));
+ }
+ // If the user being recovered is identified for the account of the nick then the user is the
+ // same person that is executing the command, so kill them off (old GHOST command).
+ else if (u->Account() == na->GetAccount())
+ {
+ if (!source.GetAccount() && na->GetAccount()->HasFieldS("NS_SECURE"))
+ {
+ source.GetUser()->Login(u->Account());
+ Log(LOG_COMMAND, source, cmd) << "and was automatically identified to " << u->Account()->GetDisplay();
+ }
+
+ if (Config->GetModule("ns_recover")->Get<bool>("restoreonrecover"))
+ {
+ if (!u->chans.empty())
+ {
+ NSRecoverInfo i;
+ for (User::ChanUserList::iterator it = u->chans.begin(), it_end = u->chans.end(); it != it_end; ++it)
+ i[it->first->name] = it->second->status;
+ source.GetUser()->Extend<NSRecoverInfo>("recover", i);
+ }
+ }
+
+ u->SendMessage(*source.service, _("This nickname has been recovered by \002{0}\002. If you did not do this, then \002{0}\002 may have your password, and you should change it."),
+ source.GetNick());
+
+ Anope::string buf = source.command.upper() + " command used by " + source.GetNick();
+ u->Kill(*source.service, buf);
+
+ source.Reply(_("Ghost with your nick has been killed."));
+
+ if (IRCD->CanSVSNick)
+ IRCD->SendForceNickChange(source.GetUser(), user, Anope::CurTime);
+ }
+ /* User is not identified or not identified to the same account as the person using this command */
+ else
+ {
+ if (!source.GetAccount() && na->GetAccount()->HasFieldS("NS_SECURE"))
+ {
+ source.GetUser()->Login(na->GetAccount()); // Identify the user using the command if they arent identified
+ Log(LOG_COMMAND, source, cmd) << "and was automatically identified to " << na->GetNick() << " (" << na->GetAccount()->GetDisplay() << ")";
+ source.Reply(_("You have been logged in as \002{0}\002."), na->GetAccount()->GetDisplay());
+ }
+
+ u->SendMessage(*source.service, _("This nickname has been recovered by \002{0}\002."), source.GetNick());
+
+ if (IRCD->CanSVSNick)
+ {
+ NSRecoverSvsnick svs;
+
+ svs.from = source.GetUser();
+ svs.to = u->nick;
+
+ u->Extend<NSRecoverSvsnick>("svsnick", svs);
+ }
+
+ if (NickServ::service)
+ NickServ::service->Collide(u, na);
+
+ if (IRCD->CanSVSNick)
+ {
+ /* If we can svsnick then release our hold and svsnick the user using the command */
+ if (NickServ::service)
+ NickServ::service->Release(na);
+
+ source.Reply(_("You have regained control of \002{0}\002."), u->nick);
+ }
+ else
+ source.Reply(_("The user with your nick has been removed. Use this command again to release services's hold on your nick."));
+ }
+ }
+
+ void OnFail(NickServ::IdentifyRequest *) override
+ {
+ if (NickServ::FindNick(user) != NULL)
+ {
+ source.Reply(_("Access denied."));
+ if (!pass.empty())
+ {
+ Log(LOG_COMMAND, source, cmd) << "with an invalid password for " << user;
+ if (source.GetUser())
+ source.GetUser()->BadPassword();
+ }
+ }
+ else
+ source.Reply(_("\002{0}\002 isn't registered."), user);
+ }
+};
+
+class CommandNSRecover : public Command
+{
+ ServiceReference<CertService> certservice;
+
+ public:
+ CommandNSRecover(Module *creator) : Command(creator, "nickserv/recover", 1, 2)
+ {
+ this->SetDesc(_("Regains control of your nick"));
+ this->SetSyntax(_("\037nickname\037 [\037password\037]"));
+ this->AllowUnregistered(true);
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &nick = params[0];
+ const Anope::string &pass = params.size() > 1 ? params[1] : "";
+
+ User *user = User::Find(nick, true);
+
+ if (user && source.GetUser() == user)
+ {
+ source.Reply(_("You can't %s yourself!"), source.command.lower().c_str());
+ return;
+ }
+
+ NickServ::Nick *na = NickServ::FindNick(nick);
+
+ if (!na)
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), nick);
+ return;
+ }
+
+ if (na->GetAccount()->HasFieldS("NS_SUSPENDED"))
+ {
+ source.Reply(_("\002{0}\002 is suspended."), na->GetNick());
+ return;
+ }
+
+ bool ok = false;
+ if (source.GetAccount() == na->GetAccount())
+ ok = true;
+ else if (!na->GetAccount()->HasFieldS("NS_SECURE") && source.GetUser() && na->GetAccount()->IsOnAccess(source.GetUser()))
+ ok = true;
+
+ if (certservice && source.GetUser() && certservice->Matches(source.GetUser(), na->GetAccount()))
+ ok = true;
+
+ if (ok == false && !pass.empty())
+ {
+ NickServ::IdentifyRequest *req = NickServ::service->CreateIdentifyRequest(new NSRecoverRequestListener(source, this, na->GetNick(), pass), GetOwner(), na->GetNick(), pass);
+ EventManager::Get()->Dispatch(&Event::CheckAuthentication::OnCheckAuthentication, source.GetUser(), req);
+ req->Dispatch();
+ }
+ else
+ {
+ NSRecoverRequestListener req(source, this, na->GetNick(), pass);
+
+ if (ok)
+ req.OnSuccess(nullptr);
+ else
+ req.OnFail(nullptr);
+ }
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Recovers your nickname from another user or from services."
+ "If services are currently holding your nickname, the hold will be released."
+ " If another user is holding your nickname and is identified they will be killed."
+ " If they are not identified they will be forced off of the nickname."));
+ return true;
+ }
+};
+
+class NSRecover : public Module
+ , public EventHook<Event::UserNickChange>
+ , public EventHook<Event::JoinChannel>
+{
+ CommandNSRecover commandnsrecover;
+ ExtensibleItem<NSRecoverInfo> recover;
+ ExtensibleItem<NSRecoverSvsnick> svsnick;
+
+ public:
+ NSRecover(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , EventHook<Event::UserNickChange>(this)
+ , EventHook<Event::JoinChannel>(this)
+ , commandnsrecover(this)
+ , recover(this, "recover")
+ , svsnick(this, "svsnick")
+ {
+
+ if (Config->GetModule("nickserv")->Get<bool>("nonicknameownership"))
+ throw ModuleException(modname + " can not be used with options:nonicknameownership enabled");
+
+ }
+
+ void OnUserNickChange(User *u, const Anope::string &oldnick) override
+ {
+ if (Config->GetModule(this)->Get<bool>("restoreonrecover"))
+ {
+ NSRecoverInfo *ei = recover.Get(u);
+ ServiceBot *NickServ = Config->GetClient("NickServ");
+
+ if (ei != NULL && NickServ != NULL)
+ for (NSRecoverInfo::iterator it = ei->begin(), it_end = ei->end(); it != it_end;)
+ {
+ Channel *c = Channel::Find(it->first);
+ const Anope::string &cname = it->first;
+ ++it;
+
+ /* User might already be on the channel */
+ if (u->FindChannel(c))
+ this->OnJoinChannel(u, c);
+ else if (IRCD->CanSVSJoin)
+ IRCD->SendSVSJoin(NickServ, u, cname, "");
+ }
+ }
+
+ NSRecoverSvsnick *svs = svsnick.Get(u);
+ if (svs)
+ {
+ if (svs->from)
+ {
+ // svsnick from to to
+ IRCD->SendForceNickChange(svs->from, svs->to, Anope::CurTime);
+ }
+
+ svsnick.Unset(u);
+ }
+ }
+
+ void OnJoinChannel(User *u, Channel *c) override
+ {
+ if (Config->GetModule(this)->Get<bool>("restoreonrecover"))
+ {
+ NSRecoverInfo *ei = recover.Get(u);
+
+ if (ei != NULL)
+ {
+ NSRecoverInfo::iterator it = ei->find(c->name);
+ if (it != ei->end())
+ {
+ for (size_t i = 0; i < it->second.Modes().length(); ++i)
+ c->SetMode(c->ci->WhoSends(), ModeManager::FindChannelModeByChar(it->second.Modes()[i]), u->GetUID());
+
+ ei->erase(it);
+ if (ei->empty())
+ recover.Unset(u);
+ }
+ }
+ }
+ }
+};
+
+MODULE_INIT(NSRecover)
diff --git a/modules/nickserv/register.cpp b/modules/nickserv/register.cpp
new file mode 100644
index 000000000..02da01f5d
--- /dev/null
+++ b/modules/nickserv/register.cpp
@@ -0,0 +1,445 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/nickserv.h"
+
+static bool SendRegmail(User *u, NickServ::Nick *na, ServiceBot *bi);
+
+class CommandNSConfirm : public Command
+{
+ public:
+ CommandNSConfirm(Module *creator) : Command(creator, "nickserv/confirm", 1, 2)
+ {
+ this->SetDesc(_("Confirm a passcode"));
+ this->SetSyntax(_("\037passcode\037"));
+ this->AllowUnregistered(true);
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &passcode = params[0];
+
+ if (source.nc && !source.nc->HasFieldS("UNCONFIRMED") && source.HasPriv("nickserv/confirm"))
+ {
+ NickServ::Nick *na = NickServ::FindNick(passcode);
+ if (na == NULL)
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), passcode);
+ return;
+ }
+
+ if (na->GetAccount()->HasFieldS("UNCONFIRMED") == false)
+ {
+ source.Reply(_("\002{0}\002 is already confirmed."), na->GetNick());
+ return;
+ }
+
+ na->GetAccount()->UnsetS<bool>("UNCONFIRMED");
+ EventManager::Get()->Dispatch(&NickServ::Event::NickConfirm::OnNickConfirm, source.GetUser(), na->GetAccount());
+ Log(LOG_ADMIN, source, this) << "to confirm nick " << na->GetNick() << " (" << na->GetAccount()->GetDisplay() << ")";
+ source.Reply(_("\002{0}\002 has been confirmed."), na->GetNick());
+
+ /* Login the users online already */
+ for (User *u : na->GetAccount()->users)
+ {
+ IRCD->SendLogin(u, na);
+
+ NickServ::Nick *u_na = NickServ::FindNick(u->nick);
+
+ /* Set +r if they're on a nick in the group */
+ if (!Config->GetModule("nickserv")->Get<bool>("nonicknameownership") && u_na && u_na->GetAccount() == na->GetAccount())
+ u->SetMode(source.service, "REGISTERED");
+ }
+ }
+ else if (source.nc)
+ {
+ Anope::string *code = source.nc->GetExt<Anope::string>("passcode");
+ if (code == nullptr || *code != passcode)
+ {
+ source.Reply(_("Invalid passcode."));
+ return;
+ }
+
+ NickServ::Account *nc = source.nc;
+ nc->Shrink<Anope::string>("passcode");
+ Log(LOG_COMMAND, source, this) << "to confirm their email";
+ source.Reply(_("Your email address of \002{0}\002 has been confirmed."), source.nc->GetEmail());
+ nc->UnsetS<bool>("UNCONFIRMED");
+
+ EventManager::Get()->Dispatch(&NickServ::Event::NickConfirm::OnNickConfirm, source.GetUser(), nc);
+
+ if (source.GetUser())
+ {
+ NickServ::Nick *na = NickServ::FindNick(source.GetNick());
+ if (na)
+ {
+ IRCD->SendLogin(source.GetUser(), na);
+ if (!Config->GetModule("nickserv")->Get<bool>("nonicknameownership") && na->GetAccount() == source.GetAccount() && !na->GetAccount()->HasFieldS("UNCONFIRMED"))
+ source.GetUser()->SetMode(source.service, "REGISTERED");
+ }
+ }
+ }
+ else
+ source.Reply(_("Invalid passcode."));
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("This command is used by several commands as a way to confirm changes made to your account.\n"
+ "\n"
+ "This is most commonly used to confirm your email address once you register or change it.\n"
+ "\n"
+ "This is also used after when resetting your password to force identify you to your account so you may change your password."));
+ if (source.HasPriv("nickserv/confirm"))
+ source.Reply(_("Additionally, Services Operators with the \037nickserv/confirm\037 permission can\n"
+ "replace \037passcode\037 with a users nick to force validate them."));
+ return true;
+ }
+};
+
+class CommandNSRegister : public Command
+{
+ public:
+ CommandNSRegister(Module *creator) : Command(creator, "nickserv/register", 1, 2)
+ {
+ this->SetDesc(_("Register a nickname"));
+ if (Config->GetModule("nickserv")->Get<bool>("forceemail", "yes"))
+ this->SetSyntax(_("\037password\037 \037email\037"));
+ else
+ this->SetSyntax(_("\037password\037 \037[email]\037"));
+ this->AllowUnregistered(true);
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ User *u = source.GetUser();
+ Anope::string u_nick = source.GetNick();
+ size_t nicklen = u_nick.length();
+ Anope::string pass = params[0];
+ Anope::string email = params.size() > 1 ? params[1] : "";
+ const Anope::string &nsregister = Config->GetModule(this->GetOwner())->Get<Anope::string>("registration");
+
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Sorry, nickname registration is temporarily disabled."));
+ return;
+ }
+
+ if (nsregister.equals_ci("disable"))
+ {
+ source.Reply(_("Registration is currently disabled."));
+ return;
+ }
+
+ time_t nickregdelay = Config->GetModule(this->GetOwner())->Get<time_t>("nickregdelay");
+ time_t reg_delay = Config->GetModule("nickserv")->Get<time_t>("regdelay");
+ if (u && !u->HasMode("OPER") && nickregdelay && Anope::CurTime - u->timestamp < nickregdelay)
+ {
+ source.Reply(_("You must have been using this nickname for at least {0} seconds to register."), nickregdelay);
+ return;
+ }
+
+ /* Prevent "Guest" nicks from being registered. -TheShadow */
+
+ /* Guest nick can now have a series of between 1 and 7 digits.
+ * --lara
+ */
+ const Anope::string &guestnick = Config->GetModule("nickserv")->Get<Anope::string>("guestnickprefix", "Guest");
+ if (nicklen <= guestnick.length() + 7 && nicklen >= guestnick.length() + 1 && !u_nick.find_ci(guestnick) && u_nick.substr(guestnick.length()).find_first_not_of("1234567890") == Anope::string::npos)
+ {
+ source.Reply(_("\002{0}\002 may not be registered."), u_nick);
+ return;
+ }
+
+ if (!IRCD->IsNickValid(u_nick))
+ {
+ source.Reply(_("\002{0}\002 may not be registered."), u_nick);
+ return;
+ }
+
+ if (ServiceBot::Find(u_nick, true))
+ {
+ source.Reply(_("\002{0}\002 may not be registered."), u_nick);
+ return;
+ }
+
+ if (Config->GetModule("nickserv")->Get<bool>("restrictopernicks"))
+ for (Oper *o : Serialize::GetObjects<Oper *>())
+ {
+ if (!source.IsOper() && u_nick.find_ci(o->GetName()) != Anope::string::npos)
+ {
+ source.Reply(_("\002{0}\002 may not be registered because it is too similar to an operator nick."), u_nick);
+ return;
+ }
+ }
+
+ unsigned int passlen = Config->GetModule("nickserv")->Get<unsigned>("passlen", "32");
+
+ if (Config->GetModule("nickserv")->Get<bool>("forceemail", "yes") && email.empty())
+ {
+ this->OnSyntaxError(source, "");
+ return;
+ }
+
+ if (u && Anope::CurTime < u->lastnickreg + reg_delay)
+ {
+ source.Reply(_("Please wait \002{0}\002 seconds before using the {1} command again."), (u->lastnickreg + reg_delay) - Anope::CurTime, source.command);
+ return;
+ }
+
+ if (NickServ::FindNick(u_nick) != NULL)
+ {
+ source.Reply(_("\002{0}\002 is already registered."), u_nick);
+ return;
+ }
+
+ if (pass.equals_ci(u_nick) || (Config->GetBlock("options")->Get<bool>("strictpasswords") && pass.length() < 5))
+ {
+ source.Reply(_("Please try again with a more obscure password. Passwords should be at least five characters long, should not be something easily guessed"
+ " (e.g. your real name or your nickname), and cannot contain the space or tab characters."));
+ return;
+ }
+
+ if (pass.length() > Config->GetModule("nickserv")->Get<unsigned>("passlen", "32"))
+ {
+ source.Reply(_("Your password is too long, it can not contain more than \002{0}\002 characters."), Config->GetModule("nickserv")->Get<unsigned>("passlen", "32"));
+ return;
+ }
+
+ if (!email.empty() && !Mail::Validate(email))
+ {
+ source.Reply(_("\002{0}\002 is not a valid e-mail address."), email);
+ return;
+ }
+
+ NickServ::Account *nc = Serialize::New<NickServ::Account *>();
+ nc->SetDisplay(u_nick);
+
+ NickServ::Nick *na = Serialize::New<NickServ::Nick *>();
+ na->SetNick(u_nick);
+ na->SetAccount(nc);
+ na->SetTimeRegistered(Anope::CurTime);
+ na->SetLastSeen(Anope::CurTime);
+
+ Anope::string epass;
+ Anope::Encrypt(pass, epass);
+ nc->SetPassword(epass);
+ if (!email.empty())
+ nc->SetEmail(email);
+
+ if (u)
+ {
+ na->SetLastUsermask(u->GetIdent() + "@" + u->GetDisplayedHost());
+ na->SetLastRealname(u->realname);
+ }
+ else
+ na->SetLastRealname(source.GetNick());
+
+ Log(LOG_COMMAND, source, this) << "to register " << na->GetNick() << " (email: " << (!na->GetAccount()->GetEmail().empty() ? na->GetAccount()->GetEmail() : "none") << ")";
+
+ source.Reply(_("\002{0}\002 has been registered."), u_nick);
+
+ if (nsregister.equals_ci("admin"))
+ {
+ nc->SetS<bool>("UNCONFIRMED", true);
+ // User::Identify() called below will notify the user that their registration is pending
+ }
+ else if (nsregister.equals_ci("mail"))
+ {
+ if (!email.empty())
+ {
+ nc->SetS<bool>("UNCONFIRMED", true);
+ SendRegmail(NULL, na, source.service);
+ }
+ }
+
+ EventManager::Get()->Dispatch(&NickServ::Event::NickRegister::OnNickRegister, source.GetUser(), na, pass);
+
+ if (u)
+ {
+ u->Identify(na);
+ u->lastnickreg = Anope::CurTime;
+ }
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Registers your nickname. Once your nickname is registered, you will be able to use most features of services, including owning and managing channels."
+ "Make sure you remember the password - you'll need it to identify yourself later. Your email address will only be used if you forget your password."));
+
+ if (!Config->GetModule("nickserv")->Get<bool>("forceemail", "yes"))
+ {
+ source.Reply(" ");
+ source.Reply(_("The \037email\037 parameter is optional and will set the email\n"
+ "for your nick immediately.\n"
+ "Your privacy is respected; this e-mail won't be given to\n"
+ "any third-party person. You may also wish to \002SET HIDE\002 it\n"
+ "after registering if it isn't the default setting already."));
+ }
+
+ if (!Config->GetModule("nickserv")->Get<bool>("nonicknameownership"))
+ {
+ source.Reply(" ");
+ source.Reply(_("This command also creates a new group for your nickname, which will allow you to group other nicknames later, which share the same configuration, the same set of memos and the same channel privileges."));
+ }
+ return true;
+ }
+};
+
+class CommandNSResend : public Command
+{
+ public:
+ CommandNSResend(Module *creator) : Command(creator, "nickserv/resend", 0, 0)
+ {
+ this->SetDesc(_("Resend registration confirmation email"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ if (!Config->GetModule(this->GetOwner())->Get<Anope::string>("registration").equals_ci("mail"))
+ {
+ source.Reply(_("Access denied."));
+ return;
+ }
+
+ NickServ::Nick *na = NickServ::FindNick(source.GetNick());
+
+ if (na == NULL)
+ {
+ source.Reply(_("Your nickname isn't registered."));
+ return;
+ }
+
+ if (na->GetAccount() != source.GetAccount() || !source.nc->HasFieldS("UNCONFIRMED"))
+ {
+ source.Reply(_("Your account is already confirmed."));
+ return;
+ }
+
+ if (Anope::CurTime < source.nc->lastmail + Config->GetModule(this->GetOwner())->Get<time_t>("resenddelay"))
+ {
+ source.Reply(_("Cannot send mail now; please retry a little later."));
+ return;
+ }
+
+ if (!SendRegmail(source.GetUser(), na, source.service))
+ {
+ Log(this->GetOwner()) << "Unable to resend registration verification code for " << source.GetNick();
+ return;
+ }
+
+ na->GetAccount()->lastmail = Anope::CurTime;
+ source.Reply(_("Your passcode has been re-sent to \002{0}\002."), na->GetAccount()->GetEmail());
+ Log(LOG_COMMAND, source, this) << "to resend registration verification code";
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ if (!Config->GetModule(this->GetOwner())->Get<Anope::string>("registration").equals_ci("mail"))
+ return false;
+
+ source.Reply(_("This command will resend you the registration confirmation email."));
+ return true;
+ }
+
+ void OnServHelp(CommandSource &source) override
+ {
+ if (Config->GetModule(this->GetOwner())->Get<Anope::string>("registration").equals_ci("mail"))
+ Command::OnServHelp(source);
+ }
+};
+
+class NSRegister : public Module
+ , public EventHook<Event::NickIdentify>
+ , public EventHook<NickServ::Event::PreNickExpire>
+{
+ CommandNSRegister commandnsregister;
+ CommandNSConfirm commandnsconfirm;
+ CommandNSResend commandnsrsend;
+
+ Serialize::Field<NickServ::Account, bool> unconfirmed;
+ Serialize::Field<NickServ::Account, Anope::string> passcode;
+
+ public:
+ NSRegister(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , EventHook<Event::NickIdentify>(this)
+ , EventHook<NickServ::Event::PreNickExpire>(this)
+ , commandnsregister(this)
+ , commandnsconfirm(this)
+ , commandnsrsend(this)
+ , unconfirmed(this, "UNCONFIRMED")
+ , passcode(this, "passcode")
+ {
+ if (Config->GetModule(this)->Get<Anope::string>("registration").equals_ci("disable"))
+ throw ModuleException("Module " + Module::name + " will not load with registration disabled.");
+ }
+
+ void OnNickIdentify(User *u) override
+ {
+ ServiceBot *NickServ;
+ if (unconfirmed.HasExt(u->Account()) && (NickServ = Config->GetClient("NickServ")))
+ {
+ const Anope::string &nsregister = Config->GetModule(this)->Get<Anope::string>("registration");
+ if (nsregister.equals_ci("admin"))
+ u->SendMessage(NickServ, _("All new accounts must be validated by an administrator. Please wait for your registration to be confirmed."));
+ else
+ u->SendMessage(NickServ, _("Your email address is not confirmed. To confirm it, follow the instructions that were emailed to you."));
+ NickServ::Nick *this_na = NickServ::FindNick(u->Account()->GetDisplay());
+ time_t time_registered = Anope::CurTime - this_na->GetTimeRegistered();
+ time_t unconfirmed_expire = Config->GetModule(this)->Get<time_t>("unconfirmedexpire", "1d");
+ if (unconfirmed_expire > time_registered)
+ u->SendMessage(NickServ, _("Your account will expire, if not confirmed, in %s."), Anope::Duration(unconfirmed_expire - time_registered, u->Account()).c_str());
+ }
+ }
+
+ void OnPreNickExpire(NickServ::Nick *na, bool &expire) override
+ {
+ if (unconfirmed.HasExt(na->GetAccount()))
+ {
+ time_t unconfirmed_expire = Config->GetModule(this)->Get<time_t>("unconfirmedexpire", "1d");
+ if (unconfirmed_expire && Anope::CurTime - na->GetTimeRegistered() >= unconfirmed_expire)
+ expire = true;
+ }
+ }
+};
+
+static bool SendRegmail(User *u, NickServ::Nick *na, ServiceBot *bi)
+{
+ NickServ::Account *nc = na->GetAccount();
+
+ Anope::string *code = na->GetAccount()->GetExt<Anope::string>("passcode");
+ if (code == NULL)
+ code = na->GetAccount()->Extend<Anope::string>("passcode", Anope::Random(9));
+
+ Anope::string subject = Language::Translate(na->GetAccount(), Config->GetBlock("mail")->Get<Anope::string>("registration_subject").c_str()),
+ message = Language::Translate(na->GetAccount(), Config->GetBlock("mail")->Get<Anope::string>("registration_message").c_str());
+
+ subject = subject.replace_all_cs("%n", na->GetNick());
+ subject = subject.replace_all_cs("%N", Config->GetBlock("networkinfo")->Get<Anope::string>("networkname"));
+ subject = subject.replace_all_cs("%c", *code);
+
+ message = message.replace_all_cs("%n", na->GetNick());
+ message = message.replace_all_cs("%N", Config->GetBlock("networkinfo")->Get<Anope::string>("networkname"));
+ message = message.replace_all_cs("%c", *code);
+
+ return Mail::Send(u, nc, bi, subject, message);
+}
+
+MODULE_INIT(NSRegister)
diff --git a/modules/nickserv/resetpass.cpp b/modules/nickserv/resetpass.cpp
new file mode 100644
index 000000000..2a70a6ebd
--- /dev/null
+++ b/modules/nickserv/resetpass.cpp
@@ -0,0 +1,150 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2009-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+
+static bool SendResetEmail(User *u, NickServ::Nick *na, ServiceBot *bi);
+
+class CommandNSResetPass : public Command
+{
+ public:
+ CommandNSResetPass(Module *creator) : Command(creator, "nickserv/resetpass", 2, 2)
+ {
+ this->SetDesc(_("Helps you reset lost passwords"));
+ this->SetSyntax(_("\037account\037 \037email\037"));
+ this->AllowUnregistered(true);
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ NickServ::Nick *na = NickServ::FindNick(params[0]);
+
+ if (!na)
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), params[0]);
+ return;
+ }
+
+ if (!na->GetAccount()->GetEmail().equals_ci(params[1]))
+ {
+ source.Reply(_("Incorrect email address."));
+ return;
+ }
+
+ if (SendResetEmail(source.GetUser(), na, source.service))
+ {
+ Log(LOG_COMMAND, source, this) << "for " << na->GetNick() << " (group: " << na->GetAccount()->GetDisplay() << ")";
+ source.Reply(_("Password reset email for \002{0}\002 has been sent."), na->GetNick());
+ }
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Sends a passcode to the email address of \037account\037 with instructions on how to reset their password. \037email\037 must be the email address associated to \037account\037."));
+ return true;
+ }
+};
+
+struct ResetInfo
+{
+ Anope::string code;
+ time_t time;
+};
+
+class NSResetPass : public Module
+ , public EventHook<Event::PreCommand>
+{
+ CommandNSResetPass commandnsresetpass;
+ ExtensibleItem<ResetInfo> reset;
+
+ public:
+ NSResetPass(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , EventHook<Event::PreCommand>(this)
+ , commandnsresetpass(this), reset(this, "reset")
+ {
+ if (!Config->GetBlock("mail")->Get<bool>("usemail"))
+ throw ModuleException("Not using mail.");
+ }
+
+ EventReturn OnPreCommand(CommandSource &source, Command *command, std::vector<Anope::string> &params) override
+ {
+ if (command->GetName() == "nickserv/confirm" && params.size() > 1)
+ {
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return EVENT_STOP;
+ }
+
+ NickServ::Nick *na = NickServ::FindNick(params[0]);
+
+ ResetInfo *ri = na ? reset.Get(na->GetAccount()) : NULL;
+ if (na && ri)
+ {
+ NickServ::Account *nc = na->GetAccount();
+ const Anope::string &passcode = params[1];
+ if (ri->time < Anope::CurTime - 3600)
+ {
+ reset.Unset(nc);
+ source.Reply(_("Your password reset request has expired."));
+ }
+ else if (passcode.equals_cs(ri->code))
+ {
+ reset.Unset(nc);
+ nc->UnsetS<bool>("UNCONFIRMED");
+
+ Log(LOG_COMMAND, source, &commandnsresetpass) << "confirmed RESETPASS to forcefully identify as " << na->GetNick();
+
+ if (source.GetUser())
+ {
+ source.GetUser()->Identify(na);
+ source.Reply(_("You are now identified for \002{0}\002. Change your password now."), na->GetAccount()->GetDisplay());
+ }
+ }
+ else
+ return EVENT_CONTINUE;
+
+ return EVENT_STOP;
+ }
+ }
+
+ return EVENT_CONTINUE;
+ }
+};
+
+static bool SendResetEmail(User *u, NickServ::Nick *na, ServiceBot *bi)
+{
+ Anope::string subject = Language::Translate(na->GetAccount(), Config->GetBlock("mail")->Get<Anope::string>("reset_subject").c_str()),
+ message = Language::Translate(na->GetAccount(), Config->GetBlock("mail")->Get<Anope::string>("reset_message").c_str()),
+ passcode = Anope::Random(20);
+
+ subject = subject.replace_all_cs("%n", na->GetNick());
+ subject = subject.replace_all_cs("%N", Config->GetBlock("networkinfo")->Get<Anope::string>("networkname"));
+ subject = subject.replace_all_cs("%c", passcode);
+
+ message = message.replace_all_cs("%n", na->GetNick());
+ message = message.replace_all_cs("%N", Config->GetBlock("networkinfo")->Get<Anope::string>("networkname"));
+ message = message.replace_all_cs("%c", passcode);
+
+ na->GetAccount()->Extend<ResetInfo>("reset", ResetInfo{passcode, Anope::CurTime});
+
+ return Mail::Send(u, na->GetAccount(), bi, subject, message);
+}
+
+MODULE_INIT(NSResetPass)
diff --git a/modules/nickserv/set.cpp b/modules/nickserv/set.cpp
new file mode 100644
index 000000000..bb6557f4b
--- /dev/null
+++ b/modules/nickserv/set.cpp
@@ -0,0 +1,1261 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/nickserv/info.h"
+#include "modules/nickserv/set.h"
+#include "modules/nickserv.h"
+
+class CommandNSSet : public Command
+{
+ public:
+ CommandNSSet(Module *creator) : Command(creator, "nickserv/set", 1, 3)
+ {
+ this->SetDesc(_("Set options, including kill protection"));
+ this->SetSyntax(_("\037option\037 \037parameters\037"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ this->OnSyntaxError(source, "");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Sets various options on your account\n"
+ "\n"
+ "Available options:"));
+
+ Anope::string this_name = source.command;
+ bool hide_privileged_commands = Config->GetBlock("options")->Get<bool>("hideprivilegedcommands"),
+ hide_registered_commands = Config->GetBlock("options")->Get<bool>("hideregisteredcommands");
+ for (CommandInfo::map::const_iterator it = source.service->commands.begin(), it_end = source.service->commands.end(); it != it_end; ++it)
+ {
+ const Anope::string &c_name = it->first;
+ const CommandInfo &info = it->second;
+
+ if (c_name.find_ci(this_name + " ") == 0)
+ {
+ ServiceReference<Command> c(info.name);
+ // XXX dup
+ if (!c)
+ continue;
+ else if (hide_registered_commands && !c->AllowUnregistered() && !source.GetAccount())
+ continue;
+ else if (hide_privileged_commands && !info.permission.empty() && !source.HasCommand(info.permission))
+ continue;
+
+ source.command = c_name;
+ c->OnServHelp(source);
+ }
+ }
+
+ CommandInfo *help = source.service->FindCommand("generic/help");
+ if (help)
+ source.Reply(_("Type \002{0}{1} {2} {3} \037option\037\002 for more information on a particular option."),
+ Config->StrictPrivmsg, source.service->nick, help->cname, this_name);
+
+ return true;
+ }
+};
+
+class CommandNSSASet : public Command
+{
+ public:
+ CommandNSSASet(Module *creator) : Command(creator, "nickserv/saset", 2, 4)
+ {
+ this->SetDesc(_("Set SET-options on another nickname"));
+ this->SetSyntax(_("\037option\037 \037nickname\037 \037parameters\037"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ this->OnSyntaxError(source, "");
+ return;
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Sets various options on other users accounts\n"
+ "\n"
+ "Available options:"));
+
+ Anope::string this_name = source.command;
+ for (CommandInfo::map::const_iterator it = source.service->commands.begin(), it_end = source.service->commands.end(); it != it_end; ++it)
+ {
+ const Anope::string &c_name = it->first;
+ const CommandInfo &info = it->second;
+
+ if (c_name.find_ci(this_name + " ") == 0)
+ {
+ ServiceReference<Command> command(info.name);
+ if (command)
+ {
+ source.command = c_name;
+ command->OnServHelp(source);
+ }
+ }
+ }
+
+ CommandInfo *help = source.service->FindCommand("generic/help");
+ if (help)
+ source.Reply(_("Type \002{0}{1} {2} {3} \037option\037\002 for more information on a particular option."),
+ Config->StrictPrivmsg, source.service->nick, help->cname, this_name);
+
+ return true;
+ }
+};
+
+class CommandNSSetPassword : public Command
+{
+ public:
+ CommandNSSetPassword(Module *creator) : Command(creator, "nickserv/set/password", 1)
+ {
+ this->SetDesc(_("Changes your password"));
+ this->SetSyntax(_("\037new-password\037"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &param = params[0];
+ unsigned len = param.length();
+
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ if (source.GetNick().equals_ci(param) || (Config->GetBlock("options")->Get<bool>("strictpasswords") && len < 5))
+ {
+ source.Reply(_("Please try again with a more obscure password. Passwords should be at least five characters long, should not be something easily guessed (e.g. your real name or your nick), and cannot contain the space or tab characters."));
+ return;
+ }
+
+ if (len > Config->GetModule("nickserv")->Get<unsigned>("passlen", "32"))
+ {
+ source.Reply(_("Your password is too long, it can not contain more than \002{0}\002 characters."), Config->GetModule("nickserv")->Get<unsigned>("passlen", "32"));
+ return;
+ }
+
+ Log(LOG_COMMAND, source, this) << "to change their password";
+
+ Anope::string tmp_pass;
+ Anope::Encrypt(param, tmp_pass);
+ source.nc->SetPassword(tmp_pass);
+
+ source.Reply(_("Password for \002{0}\002 changed."), source.nc->GetDisplay());
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
+ {
+ source.Reply(_("Changes your password to \037new-password\037."));
+ return true;
+ }
+};
+
+class CommandNSSASetPassword : public Command
+{
+ public:
+ CommandNSSASetPassword(Module *creator) : Command(creator, "nickserv/saset/password", 2, 2)
+ {
+ this->SetDesc(_("Changes the password of another user"));
+ this->SetSyntax(_("\037account\037 \037new-password\037"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ NickServ::Nick *setter_na = NickServ::FindNick(params[0]);
+ if (setter_na == NULL)
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), params[0]);
+ return;
+ }
+ NickServ::Account *nc = setter_na->GetAccount();
+
+ size_t len = params[1].length();
+
+ if (Config->GetModule("nickserv")->Get<bool>("secureadmins", "yes") && source.nc != nc && nc->IsServicesOper())
+ {
+ source.Reply(_("You may not change the password of other Services Operators."));
+ return;
+ }
+
+ if (nc->GetDisplay().equals_ci(params[1]) || (Config->GetBlock("options")->Get<bool>("strictpasswords") && len < 5))
+ {
+ source.Reply(_("Please try again with a more obscure password. Passwords should be at least five characters long, should not be something easily guessed (e.g. your real name or your nick), and cannot contain the space or tab characters."));
+ return;
+ }
+
+ if (len > Config->GetModule("nickserv")->Get<unsigned>("passlen", "32"))
+ {
+ source.Reply(_("Your password is too long, it can not contain more than \002{0}\002 characters."), Config->GetModule("nickserv")->Get<unsigned>("passlen", "32"));
+ return;
+ }
+
+ Log(LOG_ADMIN, source, this) << "to change the password of " << nc->GetDisplay();
+
+ Anope::string tmp_pass;
+ Anope::Encrypt(params[1], tmp_pass);
+ nc->SetPassword(tmp_pass);
+ source.Reply(_("Password for \002{0}\002 changed."), nc->GetDisplay());
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
+ {
+ source.Reply(_("Changes the password of \037account\037 to \037new-password\037."));
+ return true;
+ }
+};
+
+class CommandNSSetAutoOp : public Command
+{
+ public:
+ CommandNSSetAutoOp(Module *creator, const Anope::string &sname = "nickserv/set/autoop", size_t min = 1) : Command(creator, sname, min, min + 1)
+ {
+ this->SetDesc(_("Sets whether services should set channel status modes on you automatically."));
+ this->SetSyntax("{ON | OFF}");
+ }
+
+ void Run(CommandSource &source, const Anope::string &user, const Anope::string &param)
+ {
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ NickServ::Nick *na = NickServ::FindNick(user);
+ if (na == NULL)
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), user);
+ return;
+ }
+ NickServ::Account *nc = na->GetAccount();
+
+ EventReturn MOD_RESULT;
+ MOD_RESULT = EventManager::Get()->Dispatch(&Event::SetNickOption::OnSetNickOption, source, this, nc, param);
+ if (MOD_RESULT == EVENT_STOP)
+ return;
+
+ if (param.equals_ci("ON"))
+ {
+ Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to enable autoop for " << na->GetAccount()->GetDisplay();
+ nc->SetS<bool>("AUTOOP", true);
+ source.Reply(_("Services will from now on set status modes on \002{0}\002 in channels."), nc->GetDisplay());
+ }
+ else if (param.equals_ci("OFF"))
+ {
+ Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to disable autoop for " << na->GetAccount()->GetDisplay();
+ nc->UnsetS<bool>("AUTOOP");
+ source.Reply(_("Services will no longer set status modes on \002{0}\002 in channels."), nc->GetDisplay());
+ }
+ else
+ this->OnSyntaxError(source, "AUTOOP");
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ this->Run(source, source.nc->GetDisplay(), params[0]);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
+ {
+ source.Reply(_("Sets whether you will be given your channel status modes automatically when you join a channel."
+ " Note that depending on channel settings some modes may not get set automatically."));
+ return true;
+ }
+};
+
+class CommandNSSASetAutoOp : public CommandNSSetAutoOp
+{
+ public:
+ CommandNSSASetAutoOp(Module *creator) : CommandNSSetAutoOp(creator, "nickserv/saset/autoop", 2)
+ {
+ this->ClearSyntax();
+ this->SetSyntax(_("\037nickname\037 {ON | OFF}"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ this->Run(source, params[0], params[1]);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
+ {
+ source.Reply(_("Sets whether the given nickname will be given their status modes automatically when they join a channel."
+ " Note that depending on channel settings some modes may not get set automatically."));
+ return true;
+ }
+};
+
+class CommandNSSetDisplay : public Command
+{
+ public:
+ CommandNSSetDisplay(Module *creator, const Anope::string &sname = "nickserv/set/display", size_t min = 1) : Command(creator, sname, min, min + 1)
+ {
+ this->SetDesc(_("Set the display of your group in Services"));
+ this->SetSyntax(_("\037new-display\037"));
+ }
+
+ void Run(CommandSource &source, const Anope::string &user, const Anope::string &param)
+ {
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ NickServ::Nick *user_na = NickServ::FindNick(user), *na = NickServ::FindNick(param);
+
+ if (Config->GetModule("nickserv")->Get<bool>("nonicknameownership"))
+ {
+ source.Reply(_("This command may not be used on this network because nickname ownership is disabled."));
+ return;
+ }
+
+ if (user_na == NULL)
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), user);
+ return;
+ }
+
+ if (!na || na->GetAccount() != user_na->GetAccount())
+ {
+ source.Reply(_("The new display must be a nickname of the nickname group \002{0}\02."), user_na->GetAccount()->GetDisplay());
+ return;
+ }
+
+ EventReturn MOD_RESULT;
+ MOD_RESULT = EventManager::Get()->Dispatch(&Event::SetNickOption::OnSetNickOption, source, this, user_na->GetAccount(), param);
+ if (MOD_RESULT == EVENT_STOP)
+ return;
+
+ Log(user_na->GetAccount() == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to change the display of " << user_na->GetAccount()->GetDisplay() << " to " << na->GetNick();
+
+ user_na->GetAccount()->SetDisplay(na);
+
+ for (User *u : user_na->GetAccount()->users)
+ {
+ IRCD->SendLogin(u, user_na);
+ }
+
+ source.Reply(_("The new display is now \002{0}\002."), user_na->GetAccount()->GetDisplay());
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ this->Run(source, source.nc->GetDisplay(), params[0]);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
+ {
+ source.Reply(_("Changes the display used to refer to your nickname group in services. The new display nickname must be a nickname of your group."));
+ return true;
+ }
+};
+
+class CommandNSSASetDisplay : public CommandNSSetDisplay
+{
+ public:
+ CommandNSSASetDisplay(Module *creator) : CommandNSSetDisplay(creator, "nickserv/saset/display", 2)
+ {
+ this->ClearSyntax();
+ this->SetSyntax(_("\037account\037 \037new-display\037"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ this->Run(source, params[0], params[1]);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
+ {
+ source.Reply(_("Changes the display used to refer to the nickname group \037account\037 in services. The new display nickname must be a nickname in the group of \037account\037."));
+ return true;
+ }
+};
+
+class CommandNSSetEmail : public Command
+{
+ static bool SendConfirmMail(User *u, ServiceBot *bi, const Anope::string &new_email)
+ {
+ Anope::string code = Anope::Random(9);
+
+ u->Account()->Extend<std::pair<Anope::string, Anope::string> >("ns_set_email", std::make_pair(new_email, code));
+
+ Anope::string subject = Config->GetBlock("mail")->Get<Anope::string>("emailchange_subject"),
+ message = Config->GetBlock("mail")->Get<Anope::string>("emailchange_message");
+
+ subject = subject.replace_all_cs("%e", u->Account()->GetEmail());
+ subject = subject.replace_all_cs("%E", new_email);
+ subject = subject.replace_all_cs("%N", Config->GetBlock("networkinfo")->Get<Anope::string>("networkname"));
+ subject = subject.replace_all_cs("%c", code);
+
+ message = message.replace_all_cs("%e", u->Account()->GetEmail());
+ message = message.replace_all_cs("%E", new_email);
+ message = message.replace_all_cs("%N", Config->GetBlock("networkinfo")->Get<Anope::string>("networkname"));
+ message = message.replace_all_cs("%c", code);
+
+ Anope::string old = u->Account()->GetEmail();
+ u->Account()->SetEmail(new_email);
+ bool b = Mail::Send(u, u->Account(), bi, subject, message);
+ u->Account()->SetEmail(old);
+ return b;
+ }
+
+ public:
+ CommandNSSetEmail(Module *creator, const Anope::string &cname = "nickserv/set/email", size_t min = 0) : Command(creator, cname, min, min + 1)
+ {
+ this->SetDesc(_("Associate an E-mail address with your nickname"));
+ this->SetSyntax(_("\037address\037"));
+ }
+
+ void Run(CommandSource &source, const Anope::string &user, const Anope::string &param)
+ {
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ NickServ::Nick *na = NickServ::FindNick(user);
+ if (!na)
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), user);
+ return;
+ }
+ NickServ::Account *nc = na->GetAccount();
+
+ if (nc->HasFieldS("UNCONFIRMED"))
+ {
+ source.Reply(_("You may not change the email of an unconfirmed account."));
+ return;
+ }
+
+ if (param.empty() && Config->GetModule("nickserv")->Get<bool>("forceemail", "yes"))
+ {
+ source.Reply(_("You cannot unset the e-mail on this network."));
+ return;
+ }
+
+ if (Config->GetModule("nickserv")->Get<bool>("secureadmins", "yes") && source.nc != nc && nc->IsServicesOper())
+ {
+ source.Reply(_("You may not change the e-mail of other Services Operators."));
+ return;
+ }
+
+ if (!param.empty() && !Mail::Validate(param))
+ {
+ source.Reply(_("\002{0}\002 is not a valid e-mail address."), param);
+ return;
+ }
+
+ EventReturn MOD_RESULT;
+ MOD_RESULT = EventManager::Get()->Dispatch(&Event::SetNickOption::OnSetNickOption, source, this, nc, param);
+ if (MOD_RESULT == EVENT_STOP)
+ return;
+
+ if (param.empty())
+ {
+ Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to unset the email of " << nc->GetDisplay();
+ nc->SetEmail("");
+ source.Reply(_("E-mail address for \002{0}\002 unset."), nc->GetDisplay());
+ }
+ else if (Config->GetModule("nickserv")->Get<bool>("confirmemailchanges") && !source.IsServicesOper())
+ {
+ if (SendConfirmMail(source.GetUser(), source.service, param))
+ source.Reply(_("A confirmation e-mail has been sent to \002{0}\002. Follow the instructions in it to change your e-mail address."), param);
+ }
+ else
+ {
+ Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to change the email of " << nc->GetDisplay() << " to " << param;
+ nc->SetEmail(param);
+ source.Reply(_("E-mail address for \002{0}\002 changed to \002{1}\002."), nc->GetDisplay(), param);
+ }
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ this->Run(source, source.nc->GetDisplay(), params.size() ? params[0] : "");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
+ {
+ source.Reply(_("Changes your email address to \037address\037."));
+ return true;
+ }
+};
+
+class CommandNSSASetEmail : public CommandNSSetEmail
+{
+ public:
+ CommandNSSASetEmail(Module *creator) : CommandNSSetEmail(creator, "nickserv/saset/email", 2)
+ {
+ this->ClearSyntax();
+ this->SetSyntax(_("\037account\037 \037address\037"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ this->Run(source, params[0], params.size() > 1 ? params[1] : "");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
+ {
+ source.Reply(_("Changes the email address of \037account\037 to \037address\037."));
+ return true;
+ }
+};
+
+class CommandNSSetKeepModes : public Command
+{
+ public:
+ CommandNSSetKeepModes(Module *creator, const Anope::string &sname = "nickserv/set/keepmodes", size_t min = 1) : Command(creator, sname, min, min + 1)
+ {
+ this->SetDesc(_("Enable or disable keep modes"));
+ this->SetSyntax("{ON | OFF}");
+ }
+
+ void Run(CommandSource &source, const Anope::string &user, const Anope::string &param)
+ {
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ NickServ::Nick *na = NickServ::FindNick(user);
+ if (!na)
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), user);
+ return;
+ }
+ NickServ::Account *nc = na->GetAccount();
+
+ EventReturn MOD_RESULT;
+ MOD_RESULT = EventManager::Get()->Dispatch(&Event::SetNickOption::OnSetNickOption, source, this, nc, param);
+ if (MOD_RESULT == EVENT_STOP)
+ return;
+
+ if (param.equals_ci("ON"))
+ {
+ Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to enable keepmodes for " << nc->GetDisplay();
+ nc->SetS<bool>("NS_KEEP_MODES", true);
+ source.Reply(_("Keep modes for \002{0}\002 is now \002on\002."), nc->GetDisplay());
+ }
+ else if (param.equals_ci("OFF"))
+ {
+ Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to disable keepmodes for " << nc->GetDisplay();
+ nc->UnsetS<bool>("NS_KEEP_MODES");
+ source.Reply(_("Keep modes for \002{0}\002 is now \002off\002."), nc->GetDisplay());
+ }
+ else
+ this->OnSyntaxError(source, "");
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ this->Run(source, source.nc->GetDisplay(), params[0]);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
+ {
+ source.Reply(_("Enables or disables keepmodes for your account. If keepmodes is enabled, services will remember your usermodes and attempt to re-set them the next time you log on."));
+ return true;
+ }
+};
+
+class CommandNSSASetKeepModes : public CommandNSSetKeepModes
+{
+ public:
+ CommandNSSASetKeepModes(Module *creator) : CommandNSSetKeepModes(creator, "nickserv/saset/keepmodes", 2)
+ {
+ this->ClearSyntax();
+ this->SetSyntax(_("\037account\037 {ON | OFF}"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ this->Run(source, params[0], params[1]);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
+ {
+ source.Reply(_("Enables or disables keepmodes for \037account\037. If keep modes is enabled, services will remember users' usermodes and attempt to re-set them the next time they log pn."));
+ return true;
+ }
+};
+
+class CommandNSSetKill : public Command
+{
+ public:
+ CommandNSSetKill(Module *creator, const Anope::string &sname = "nickserv/set/kill", size_t min = 1) : Command(creator, sname, min, min + 1)
+ {
+ this->SetDesc(_("Turn protection on or off"));
+ this->SetSyntax("{ON | QUICK | IMMED | OFF}");
+ }
+
+ void Run(CommandSource &source, const Anope::string &user, const Anope::string &param)
+ {
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ if (Config->GetModule("nickserv")->Get<bool>("nonicknameownership"))
+ {
+ source.Reply(_("This command may not be used on this network because nickname ownership is disabled."));
+ return;
+ }
+
+ NickServ::Nick *na = NickServ::FindNick(user);
+ if (!na)
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), user);
+ return;
+ }
+ NickServ::Account *nc = na->GetAccount();
+
+ EventReturn MOD_RESULT;
+ MOD_RESULT = EventManager::Get()->Dispatch(&Event::SetNickOption::OnSetNickOption, source, this, nc, param);
+ if (MOD_RESULT == EVENT_STOP)
+ return;
+
+ if (param.equals_ci("ON"))
+ {
+ nc->SetS<bool>("KILLPROTECT", true);
+ nc->UnsetS<bool>("KILL_QUICK");
+ nc->UnsetS<bool>("KILL_IMMED");
+ Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to set kill on for " << nc->GetDisplay();
+ source.Reply(_("Protection is now \002on\002 for \002{0}\002."), nc->GetDisplay());
+ }
+ else if (param.equals_ci("QUICK"))
+ {
+ nc->SetS<bool>("KILLPROTECT", true);
+ nc->SetS<bool>("KILL_QUICK", true);
+ nc->UnsetS<bool>("KILL_IMMED");
+ Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to set kill quick for " << nc->GetDisplay();
+ source.Reply(_("Protection is now \002on\002 for \002{0}\002, with a reduced delay."), nc->GetDisplay());
+ }
+ else if (param.equals_ci("IMMED"))
+ {
+ if (Config->GetModule(this->GetOwner())->Get<bool>("allowkillimmed"))
+ {
+ nc->SetS<bool>("KILLPROTECT",true);
+ nc->UnsetS<bool>("KILL_QUICK");
+ nc->SetS<bool>("KILL_IMMED", true);
+ Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to set kill immed for " << nc->GetDisplay();
+ source.Reply(_("Protection is now \002on\002 for \002{0}\002, with no delay."), nc->GetDisplay());
+ }
+ else
+ source.Reply(_("The \002IMMED\002 option is not available on this network."));
+ }
+ else if (param.equals_ci("OFF"))
+ {
+ nc->UnsetS<bool>("KILLPROTECT");
+ nc->UnsetS<bool>("KILL_QUICK");
+ nc->UnsetS<bool>("KILL_IMMED");
+ Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to disable kill for " << nc->GetDisplay();
+ source.Reply(_("Protection is now \002off\002 for \002{0}\002."), nc->GetDisplay());
+ }
+ else
+ this->OnSyntaxError(source, "KILL");
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ this->Run(source, source.nc->GetDisplay(), params[0]);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
+ {
+ source.Reply(_("Turns the automatic protection option for your account on or off."
+ " With protection on, if another user tries to use one of your nicknames, they will be given {0} to change to another their nickname, after which {1} will forcibly change their nickname.\n"
+ "\n"
+ "If you select \002QUICK\002, the user will be given only {1} to change their nick instead {0}."
+ " If you select \002IMMED\002, the user's nickname will be changed immediately \037without\037 being warned first or given a chance to change their nick."
+ " With this set, the only way to use the nickname is to match an entry on the account's access list."));
+ return true;
+ }
+};
+
+class CommandNSSASetKill : public CommandNSSetKill
+{
+ public:
+ CommandNSSASetKill(Module *creator) : CommandNSSetKill(creator, "nickserv/saset/kill", 2)
+ {
+ this->ClearSyntax();
+ this->SetSyntax(_("\037account\037 {ON | QUICK | IMMED | OFF}"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ this->Run(source, params[0], params[1]);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
+ {
+ source.Reply(_("Turns the automatic protection option for \037account\037 on or off."
+ " With protection on, if another user tries to use one of the nicknames in the group, they will be given {0} to change to another their nickname, after which {1} will forcibly change their nickname.\n"
+ "\n"
+ "If you select \002QUICK\002, the user will be given only {1} to change their nick instead {0}."
+ " If you select \002IMMED\002, the user's nickname will be changed immediately \037without\037 being warned first or given a chance to change their nick."
+ " With this set, the only way to use the nickname is to match an entry on the account's access list."));
+ return true;
+ }
+};
+
+class CommandNSSetLanguage : public Command
+{
+ public:
+ CommandNSSetLanguage(Module *creator, const Anope::string &sname = "nickserv/set/language", size_t min = 1) : Command(creator, sname, min, min + 1)
+ {
+ this->SetDesc(_("Set the language Services will use when messaging you"));
+ this->SetSyntax(_("\037language\037"));
+ }
+
+ void Run(CommandSource &source, const Anope::string &user, const Anope::string &param)
+ {
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ NickServ::Nick *na = NickServ::FindNick(user);
+ if (!na)
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), user);
+ return;
+ }
+ NickServ::Account *nc = na->GetAccount();
+
+ EventReturn MOD_RESULT;
+ MOD_RESULT = EventManager::Get()->Dispatch(&Event::SetNickOption::OnSetNickOption, source, this, nc, param);
+ if (MOD_RESULT == EVENT_STOP)
+ return;
+
+ if (param != "en_US")
+ for (unsigned j = 0; j < Language::Languages.size(); ++j)
+ {
+ if (Language::Languages[j] == param)
+ break;
+ else if (j + 1 == Language::Languages.size())
+ {
+ this->OnSyntaxError(source, "");
+ return;
+ }
+ }
+
+ Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to change the language of " << nc->GetDisplay() << " to " << param;
+
+ nc->SetLanguage(param);
+
+ if (source.GetAccount() == nc)
+ source.Reply(_("Language changed to \002{0}\002."), Language::Translate(param.c_str(), _("English")));
+ else
+ source.Reply(_("Language for \002{0}\002 changed to \002{1}\002."), nc->GetDisplay(), Language::Translate(param.c_str(), _("English")));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &param) override
+ {
+ this->Run(source, source.nc->GetDisplay(), param[0]);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
+ {
+ source.Reply(_("Changes the language services will use when sending messages to you (for example, when responding to a command you send). \037language\037 should be chosen from the following list of supported languages:"));
+
+ source.Reply(" en_US (English)");
+ for (unsigned j = 0; j < Language::Languages.size(); ++j)
+ {
+ const Anope::string &langname = Language::Translate(Language::Languages[j].c_str(), _("English"));
+ if (langname == "English")
+ continue;
+ source.Reply(" %s (%s)", Language::Languages[j].c_str(), langname.c_str());
+ }
+
+ return true;
+ }
+};
+
+class CommandNSSASetLanguage : public CommandNSSetLanguage
+{
+ public:
+ CommandNSSASetLanguage(Module *creator) : CommandNSSetLanguage(creator, "nickserv/saset/language", 2)
+ {
+ this->ClearSyntax();
+ this->SetSyntax(_("\037account\037 \037language\037"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ this->Run(source, params[0], params[1]);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
+ {
+ source.Reply(_("Changes the language services will use when sending messages to the given user (for example, when responding to a command they send). \037language\037 should be chosen from the following list of supported languages:"));
+ source.Reply(" en (English)");
+ for (unsigned j = 0; j < Language::Languages.size(); ++j)
+ {
+ const Anope::string &langname = Language::Translate(Language::Languages[j].c_str(), _("English"));
+ if (langname == "English")
+ continue;
+ source.Reply(" %s (%s)", Language::Languages[j].c_str(), langname.c_str());
+ }
+ return true;
+ }
+};
+
+class CommandNSSetMessage : public Command
+{
+ public:
+ CommandNSSetMessage(Module *creator, const Anope::string &sname = "nickserv/set/message", size_t min = 1) : Command(creator, sname, min, min + 1)
+ {
+ this->SetDesc(_("Change the communication method of Services"));
+ this->SetSyntax("{ON | OFF}");
+ }
+
+ void Run(CommandSource &source, const Anope::string &user, const Anope::string &param)
+ {
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ NickServ::Nick *na = NickServ::FindNick(user);
+ if (!na)
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), user);
+ return;
+ }
+ NickServ::Account *nc = na->GetAccount();
+
+ if (!Config->GetBlock("options")->Get<bool>("useprivmsg"))
+ {
+ source.Reply(_("You cannot %s on this network."), source.command);
+ return;
+ }
+
+ EventReturn MOD_RESULT;
+ MOD_RESULT = EventManager::Get()->Dispatch(&Event::SetNickOption::OnSetNickOption, source, this, nc, param);
+ if (MOD_RESULT == EVENT_STOP)
+ return;
+
+ if (param.equals_ci("ON"))
+ {
+ Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to enable " << source.command << " for " << nc->GetDisplay();
+ nc->SetS<bool>("MSG", true);
+ source.Reply(_("Services will now reply to \002{0}\002 with \002messages\002."), nc->GetDisplay());
+ }
+ else if (param.equals_ci("OFF"))
+ {
+ Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to disable " << source.command << " for " << nc->GetDisplay();
+ nc->UnsetS<bool>("MSG");
+ source.Reply(_("Services will now reply to \002{0}\002 with \002notices\002."), nc->GetDisplay());
+ }
+ else
+ this->OnSyntaxError(source, "MSG");
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ this->Run(source, source.nc->GetDisplay(), params[0]);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
+ {
+ Anope::string cmd = source.command;
+ size_t i = cmd.find_last_of(' ');
+ if (i != Anope::string::npos)
+ cmd = cmd.substr(i + 1);
+
+ source.Reply(_("Allows you to choose the way services communicate with you. With \002{0}\002 set, services will use messages instead of notices."), cmd);
+ return true;
+ }
+
+ void OnServHelp(CommandSource &source) override
+ {
+ if (!Config->GetBlock("options")->Get<bool>("useprivmsg"))
+ Command::OnServHelp(source);
+ }
+};
+
+class CommandNSSASetMessage : public CommandNSSetMessage
+{
+ public:
+ CommandNSSASetMessage(Module *creator) : CommandNSSetMessage(creator, "nickserv/saset/message", 2)
+ {
+ this->ClearSyntax();
+ this->SetSyntax(_("\037account\037 {ON | OFF}"));
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
+ {
+ Anope::string cmd = source.command;
+ size_t i = cmd.find_last_of(' ');
+ if (i != Anope::string::npos)
+ cmd = cmd.substr(i + 1);
+
+ source.Reply(_("Allows you to choose the way services communicate with the given user. With \002{0}\002 set, services will use messages instead of notices."), cmd);
+ return true;
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ this->Run(source, params[0], params[1]);
+ }
+};
+
+class CommandNSSetSecure : public Command
+{
+ public:
+ CommandNSSetSecure(Module *creator, const Anope::string &sname = "nickserv/set/secure", size_t min = 1) : Command(creator, sname, min, min + 1)
+ {
+ this->SetDesc(_("Turn nickname security on or off"));
+ this->SetSyntax("{ON | OFF}");
+ }
+
+ void Run(CommandSource &source, const Anope::string &user, const Anope::string &param)
+ {
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ NickServ::Nick *na = NickServ::FindNick(user);
+ if (!na)
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), user);
+ return;
+ }
+ NickServ::Account *nc = na->GetAccount();
+
+ EventReturn MOD_RESULT;
+ MOD_RESULT = EventManager::Get()->Dispatch(&Event::SetNickOption::OnSetNickOption, source, this, nc, param);
+ if (MOD_RESULT == EVENT_STOP)
+ return;
+
+ if (param.equals_ci("ON"))
+ {
+ Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to enable secure for " << nc->GetDisplay();
+ nc->SetS<bool>("NS_SECURE", true);
+ source.Reply(_("Secure option is now \002on\002 for \002{0}\002."), nc->GetDisplay());
+ }
+ else if (param.equals_ci("OFF"))
+ {
+ Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to disable secure for " << nc->GetDisplay();
+ nc->UnsetS<bool>("NS_SECURE");
+ source.Reply(_("Secure option is now \002off\002 for \002{0}\002."), nc->GetDisplay());
+ }
+ else
+ this->OnSyntaxError(source, "SECURE");
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ this->Run(source, source.nc->GetDisplay(), params[0]);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
+ {
+ source.Reply(_("Turns the security feature on or off for your account."
+ " With \002SECURE\002 set, you must enter your password before you will be recognized as the owner of the account,"
+ " regardless of whether your address is on the access list or not."
+ " However, if you are on the access list, services will not force you to change your nickname, regardless of the setting of the \002kill\002 option."));
+ return true;
+ }
+};
+
+class CommandNSSASetSecure : public CommandNSSetSecure
+{
+ public:
+ CommandNSSASetSecure(Module *creator) : CommandNSSetSecure(creator, "nickserv/saset/secure", 2)
+ {
+ this->ClearSyntax();
+ this->SetSyntax(_("\037account\037 {ON | OFF}"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ this->Run(source, params[0], params[1]);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
+ {
+ source.Reply(_("Turns the security feature on or off for \037account\037."
+ " With \002SECURE\002 set, you the user must enter their password before they will be recognized as the owner of the account,"
+ " regardless of whether their address address is on the access list or not."
+ " However, if they are on the access list, services will not force them to change your nickname, regardless of the setting of the \002kill\002 option."));
+ return true;
+ }
+};
+
+class CommandNSSASetNoexpire : public Command
+{
+ public:
+ CommandNSSASetNoexpire(Module *creator) : Command(creator, "nickserv/saset/noexpire", 1, 2)
+ {
+ this->SetDesc(_("Prevent the nickname from expiring"));
+ this->SetSyntax(_("\037nickname\037 {ON | OFF}"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ const Anope::string &user = params[0];
+ NickServ::Nick *na = NickServ::FindNick(user);
+ if (!na)
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), user);
+ return;
+ }
+
+ Anope::string param = params.size() > 1 ? params[1] : "";
+
+ if (param.equals_ci("ON"))
+ {
+ Log(LOG_ADMIN, source, this) << "to enable noexpire for " << na->GetAccount()->GetDisplay();
+ na->SetS<bool>("NS_NO_EXPIRE", true);
+ source.Reply(_("\002{0}\002 \002will not\002 expire."), na->GetNick());
+ }
+ else if (param.equals_ci("OFF"))
+ {
+ Log(LOG_ADMIN, source, this) << "to disable noexpire for " << na->GetAccount()->GetDisplay();
+ na->UnsetS<bool>("NS_NO_EXPIRE");
+ source.Reply(_("\002{0}\002 \002will\002 expire."), na->GetNick());
+ }
+ else
+ this->OnSyntaxError(source, "NOEXPIRE");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
+ {
+ source.Reply(_("Sets whether the given nickname will expire. Setting this to \002ON\002 prevents the nickname from expiring."));
+ return true;
+ }
+};
+
+class NSSet : public Module
+ , public EventHook<Event::PreCommand>
+ , public EventHook<Event::SetCorrectModes>
+ , public EventHook<NickServ::Event::PreNickExpire>
+ , public EventHook<Event::NickInfo>
+ , public EventHook<Event::UserModeSet>
+ , public EventHook<Event::UserModeUnset>
+ , public EventHook<Event::UserLogin>
+{
+ CommandNSSet commandnsset;
+ CommandNSSASet commandnssaset;
+
+ CommandNSSetAutoOp commandnssetautoop;
+ CommandNSSASetAutoOp commandnssasetautoop;
+
+ CommandNSSetDisplay commandnssetdisplay;
+ CommandNSSASetDisplay commandnssasetdisplay;
+
+ CommandNSSetEmail commandnssetemail;
+ CommandNSSASetEmail commandnssasetemail;
+
+ CommandNSSetKeepModes commandnssetkeepmodes;
+ CommandNSSASetKeepModes commandnssasetkeepmodes;
+
+ CommandNSSetKill commandnssetkill;
+ CommandNSSASetKill commandnssasetkill;
+
+ CommandNSSetLanguage commandnssetlanguage;
+ CommandNSSASetLanguage commandnssasetlanguage;
+
+ CommandNSSetMessage commandnssetmessage;
+ CommandNSSASetMessage commandnssasetmessage;
+
+ CommandNSSetPassword commandnssetpassword;
+ CommandNSSASetPassword commandnssasetpassword;
+
+ CommandNSSetSecure commandnssetsecure;
+ CommandNSSASetSecure commandnssasetsecure;
+
+ CommandNSSASetNoexpire commandnssasetnoexpire;
+
+ Serialize::Field<NickServ::Account, bool> autoop, keep_modes, killprotect, kill_quick, kill_immed, message, secure;
+ Serialize::Field<NickServ::Nick, bool> noexpire;
+
+ /* email, passcode */
+ ExtensibleItem<std::pair<Anope::string, Anope::string > > ns_set_email;
+
+ public:
+ NSSet(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , EventHook<Event::PreCommand>(this)
+ , EventHook<Event::SetCorrectModes>(this)
+ , EventHook<NickServ::Event::PreNickExpire>(this)
+ , EventHook<Event::NickInfo>(this)
+ , EventHook<Event::UserModeSet>(this)
+ , EventHook<Event::UserModeUnset>(this)
+ , EventHook<Event::UserLogin>(this)
+
+ , commandnsset(this)
+ , commandnssaset(this)
+ , commandnssetautoop(this)
+ , commandnssasetautoop(this)
+ , commandnssetdisplay(this)
+ , commandnssasetdisplay(this)
+ , commandnssetemail(this)
+ , commandnssasetemail(this)
+ , commandnssetkeepmodes(this)
+ , commandnssasetkeepmodes(this)
+ , commandnssetkill(this)
+ , commandnssasetkill(this)
+ , commandnssetlanguage(this)
+ , commandnssasetlanguage(this)
+ , commandnssetmessage(this)
+ , commandnssasetmessage(this)
+ , commandnssetpassword(this)
+ , commandnssasetpassword(this)
+ , commandnssetsecure(this)
+ , commandnssasetsecure(this)
+ , commandnssasetnoexpire(this)
+
+ , autoop(this, "AUTOOP")
+ , keep_modes(this, "NS_KEEP_MODES")
+ , killprotect(this, "KILLPROTECT")
+ , kill_quick(this, "KILL_QUICK")
+ , kill_immed(this, "KILL_IMMED")
+ , message(this, "MSG")
+ , secure(this, "NS_SECURE")
+ , noexpire(this, "NS_NO_EXPIRE")
+
+ , ns_set_email(this, "ns_set_email")
+ {
+
+ }
+
+ EventReturn OnPreCommand(CommandSource &source, Command *command, std::vector<Anope::string> &params) override
+ {
+ NickServ::Account *uac = source.nc;
+
+ if (command->GetName() == "nickserv/confirm" && !params.empty() && uac)
+ {
+ std::pair<Anope::string, Anope::string> *n = ns_set_email.Get(uac);
+ if (n)
+ {
+ if (params[0] == n->second)
+ {
+ uac->SetEmail(n->first);
+ Log(LOG_COMMAND, source, command) << "to confirm their email address change to " << uac->GetEmail();
+ source.Reply(_("Your email address has been changed to \002%s\002."), uac->GetEmail().c_str());
+ ns_set_email.Unset(uac);
+ return EVENT_STOP;
+ }
+ }
+ }
+
+ return EVENT_CONTINUE;
+ }
+
+ void OnSetCorrectModes(User *user, Channel *chan, ChanServ::AccessGroup &access, bool &give_modes, bool &take_modes) override
+ {
+ if (chan->ci)
+ {
+ /* Only give modes if autoop is set */
+ give_modes &= !user->Account() || autoop.HasExt(user->Account());
+ }
+ }
+
+ void OnPreNickExpire(NickServ::Nick *na, bool &expire) override
+ {
+ if (noexpire.HasExt(na))
+ expire = false;
+ }
+
+ void OnNickInfo(CommandSource &source, NickServ::Nick *na, InfoFormatter &info, bool show_hidden) override
+ {
+ if (!show_hidden)
+ return;
+
+ if (kill_immed.HasExt(na->GetAccount()))
+ info.AddOption(_("Immediate protection"));
+ else if (kill_quick.HasExt(na->GetAccount()))
+ info.AddOption(_("Quick protection"));
+ else if (killprotect.HasExt(na->GetAccount()))
+ info.AddOption(_("Protection"));
+ if (secure.HasExt(na->GetAccount()))
+ info.AddOption(_("Security"));
+ if (message.HasExt(na->GetAccount()))
+ info.AddOption(_("Message mode"));
+ if (autoop.HasExt(na->GetAccount()))
+ info.AddOption(_("Auto-op"));
+ if (noexpire.HasExt(na))
+ info.AddOption(_("No expire"));
+ if (keep_modes.HasExt(na->GetAccount()))
+ info.AddOption(_("Keep modes"));
+ }
+
+ void OnUserModeSet(const MessageSource &setter, User *u, const Anope::string &mname) override
+ {
+ if (u->Account() && setter.GetUser() == u)
+ {
+ NickServ::Mode *m = Serialize::New<NickServ::Mode *>();
+ if (m != nullptr)
+ {
+ m->SetAccount(u->Account());
+ m->SetMode(mname);
+ }
+ }
+ }
+
+ void OnUserModeUnset(const MessageSource &setter, User *u, const Anope::string &mname) override
+ {
+ if (u->Account() && setter.GetUser() == u)
+ {
+ for (NickServ::Mode *m : u->Account()->GetRefs<NickServ::Mode *>())
+ if (m->GetMode() == mname)
+ m->Delete();
+ }
+ }
+
+ void OnUserLogin(User *u) override
+ {
+ if (keep_modes.HasExt(u->Account()))
+ for (NickServ::Mode *mode : u->Account()->GetRefs<NickServ::Mode *>())
+ {
+ UserMode *um = ModeManager::FindUserModeByName(mode->GetMode());
+ /* if the null user can set the mode, then it's probably safe */
+ if (um && um->CanSet(NULL))
+ u->SetMode(NULL, mode->GetMode());
+ }
+ }
+};
+
+MODULE_INIT(NSSet)
diff --git a/modules/nickserv/set_misc.cpp b/modules/nickserv/set_misc.cpp
new file mode 100644
index 000000000..899ddb4ab
--- /dev/null
+++ b/modules/nickserv/set_misc.cpp
@@ -0,0 +1,239 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2010-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/nickserv/set_misc.h"
+#include "modules/nickserv/info.h"
+#include "modules/nickserv/set.h"
+
+static Anope::map<Anope::string> descriptions;
+
+class NSMiscDataImpl : public NSMiscData
+{
+ friend class NSMiscDataType;
+
+ NickServ::Account *account = nullptr;
+ Anope::string name, data;
+
+ public:
+ NSMiscDataImpl(Serialize::TypeBase *type) : NSMiscData(type) { }
+ NSMiscDataImpl(Serialize::TypeBase *type, Serialize::ID id) : NSMiscData(type, id) { }
+
+ NickServ::Account *GetAccount() override;
+ void SetAccount(NickServ::Account *s) override;
+
+ Anope::string GetName() override;
+ void SetName(const Anope::string &n) override;
+
+ Anope::string GetData() override;
+ void SetData(const Anope::string &d) override;
+};
+
+class NSMiscDataType : public Serialize::Type<NSMiscDataImpl>
+{
+ public:
+ Serialize::ObjectField<NSMiscDataImpl, NickServ::Account *> owner;
+ Serialize::Field<NSMiscDataImpl, Anope::string> name, data;
+
+ NSMiscDataType(Module *me) : Serialize::Type<NSMiscDataImpl>(me)
+ , owner(this, "nc", &NSMiscDataImpl::account, true)
+ , name(this, "name", &NSMiscDataImpl::name)
+ , data(this, "data", &NSMiscDataImpl::data)
+ {
+ }
+};
+
+NickServ::Account *NSMiscDataImpl::GetAccount()
+{
+ return Get(&NSMiscDataType::owner);
+}
+
+void NSMiscDataImpl::SetAccount(NickServ::Account *s)
+{
+ Set(&NSMiscDataType::owner, s);
+}
+
+Anope::string NSMiscDataImpl::GetName()
+{
+ return Get(&NSMiscDataType::name);
+}
+
+void NSMiscDataImpl::SetName(const Anope::string &n)
+{
+ Set(&NSMiscDataType::name, n);
+}
+
+Anope::string NSMiscDataImpl::GetData()
+{
+ return Get(&NSMiscDataType::data);
+}
+
+void NSMiscDataImpl::SetData(const Anope::string &d)
+{
+ Set(&NSMiscDataType::data, d);
+}
+
+class CommandNSSetMisc : public Command
+{
+ Anope::string GetAttribute(const Anope::string &command)
+ {
+ size_t sp = command.rfind(' ');
+ if (sp != Anope::string::npos)
+ return command.substr(sp + 1);
+ return command;
+ }
+
+ public:
+ CommandNSSetMisc(Module *creator, const Anope::string &cname = "nickserv/set/misc", size_t min = 0) : Command(creator, cname, min, min + 1)
+ {
+ this->SetSyntax(_("[\037parameter\037]"));
+ }
+
+ void Run(CommandSource &source, const Anope::string &user, const Anope::string &param)
+ {
+ if (Anope::ReadOnly)
+ {
+ source.Reply(_("Services are in read-only mode."));
+ return;
+ }
+
+ NickServ::Nick *na = NickServ::FindNick(user);
+ if (!na)
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), user);
+ return;
+ }
+ NickServ::Account *nc = na->GetAccount();
+
+ EventReturn MOD_RESULT = EventManager::Get()->Dispatch(&Event::SetNickOption::OnSetNickOption, source, this, nc, param);
+ if (MOD_RESULT == EVENT_STOP)
+ return;
+
+ Anope::string scommand = GetAttribute(source.command);
+
+ /* remove existing */
+ for (NSMiscData *data : nc->GetRefs<NSMiscData *>())
+ if (data->GetName() == scommand)
+ {
+ data->Delete();
+ break;
+ }
+
+ if (!param.empty())
+ {
+ NSMiscData *data = Serialize::New<NSMiscData *>();
+ data->SetAccount(nc);
+ data->SetName(scommand);
+ data->SetData(param);
+
+ source.Reply(_("\002{0}\002 for \002{1}\002 set to \002{2}\002."), scommand, nc->GetDisplay(), param);
+ }
+ else
+ {
+ source.Reply(_("\002{0}\002 for \002{1}\002 unset."), scommand, nc->GetDisplay());
+ }
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ this->Run(source, source.nc->GetDisplay(), !params.empty() ? params[0] : "");
+ }
+
+ void OnServHelp(CommandSource &source) override
+ {
+ if (descriptions.count(source.command))
+ {
+ this->SetDesc(descriptions[source.command]);
+ Command::OnServHelp(source);
+ }
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ if (descriptions.count(source.command))
+ {
+ source.Reply(Language::Translate(source.nc, descriptions[source.command].c_str()));
+ return true;
+ }
+ return false;
+ }
+};
+
+class CommandNSSASetMisc : public CommandNSSetMisc
+{
+ public:
+ CommandNSSASetMisc(Module *creator) : CommandNSSetMisc(creator, "nickserv/saset/misc", 1)
+ {
+ this->ClearSyntax();
+ this->SetSyntax(_("\037nickname\037 [\037parameter\037]"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ this->Run(source, params[0], params.size() > 1 ? params[1] : "");
+ }
+};
+
+class NSSetMisc : public Module
+ , public EventHook<Event::NickInfo>
+{
+ CommandNSSetMisc commandnssetmisc;
+ CommandNSSASetMisc commandnssasetmisc;
+ NSMiscDataType type;
+
+ public:
+ NSSetMisc(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , EventHook<Event::NickInfo>(this)
+ , commandnssetmisc(this)
+ , commandnssasetmisc(this)
+ , type(this)
+ {
+ }
+
+ void OnReload(Configuration::Conf *conf) override
+ {
+ descriptions.clear();
+
+ for (int i = 0; i < conf->CountBlock("command"); ++i)
+ {
+ Configuration::Block *block = conf->GetBlock("command", i);
+
+ const Anope::string &cmd = block->Get<Anope::string>("command");
+
+ if (cmd != "nickserv/set/misc" && cmd != "nickserv/saset/misc")
+ continue;
+
+ Anope::string cname = block->Get<Anope::string>("name");
+ Anope::string desc = block->Get<Anope::string>("misc_description");
+
+ if (cname.empty() || desc.empty())
+ continue;
+
+ descriptions[cname] = desc;
+ }
+ }
+
+ void OnNickInfo(CommandSource &source, NickServ::Nick *na, InfoFormatter &info, bool) override
+ {
+ for (NSMiscData *data : na->GetAccount()->GetRefs<NSMiscData *>())
+ info[data->GetName()] = data->GetData();
+ }
+};
+
+MODULE_INIT(NSSetMisc)
diff --git a/modules/commands/ns_status.cpp b/modules/nickserv/status.cpp
index b06e51ec4..c092a9b46 100644
--- a/modules/commands/ns_status.cpp
+++ b/modules/nickserv/status.cpp
@@ -1,12 +1,20 @@
-/* NickServ core functions
+/*
+ * Anope IRC Services
*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
*
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "module.h"
@@ -21,34 +29,35 @@ class CommandNSStatus : public Command
this->AllowUnregistered(true);
}
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
{
const Anope::string &nick = !params.empty() ? params[0] : source.GetNick();
- const NickAlias *na = NickAlias::Find(nick);
+ const NickServ::Nick *na = NickServ::FindNick(nick);
spacesepstream sep(nick);
Anope::string nickbuf;
while (sep.GetToken(nickbuf))
{
+ #if 0
User *u2 = User::Find(nickbuf, true);
if (!u2) /* Nick is not online */
source.Reply("STATUS %s %d %s", nickbuf.c_str(), 0, "");
- else if (u2->IsIdentified() && na && na->nc == u2->Account()) /* Nick is identified */
- source.Reply("STATUS %s %d %s", nickbuf.c_str(), 3, u2->Account()->display.c_str());
+ else if (u2->IsIdentified() && na && na->GetAccount() == u2->Account()) /* Nick is identified */
+ source.Reply("STATUS %s %d %s", nickbuf.c_str(), 3, u2->Account()->GetDisplay().c_str());
else if (u2->IsRecognized()) /* Nick is recognised, but NOT identified */
- source.Reply("STATUS %s %d %s", nickbuf.c_str(), 2, u2->Account() ? u2->Account()->display.c_str() : "");
+ source.Reply("STATUS %s %d %s", nickbuf.c_str(), 2, u2->Account() ? u2->Account()->GetDisplay().c_str() : "");
else if (!na) /* Nick is online, but NOT a registered */
source.Reply("STATUS %s %d %s", nickbuf.c_str(), 0, "");
else
/* Nick is not identified for the nick, but they could be logged into an account,
* so we tell the user about it
*/
- source.Reply("STATUS %s %d %s", nickbuf.c_str(), 1, u2->Account() ? u2->Account()->display.c_str() : "");
+ source.Reply("STATUS %s %d %s", nickbuf.c_str(), 1, u2->Account() ? u2->Account()->GetDisplay().c_str() : "");
+ #endif
}
- return;
}
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
{
this->SendSyntax(source);
source.Reply(" ");
@@ -77,10 +86,10 @@ class NSStatus : public Module
CommandNSStatus commandnsstatus;
public:
- NSStatus(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandnsstatus(this)
+ NSStatus(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandnsstatus(this)
{
-
+ throw ModuleException("Remind Adam to fix this");
}
};
diff --git a/modules/nickserv/suspend.cpp b/modules/nickserv/suspend.cpp
new file mode 100644
index 000000000..9bdafa2d8
--- /dev/null
+++ b/modules/nickserv/suspend.cpp
@@ -0,0 +1,348 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/nickserv/suspend.h"
+#include "modules/nickserv/info.h"
+#include "modules/nickserv.h"
+
+class NSSuspendInfoImpl : public NSSuspendInfo
+{
+ friend class NSSuspendType;
+
+ NickServ::Account *account = nullptr;
+ Anope::string by, reason;
+ time_t when = 0, expires =0;
+
+ public:
+ NSSuspendInfoImpl(Serialize::TypeBase *type) : NSSuspendInfo(type) { }
+ NSSuspendInfoImpl(Serialize::TypeBase *type, Serialize::ID id) : NSSuspendInfo(type, id) { }
+
+ NickServ::Account *GetAccount() override;
+ void SetAccount(NickServ::Account *) override;
+
+ Anope::string GetBy() override;
+ void SetBy(const Anope::string &by) override;
+
+ Anope::string GetReason() override;
+ void SetReason(const Anope::string &reason) override;
+
+ time_t GetWhen() override;
+ void SetWhen(const time_t &w) override;
+
+ time_t GetExpires() override;
+ void SetExpires(const time_t &e) override;
+};
+
+class NSSuspendType : public Serialize::Type<NSSuspendInfoImpl>
+{
+ public:
+ Serialize::ObjectField<NSSuspendInfoImpl, NickServ::Account *> account;
+ Serialize::Field<NSSuspendInfoImpl, Anope::string> by, reason;
+ Serialize::Field<NSSuspendInfoImpl, time_t> when, expires;
+
+ NSSuspendType(Module *me) : Serialize::Type<NSSuspendInfoImpl>(me)
+ , account(this, "nick", &NSSuspendInfoImpl::account, true)
+ , by(this, "by", &NSSuspendInfoImpl::by)
+ , reason(this, "reason", &NSSuspendInfoImpl::reason)
+ , when(this, "when", &NSSuspendInfoImpl::when)
+ , expires(this, "expires", &NSSuspendInfoImpl::expires)
+ {
+ }
+};
+
+NickServ::Account *NSSuspendInfoImpl::GetAccount()
+{
+ return Get(&NSSuspendType::account);
+}
+
+void NSSuspendInfoImpl::SetAccount(NickServ::Account *s)
+{
+ Set(&NSSuspendType::account, s);
+}
+
+Anope::string NSSuspendInfoImpl::GetBy()
+{
+ return Get(&NSSuspendType::by);
+}
+
+void NSSuspendInfoImpl::SetBy(const Anope::string &by)
+{
+ Set(&NSSuspendType::by, by);
+}
+
+Anope::string NSSuspendInfoImpl::GetReason()
+{
+ return Get(&NSSuspendType::reason);
+}
+
+void NSSuspendInfoImpl::SetReason(const Anope::string &reason)
+{
+ Set(&NSSuspendType::reason, reason);
+}
+
+time_t NSSuspendInfoImpl::GetWhen()
+{
+ return Get(&NSSuspendType::when);
+}
+
+void NSSuspendInfoImpl::SetWhen(const time_t &w)
+{
+ Set(&NSSuspendType::when, w);
+}
+
+time_t NSSuspendInfoImpl::GetExpires()
+{
+ return Get(&NSSuspendType::expires);
+}
+
+void NSSuspendInfoImpl::SetExpires(const time_t &e)
+{
+ Set(&NSSuspendType::expires, e);
+}
+
+class CommandNSSuspend : public Command
+{
+ public:
+ CommandNSSuspend(Module *creator) : Command(creator, "nickserv/suspend", 2, 3)
+ {
+ this->SetDesc(_("Suspend a given nick"));
+ this->SetSyntax(_("\037account\037 [+\037expiry\037] [\037reason\037]"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+
+ const Anope::string &nick = params[0];
+ Anope::string expiry = params[1];
+ Anope::string reason = params.size() > 2 ? params[2] : "";
+ time_t expiry_secs = Config->GetModule(this->GetOwner())->Get<time_t>("suspendexpire");
+
+ if (Anope::ReadOnly)
+ source.Reply(_("Services are in read-only mode. Any changes made may not persist."));
+
+ if (expiry[0] != '+')
+ {
+ reason = expiry + " " + reason;
+ reason.trim();
+ expiry.clear();
+ }
+ else
+ {
+ expiry_secs = Anope::DoTime(expiry);
+ if (expiry_secs == -1)
+ {
+ source.Reply(_("Invalid expiry time \002{0}\002."), expiry);
+ return;
+ }
+ }
+
+ NickServ::Nick *na = NickServ::FindNick(nick);
+ if (!na)
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), nick);
+ return;
+ }
+
+ if (Config->GetModule("nickserv")->Get<bool>("secureadmins", "yes") && na->GetAccount()->IsServicesOper())
+ {
+ source.Reply(_("You may not suspend other Services Operators' nicknames."));
+ return;
+ }
+
+ NSSuspendInfo *si = na->GetAccount()->GetRef<NSSuspendInfo *>();
+ if (!si)
+ {
+ source.Reply(_("\002%s\002 is already suspended."), na->GetAccount()->GetDisplay().c_str());
+ return;
+ }
+
+ NickServ::Account *nc = na->GetAccount();
+
+ si = Serialize::New<NSSuspendInfo *>();
+ si->SetAccount(nc);
+ si->SetBy(source.GetNick());
+ si->SetReason(reason);
+ si->SetWhen(Anope::CurTime);
+ si->SetExpires(expiry_secs ? expiry_secs + Anope::CurTime : 0);
+
+ for (NickServ::Nick *na2 : nc->GetRefs<NickServ::Nick *>())
+ {
+ na2->SetLastQuit(reason);
+
+ User *u2 = User::Find(na2->GetNick(), true);
+ if (u2)
+ {
+ u2->Logout();
+ if (NickServ::service)
+ NickServ::service->Collide(u2, na2);
+ }
+ }
+
+ Log(LOG_ADMIN, source, this) << "for " << nick << " (" << (!reason.empty() ? reason : "No reason") << "), expires on " << (expiry_secs ? Anope::strftime(Anope::CurTime + expiry_secs) : "never");
+ source.Reply(_("\002{0}\002 is now suspended."), na->GetNick());
+
+ EventManager::Get()->Dispatch(&Event::NickSuspend::OnNickSuspend, na);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Suspends \037account\037, which prevents it from being used while keeping all the data for it."
+ " If an expiry is given the account will be unsuspended after that period of time, otherwise the default expiry from the configuration is used.")); // XXX
+ return true;
+ }
+};
+
+class CommandNSUnSuspend : public Command
+{
+ public:
+ CommandNSUnSuspend(Module *creator) : Command(creator, "nickserv/unsuspend", 1, 1)
+ {
+ this->SetDesc(_("Unsuspend a given nick"));
+ this->SetSyntax(_("\037account\037"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &nick = params[0];
+
+ if (Anope::ReadOnly)
+ source.Reply(_("Services are in read-only mode. Any changes made may not persist."));
+
+ NickServ::Nick *na = NickServ::FindNick(nick);
+ if (!na)
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), nick);
+ return;
+ }
+
+ NSSuspendInfo *si = na->GetAccount()->GetRef<NSSuspendInfo *>();
+ if (!si)
+ {
+ source.Reply(_("\002{0}\002 is not suspended."), na->GetNick());
+ return;
+ }
+
+ Log(LOG_ADMIN, source, this) << "for " << na->GetNick() << " which was suspended by " << (!si->GetBy().empty() ? si->GetBy() : "(none)") << " for: " << (!si->GetReason().empty() ? si->GetReason() : "No reason");
+
+ si->Delete();
+
+ source.Reply(_("\002{0}\002 is now released."), na->GetNick());
+
+ EventManager::Get()->Dispatch(&Event::NickUnsuspend::OnNickUnsuspend, na);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Unsuspends \037account\037, which allows it to be used again."));
+ return true;
+ }
+};
+
+class NSSuspend : public Module
+ , public EventHook<Event::NickInfo>
+ , public EventHook<NickServ::Event::PreNickExpire>
+ , public EventHook<NickServ::Event::NickValidate>
+{
+ CommandNSSuspend commandnssuspend;
+ CommandNSUnSuspend commandnsunsuspend;
+ std::vector<Anope::string> show;
+ NSSuspendType nst;
+
+ struct trim
+ {
+ Anope::string operator()(Anope::string s) const
+ {
+ return s.trim();
+ }
+ };
+
+ bool Show(CommandSource &source, const Anope::string &what) const
+ {
+ return source.IsOper() || std::find(show.begin(), show.end(), what) != show.end();
+ }
+
+ public:
+ NSSuspend(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , EventHook<Event::NickInfo>(this)
+ , EventHook<NickServ::Event::PreNickExpire>(this)
+ , EventHook<NickServ::Event::NickValidate>(this)
+ , commandnssuspend(this)
+ , commandnsunsuspend(this)
+ , nst(this)
+ {
+ }
+
+ void OnReload(Configuration::Conf *conf) override
+ {
+ Anope::string s = conf->GetModule(this)->Get<Anope::string>("show");
+ commasepstream(s).GetTokens(show);
+ std::transform(show.begin(), show.end(), show.begin(), trim());
+ }
+
+ void OnNickInfo(CommandSource &source, NickServ::Nick *na, InfoFormatter &info, bool show_hidden) override
+ {
+ NSSuspendInfo *s = na->GetAccount()->GetRef<NSSuspendInfo *>();
+ if (!s)
+ return;
+
+ if (show_hidden || Show(source, "suspended"))
+ info[_("Suspended")] = _("This nickname is \002suspended\002.");
+ if (!s->GetBy().empty() && (show_hidden || Show(source, "by")))
+ info[_("Suspended by")] = s->GetBy();
+ if (!s->GetReason().empty() && (show_hidden || Show(source, "reason")))
+ info[_("Suspend reason")] = s->GetReason();
+ if (s->GetWhen() && (show_hidden || Show(source, "on")))
+ info[_("Suspended on")] = Anope::strftime(s->GetWhen(), source.GetAccount());
+ if (s->GetExpires() && (show_hidden || Show(source, "expires")))
+ info[_("Suspension expires")] = Anope::strftime(s->GetExpires(), source.GetAccount());
+ }
+
+ void OnPreNickExpire(NickServ::Nick *na, bool &expire) override
+ {
+ NSSuspendInfo *s = na->GetAccount()->GetRef<NSSuspendInfo *>();
+ if (!s)
+ return;
+
+ expire = false;
+
+ if (!s->GetExpires())
+ return;
+
+ if (s->GetExpires() < Anope::CurTime)
+ {
+ na->SetLastSeen(Anope::CurTime);
+ s->Delete();
+
+ Log(LOG_NORMAL, "nickserv/expire", Config->GetClient("NickServ")) << "Expiring suspend for " << na->GetNick();
+ }
+ }
+
+ EventReturn OnNickValidate(User *u, NickServ::Nick *na) override
+ {
+ NSSuspendInfo *s = na->GetAccount()->GetRef<NSSuspendInfo *>();
+ if (!s)
+ return EVENT_CONTINUE;
+
+ u->SendMessage(Config->GetClient("NickServ"), _("\002{0}\002 is suspended."), u->nick);
+ return EVENT_STOP;
+ }
+};
+
+MODULE_INIT(NSSuspend)
diff --git a/modules/nickserv/update.cpp b/modules/nickserv/update.cpp
new file mode 100644
index 000000000..70feabadf
--- /dev/null
+++ b/modules/nickserv/update.cpp
@@ -0,0 +1,71 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/nickserv/update.h"
+
+class CommandNSUpdate : public Command
+{
+ public:
+ CommandNSUpdate(Module *creator) : Command(creator, "nickserv/update", 0, 0)
+ {
+ this->SetDesc(_("Updates your current status, i.e. it checks for new memos"));
+ this->RequireUser(true);
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ User *u = source.GetUser();
+ NickServ::Nick *na = NickServ::FindNick(u->nick);
+
+ if (na && na->GetAccount() == source.GetAccount())
+ {
+ na->SetLastRealname(u->realname);
+ na->SetLastSeen(Anope::CurTime);
+ }
+
+ EventManager::Get()->Dispatch(&Event::NickUpdate::OnNickUpdate, u);
+
+ source.Reply(_("Status updated (memos, vhost, chmodes, flags)."));
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &) override
+ {
+ this->SendSyntax(source);
+ source.Reply(" ");
+ source.Reply(_("Updates your current status, i.e. it checks for new memos,\n"
+ "sets needed channel modes and updates your vhost and\n"
+ "your userflags (lastseentime, etc)."));
+ return true;
+ }
+};
+
+class NSUpdate : public Module
+{
+ CommandNSUpdate commandnsupdate;
+
+ public:
+ NSUpdate(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandnsupdate(this)
+ {
+
+ }
+};
+
+MODULE_INIT(NSUpdate)
diff --git a/modules/operserv/CMakeLists.txt b/modules/operserv/CMakeLists.txt
new file mode 100644
index 000000000..cd225a94d
--- /dev/null
+++ b/modules/operserv/CMakeLists.txt
@@ -0,0 +1 @@
+build_modules(${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/modules/operserv/akill.cpp b/modules/operserv/akill.cpp
new file mode 100644
index 000000000..36d555b79
--- /dev/null
+++ b/modules/operserv/akill.cpp
@@ -0,0 +1,455 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+
+class CommandOSAKill : public Command
+{
+ ServiceReference<XLineManager> akills;
+
+ void DoAdd(CommandSource &source, const std::vector<Anope::string> &params)
+ {
+ Anope::string expiry, mask;
+
+ if (params.size() < 2)
+ {
+ this->OnSyntaxError(source, "ADD");
+ return;
+ }
+
+ spacesepstream sep(params[1]);
+ sep.GetToken(mask);
+
+ if (mask[0] == '+')
+ {
+ expiry = mask;
+ sep.GetToken(mask);
+ }
+
+ time_t expires = !expiry.empty() ? Anope::DoTime(expiry) : Config->GetModule("operserv")->Get<time_t>("autokillexpiry", "30d");
+ /* If the expiry given does not contain a final letter, it's in days,
+ * said the doc. Ah well.
+ */
+ if (!expiry.empty() && isdigit(expiry[expiry.length() - 1]))
+ expires *= 86400;
+ /* Do not allow less than a minute expiry time */
+ if (expires && expires < 60)
+ {
+ source.Reply(_("Invalid expiry time \002{0}\002."), expiry);
+ return;
+ }
+ else if (expires > 0)
+ expires += Anope::CurTime;
+
+ if (sep.StreamEnd())
+ {
+ this->OnSyntaxError(source, "ADD");
+ return;
+ }
+
+ Anope::string reason;
+ if (mask.find('#') != Anope::string::npos)
+ {
+ Anope::string remaining = sep.GetRemaining();
+
+ size_t co = remaining[0] == ':' ? 0 : remaining.rfind(" :");
+ if (co == Anope::string::npos)
+ {
+ this->OnSyntaxError(source, "ADD");
+ return;
+ }
+
+ if (co != 0)
+ ++co;
+
+ reason = remaining.substr(co + 1);
+ mask += " " + remaining.substr(0, co);
+ mask.trim();
+ }
+ else
+ reason = sep.GetRemaining();
+
+ if (mask[0] == '/' && mask[mask.length() - 1] == '/')
+ {
+ if (!Config->regex_flags)
+ {
+ source.Reply(_("Regex is disabled."));
+ return;
+ }
+
+ Anope::string stripped_mask = mask.substr(1, mask.length() - 2);
+ try
+ {
+ std::regex(stripped_mask.str(), Config->regex_flags);
+ }
+ catch (const std::regex_error &ex)
+ {
+ source.Reply("%s", ex.what());
+ return;
+ }
+ }
+
+ User *targ = User::Find(mask, true);
+ if (targ)
+ mask = "*@" + targ->host;
+
+ if (Config->GetModule("operserv")->Get<bool>("addakiller", "yes") && !source.GetNick().empty())
+ reason = "[" + source.GetNick() + "] " + reason;
+
+ if (mask.find_first_not_of("/~@.*?") == Anope::string::npos)
+ {
+ source.Reply(_("\002{0}\002 coverage is too wide; Please use a more specific mask."), mask);
+ return;
+ }
+
+ if (mask.find('@') == Anope::string::npos)
+ {
+ source.Reply(_("Mask must be in the form \037user\037@\037host\037."));
+ return;
+ }
+
+ if (!akills->CanAdd(source, mask, expires, reason))
+ return;
+
+ XLine *x = Serialize::New<XLine *>();
+ x->SetMask(mask);
+ x->SetBy(source.GetNick());
+ x->SetExpires(expires);
+ x->SetReason(reason);
+
+ if (Config->GetModule("operserv")->Get<bool>("akillids"))
+ x->SetID(XLineManager::GenerateUID());
+
+ unsigned int affected = 0;
+ for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it)
+ if (akills->Check(it->second, x))
+ ++affected;
+ float percent = static_cast<float>(affected) / static_cast<float>(UserListByNick.size()) * 100.0;
+
+ if (percent > 95)
+ {
+ source.Reply(_("\002{0}\002 coverage is too wide; Please use a more specific mask."), mask);
+ Log(LOG_ADMIN, source, this) << "tried to akill " << percent << "% of the network (" << affected << " users)";
+ delete x;
+ return;
+ }
+
+ EventReturn MOD_RESULT;
+ MOD_RESULT = EventManager::Get()->Dispatch(&Event::AddXLine::OnAddXLine, source, x, akills);
+ if (MOD_RESULT == EVENT_STOP)
+ {
+ delete x;
+ return;
+ }
+
+ akills->AddXLine(x);
+ if (Config->GetModule("operserv")->Get<bool>("akillonadd"))
+ akills->Send(NULL, x);
+
+ source.Reply(_("\002{0}\002 added to the akill list."), mask);
+
+ Log(LOG_ADMIN, source, this) << "on " << mask << " (" << x->GetReason() << "), expires in " << (expires ? Anope::Duration(expires - Anope::CurTime) : "never") << " [affects " << affected << " user(s) (" << percent << "%)]";
+ if (Anope::ReadOnly)
+ source.Reply(_("Services are in read-only mode. Any changes made may not persist."));
+ }
+
+ void DoDel(CommandSource &source, const std::vector<Anope::string> &params)
+ {
+ const Anope::string &mask = params.size() > 1 ? params[1] : "";
+
+ if (mask.empty())
+ {
+ this->OnSyntaxError(source, "DEL");
+ return;
+ }
+
+ if (akills->GetXLines().empty())
+ {
+ source.Reply(_("The akill list is empty."));
+ return;
+ }
+
+ if (isdigit(mask[0]) && mask.find_first_not_of("1234567890,-") == Anope::string::npos)
+ {
+ unsigned int deleted = 0;
+
+ NumberList(mask, true,
+ [&](unsigned int number)
+ {
+ XLine *x = akills->GetEntry(number - 1);
+
+ if (!x)
+ return;
+
+ Log(LOG_ADMIN, source, this) << "to remove " << x->GetMask() << " from the list";
+
+ ++deleted;
+ x->Delete();
+ },
+ [&]()
+ {
+ if (!deleted)
+ source.Reply(_("No matching entries on the akill list."));
+ else if (deleted == 1)
+ source.Reply(_("Deleted \0021\002 entry from the akill list."));
+ else
+ source.Reply(_("Deleted \002{0}\002 entries from the akill list."), deleted);
+ });
+ }
+ else
+ {
+ XLine *x = akills->HasEntry(mask);
+
+ if (!x)
+ {
+ source.Reply(_("\002{0}\002 was not found on the akill list."), mask);
+ return;
+ }
+
+ do
+ {
+ EventManager::Get()->Dispatch(&Event::DelXLine::OnDelXLine, source, x, akills);
+
+ Log(LOG_ADMIN, source, this) << "to remove " << x->GetMask() << " from the list";
+ source.Reply(_("\002{0}\002 deleted from the akill list."), x->GetMask());
+ x->Delete();
+ }
+ while ((x = akills->HasEntry(mask)));
+
+ }
+
+ if (Anope::ReadOnly)
+ source.Reply(_("Services are in read-only mode. Any changes made may not persist."));
+ }
+
+ void ProcessList(CommandSource &source, const std::vector<Anope::string> &params, ListFormatter &list)
+ {
+ const Anope::string &mask = params.size() > 1 ? params[1] : "";
+
+ if (!mask.empty() && isdigit(mask[0]) && mask.find_first_not_of("1234567890,-") == Anope::string::npos)
+ {
+ NumberList(mask, false,
+ [&](unsigned int number)
+ {
+ XLine *x = akills->GetEntry(number - 1);
+
+ if (!x)
+ return;
+
+ ListFormatter::ListEntry entry;
+ entry["Number"] = stringify(number);
+ entry["Mask"] = x->GetMask();
+ entry["Creator"] = x->GetBy();
+ entry["Created"] = Anope::strftime(x->GetCreated(), NULL, true);
+ entry["Expires"] = Anope::Expires(x->GetExpires(), source.nc);
+ entry["ID"] = x->GetID();
+ entry["Reason"] = x->GetReason();
+ list.AddEntry(entry);
+ },
+ [&]{});
+ }
+ else
+ {
+ unsigned int i = 0;
+ for (XLine *x : akills->GetXLines())
+ {
+ if (mask.empty() || mask.equals_ci(x->GetMask()) || mask == x->GetID() || Anope::Match(x->GetMask(), mask, false, true))
+ {
+ ListFormatter::ListEntry entry;
+ entry["Number"] = stringify(++i);
+ entry["Mask"] = x->GetMask();
+ entry["Creator"] = x->GetBy();
+ entry["Created"] = Anope::strftime(x->GetCreated(), NULL, true);
+ entry["Expires"] = Anope::Expires(x->GetExpires(), source.nc);
+ entry["ID"] = x->GetID();
+ entry["Reason"] = x->GetReason();
+ list.AddEntry(entry);
+ }
+ }
+ }
+
+ if (list.IsEmpty())
+ {
+ source.Reply(_("There are no matching entries on the akill list."));
+ return;
+ }
+
+ source.Reply(_("Current akill list:"));
+
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+
+ source.Reply(_("End of akill list."));
+ }
+
+ void DoList(CommandSource &source, const std::vector<Anope::string> &params)
+ {
+ if (akills->GetXLines().empty())
+ {
+ source.Reply(_("The akill list is empty."));
+ return;
+ }
+
+ ListFormatter list(source.GetAccount());
+ list.AddColumn(_("Number")).AddColumn(_("Mask")).AddColumn(_("Reason"));
+
+ this->ProcessList(source, params, list);
+ }
+
+ void DoView(CommandSource &source, const std::vector<Anope::string> &params)
+ {
+ if (akills->GetXLines().empty())
+ {
+ source.Reply(_("The akill list is empty."));
+ return;
+ }
+
+ ListFormatter list(source.GetAccount());
+ list.AddColumn(_("Number")).AddColumn(_("Mask")).AddColumn(_("Creator")).AddColumn(_("Created")).AddColumn(_("Expires"));
+ if (Config->GetModule("operserv")->Get<bool>("akillids"))
+ list.AddColumn(_("ID"));
+ list.AddColumn(_("Reason"));
+
+ this->ProcessList(source, params, list);
+ }
+
+ void DoClear(CommandSource &source)
+ {
+ for (XLine *x : akills->GetXLines())
+ {
+ EventManager::Get()->Dispatch(&Event::DelXLine::OnDelXLine, source, x, akills);
+ x->Delete();
+ }
+
+ Log(LOG_ADMIN, source, this) << "to CLEAR the list";
+ source.Reply(_("The akill list has been cleared."));
+
+ if (Anope::ReadOnly)
+ source.Reply(_("Services are in read-only mode. Any changes made may not persist."));
+ }
+ public:
+ CommandOSAKill(Module *creator) : Command(creator, "operserv/akill", 1, 2)
+ , akills("xlinemanager/sgline")
+ {
+ this->SetDesc(_("Manipulate the AKILL list"));
+ this->SetSyntax(_("ADD [+\037expiry\037] \037mask\037 \037reason\037"));
+ this->SetSyntax(_("DEL {\037mask\037 | \037entry-num\037 | \037list\037 | \037id\037}"));
+ this->SetSyntax(_("LIST [\037mask\037 | \037list\037 | \037id\037]"));
+ this->SetSyntax(_("VIEW [\037mask\037 | \037list\037 | \037id\037]"));
+ this->SetSyntax("CLEAR");
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &cmd = params[0];
+
+ if (!akills)
+ return;
+
+ if (cmd.equals_ci("ADD"))
+ return this->DoAdd(source, params);
+ else if (cmd.equals_ci("DEL"))
+ return this->DoDel(source, params);
+ else if (cmd.equals_ci("LIST"))
+ return this->DoList(source, params);
+ else if (cmd.equals_ci("VIEW"))
+ return this->DoView(source, params);
+ else if (cmd.equals_ci("CLEAR"))
+ return this->DoClear(source);
+ else
+ this->OnSyntaxError(source, "");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ if (subcommand.equals_ci("ADD"))
+ {
+ source.Reply(_("The \002{0} ADD\002 command adds \037mask\037 to the auto kill list with the given \037reason\037."
+ "\037mask\037 can be in the format of nickname!username@hostname#realname."
+ " If a real name is specified, the reason must be prefixed with a :."
+ " \037expiry\037 is optional, and if specified must be an integer followed by one of \037d\037 (days), \037h\037 (hours), or \037m\037 (minutes)."
+ " If a unit specifier is not included, the default is days, so \037+30\037 means 30 days."
+ " To add an auto kill which does not expire, use \037+0\037. "
+ " The default auto kill expiry time is \002{1}\002"),
+ source.command, Anope::Duration(Config->GetModule("operserv")->Get<time_t>("autokillexpiry", "30d"), source.GetAccount()));
+
+ const Anope::string &regexengine = Config->GetBlock("options")->Get<Anope::string>("regexengine");
+ if (!regexengine.empty())
+ {
+ source.Reply(" ");
+ source.Reply(_("Regex matches are also supported using the %s engine. Enclose your mask in // if this is desired."), regexengine);
+ }
+ }
+ else if (subcommand.equals_ci("DEL"))
+ source.Reply(_("The \002{0} DEL\002 command removes the given \037mask\037 from the auto kill list, if present."
+ " If a list of entry numbers is given, those entries are deleted."),
+ source.command);
+ else if (subcommand.equals_ci("LIST") || subcommand.equals_ci("VIEW"))
+ source.Reply(_("The \002{0} LIST\002 and \002{0} VIEW\002 commands display the auto kill list."
+ " If a wildcard \037mask\037 is given, only those entries matching the mask are displayed."
+ " If a list of entry numbers is given, only those entries are shown."
+ " \002VIEW\002 is similar to \002LIST\002 but also shows who created the auto kill, when it was created, and when it expires.\n"
+ "\n"
+ "Example:\n"
+ "\n"
+ " {0} LIST 2-5,7-9\n"
+ " Lists auto kill entries numbered 2 through 5 and 7 through 9."),
+ source.command);
+ else if (subcommand.equals_ci("CLEAR"))
+ source.Reply(_("The \002{0} CLEAR\002 command removes all auto kills from the auto kill list."),
+ source.command);
+ else
+ {
+ CommandInfo *help = source.service->FindCommand("generic/help");
+ source.Reply(_("Allows manipulating the AKILL list."
+ " If a user matching an AKILL mask connects, services will issue a kill the user and prevent them from reconnecting.\n"
+ "\n"
+ "The \002ADD\002 command adds \037mask\037 to the auto kill list.\n"
+ "\002{msg}{service} {help} {command} ADD\002 for more information.\n"
+ "\n"
+ "The \002DEL\002 command removes \037mask\037 from the auto kill list.\n"
+ "\002{msg}{service} {help} {command} DEL\002 for more information.\n"
+ "\n"
+ "The \002LIST\002 and \002VIEW\002 commands both show the auto kill list, but \002VIEW\002 also shows who created the auto kill entry, when it was created, and when it expires.\n"
+ "\002{msg}{service} {help} {command} [LIST | VIEW]\002 for more information.\n"
+ "\n"
+ "The \002CLEAR\002 command clears th auto kill list."
+ "\002{msg}{service} {help} {command} CLEAR\002 for more information.\n"),
+ "msg"_kw = Config->StrictPrivmsg, "service"_kw = source.service->nick, "help"_kw = help->cname, "command"_kw = source.command);
+ }
+ return true;
+ }
+};
+
+class OSAKill : public Module
+{
+ CommandOSAKill commandosakill;
+
+ public:
+ OSAKill(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandosakill(this)
+ {
+
+ }
+};
+
+MODULE_INIT(OSAKill)
diff --git a/modules/operserv/chankill.cpp b/modules/operserv/chankill.cpp
new file mode 100644
index 000000000..9ab820821
--- /dev/null
+++ b/modules/operserv/chankill.cpp
@@ -0,0 +1,134 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "module.h"
+
+class CommandOSChanKill : public Command
+{
+ ServiceReference<XLineManager> akills;
+
+ public:
+ CommandOSChanKill(Module *creator) : Command(creator, "operserv/chankill", 2, 3)
+ , akills("xlinemanager/sgline")
+ {
+ this->SetDesc(_("AKILL all users on a specific channel"));
+ this->SetSyntax(_("[+\037expiry\037] \037channel\037 \037reason\037"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ if (!akills)
+ return;
+
+ Anope::string expiry, channel;
+ unsigned last_param = 1;
+
+ channel = params[0];
+ if (!channel.empty() && channel[0] == '+')
+ {
+ expiry = channel;
+ channel = params[1];
+ last_param = 2;
+ }
+
+ time_t expires = !expiry.empty() ? Anope::DoTime(expiry) : Config->GetModule("operserv")->Get<time_t>("autokillexpiry", "30d");
+ if (!expiry.empty() && isdigit(expiry[expiry.length() - 1]))
+ expires *= 86400;
+
+ if (expires)
+ {
+ if (expires < 60)
+ {
+ source.Reply(_("Invalid expiry time \002{0}\002."), expiry);
+ return;
+ }
+
+ expires += Anope::CurTime;
+ }
+
+ if (params.size() <= last_param)
+ {
+ this->OnSyntaxError(source, "");
+ return;
+ }
+
+ Anope::string reason = params[last_param];
+ if (params.size() > last_param + 1)
+ reason += params[last_param + 1];
+
+ Anope::string realreason;
+ if (Config->GetModule("operserv")->Get<bool>("addakiller") && !source.GetNick().empty())
+ realreason = "[" + source.GetNick() + "] " + reason;
+ else
+ realreason = reason;
+
+ Channel *c = Channel::Find(channel);
+ if (!c)
+ {
+ source.Reply(_("Channel \002{0}\002 doesn't exist."), channel);
+ return;
+ }
+
+ for (Channel::ChanUserList::iterator it = c->users.begin(), it_end = c->users.end(); it != it_end; ++it)
+ {
+ ChanUserContainer *uc = it->second;
+
+ if (uc->user->server == Me || uc->user->HasMode("OPER"))
+ continue;
+
+ Anope::string akillmask = "*@" + uc->user->host;
+
+ if (akills->HasEntry(akillmask))
+ continue;
+
+ XLine *x = Serialize::New<XLine *>();
+ x->SetMask(akillmask);
+ x->SetBy(source.GetNick());
+ x->SetExpires(expires);
+ x->SetReason(realreason);
+ x->SetID(XLineManager::GenerateUID());
+
+ akills->AddXLine(x);
+ akills->OnMatch(uc->user, x);
+ }
+
+ Log(LOG_ADMIN, source, this) << "on " << c->name << " (" << realreason << ")";
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Adds an auto kill for every user on \037channel\037, except for IRC operators."));
+ return true;
+ }
+};
+
+class OSChanKill : public Module
+{
+ CommandOSChanKill commandoschankill;
+
+ public:
+ OSChanKill(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandoschankill(this)
+ {
+
+ }
+};
+
+MODULE_INIT(OSChanKill)
diff --git a/modules/commands/os_config.cpp b/modules/operserv/config.cpp
index a2e4f3272..3935b4899 100644
--- a/modules/commands/os_config.cpp
+++ b/modules/operserv/config.cpp
@@ -1,12 +1,20 @@
-/* OperServ core functions
+/*
+ * Anope IRC Services
*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2011-2016 Anope Team <team@anope.org>
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
*
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "module.h"
@@ -20,7 +28,7 @@ class CommandOSConfig : public Command
this->SetSyntax(_("{\037MODIFY\037|\037VIEW\037} [\037block name\037 \037item name\037 \037item value\037]"));
}
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
{
const Anope::string &what = params[0];
@@ -28,7 +36,7 @@ class CommandOSConfig : public Command
{
if (!source.HasPriv("operserv/config"))
{
- source.Reply(ACCESS_DENIED);
+ source.Reply(_("Access denied. You do not have the operator privilege \002{0}\002."), "operserv/config");
return;
}
@@ -38,14 +46,14 @@ class CommandOSConfig : public Command
if (!block)
{
- source.Reply(_("There is no such configuration block %s."), params[1].c_str());
+ source.Reply(_("There is no such configuration block \002{0}\002."), params[1]);
return;
}
block->Set(params[2], params[3]);
Log(LOG_ADMIN, source, this) << "to change the configuration value of " << params[1] << ":" << params[2] << " to " << params[3];
- source.Reply(_("Value of %s:%s changed to %s"), params[1].c_str(), params[2].c_str(), params[3].c_str());
+ source.Reply(_("Value of \002{0}:{1}\002 changed to \002{2}\002."), params[1], params[2], params[3]);
}
else if (what.equals_ci("VIEW"))
{
@@ -53,7 +61,7 @@ class CommandOSConfig : public Command
const Anope::string show_blocks[] = { "serverinfo", "networkinfo", "options", "" };
Log(LOG_ADMIN, source, this) << "VIEW";
-
+
for (unsigned i = 0; !show_blocks[i].empty(); ++i)
{
Configuration::Block *block = Config->GetBlock(show_blocks[i]);
@@ -76,7 +84,7 @@ class CommandOSConfig : public Command
std::vector<Anope::string> replies;
lflist.Process(replies);
- source.Reply(_("%s settings:"), block->GetName().c_str());
+ source.Reply(_("%s settings:"), block->GetName());
for (unsigned j = 0; j < replies.size(); ++j)
source.Reply(replies[j]);
@@ -120,17 +128,14 @@ class CommandOSConfig : public Command
this->OnSyntaxError(source, what);
}
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
{
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Allows you to change and view configuration settings.\n"
- "Settings changed by this command are temporary and will not be reflected\n"
- "back into the configuration file, and will be lost if Anope is shut down,\n"
- "restarted, or the configuration is reloaded.\n"
- " \n"
- "Example:\n"
- " \002MODIFY nickserv forcemail no\002"));
+ source.Reply(_("Allows you to change and view configuration settings. Settings changed by this command are temporary and will not be reflected back into the configuration file, and will be lost if Anope is shut down, restarted, or the configuration is reloaded.\n"
+ "Use of the \002MODIFY\002 command requires the \002{0}\002 operator privilege.\n"
+ "\n"
+ "Example:\n"
+ " {0} MODIFY nickserv forcemail no"
+ " Changes the \"forceemail\" setting of the module configuration for \"nickserv\" to \"no\""));
return true;
}
};
@@ -140,8 +145,8 @@ class OSConfig : public Module
CommandOSConfig commandosconfig;
public:
- OSConfig(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandosconfig(this)
+ OSConfig(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandosconfig(this)
{
}
diff --git a/modules/commands/os_defcon.cpp b/modules/operserv/defcon.cpp
index fbccf2aae..d7f95d0e7 100644
--- a/modules/commands/os_defcon.cpp
+++ b/modules/operserv/defcon.cpp
@@ -1,16 +1,26 @@
-/* OperServ core functions
+/*
+ * Anope IRC Services
*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
*
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "module.h"
-#include "modules/os_session.h"
+#include "modules/operserv/session.h"
+#include "modules/operserv/defcon.h"
+#include "modules/global.h"
enum DefconLevel
{
@@ -79,19 +89,19 @@ struct DefconConfig
{
DefConModesOnParams.erase(name);
}
-
+
bool GetDefConParam(const Anope::string &name, Anope::string &buf)
{
std::map<Anope::string, Anope::string>::iterator it = DefConModesOnParams.find(name);
-
+
buf.clear();
-
+
if (it != DefConModesOnParams.end())
{
buf = it->second;
return true;
}
-
+
return false;
}
};
@@ -101,13 +111,12 @@ static DefconConfig DConfig;
static void runDefCon();
static Anope::string defconReverseModes(const Anope::string &modes);
-static ServiceReference<GlobalService> GlobalService("GlobalService", "Global");
-
static Timer *timeout;
class DefConTimeout : public Timer
{
int level;
+ ServiceReference<Global::GlobalService> global;
public:
DefConTimeout(Module *mod, int newlevel) : Timer(mod, DConfig.timeout), level(newlevel)
@@ -120,23 +129,23 @@ class DefConTimeout : public Timer
timeout = NULL;
}
- void Tick(time_t) anope_override
+ void Tick(time_t) override
{
if (DConfig.defaultlevel != level)
{
DConfig.defaultlevel = level;
- FOREACH_MOD(OnDefconLevel, (level));
+ EventManager::Get()->Dispatch(&Event::DefconLevel::OnDefconLevel, level);
Log(Config->GetClient("OperServ"), "operserv/defcon") << "Defcon level timeout, returning to level " << level;
- if (DConfig.globalondefcon)
+ if (DConfig.globalondefcon && global)
{
if (!DConfig.offmessage.empty())
- GlobalService->SendGlobal(NULL, "", DConfig.offmessage);
+ global->SendGlobal(NULL, "", DConfig.offmessage);
else
- GlobalService->SendGlobal(NULL, "", Anope::printf(Language::Translate(_("The Defcon level is now at: \002%d\002")), DConfig.defaultlevel));
+ global->SendGlobal(NULL, "", Anope::printf(Language::Translate(_("The Defcon level is now at: \002%d\002")), DConfig.defaultlevel));
if (!DConfig.message.empty())
- GlobalService->SendGlobal(NULL, "", DConfig.message);
+ global->SendGlobal(NULL, "", DConfig.message);
}
runDefCon();
@@ -146,6 +155,8 @@ class DefConTimeout : public Timer
class CommandOSDefcon : public Command
{
+ ServiceReference<Global::GlobalService> global;
+
void SendLevels(CommandSource &source)
{
if (DConfig.Check(DEFCON_NO_NEW_CHANNELS))
@@ -155,9 +166,9 @@ class CommandOSDefcon : public Command
if (DConfig.Check(DEFCON_NO_MLOCK_CHANGE))
source.Reply(_("* No mode lock changes"));
if (DConfig.Check(DEFCON_FORCE_CHAN_MODES) && !DConfig.chanmodes.empty())
- source.Reply(_("* Force channel modes (%s) to be set on all channels"), DConfig.chanmodes.c_str());
+ source.Reply(_("* Force channel modes (\002{0}\002) to be set on all channels"), DConfig.chanmodes);
if (DConfig.Check(DEFCON_REDUCE_SESSION))
- source.Reply(_("* Use the reduced session limit of %d"), DConfig.sessionlimit);
+ source.Reply(_("* Use the reduced session limit of \002{0}\002"), DConfig.sessionlimit);
if (DConfig.Check(DEFCON_NO_NEW_CLIENTS))
source.Reply(_("* Kill any new clients connecting"));
if (DConfig.Check(DEFCON_OPER_ONLY))
@@ -177,13 +188,13 @@ class CommandOSDefcon : public Command
this->SetSyntax(_("[\0021\002|\0022\002|\0023\002|\0024\002|\0025\002]"));
}
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
{
const Anope::string &lvl = params[0];
if (lvl.empty())
{
- source.Reply(_("Services are now at DEFCON \002%d\002."), DConfig.defaultlevel);
+ source.Reply(_("Services are now at defcon \002{0}\002."), DConfig.defaultlevel);
this->SendLevels(source);
return;
}
@@ -203,48 +214,50 @@ class CommandOSDefcon : public Command
DConfig.defaultlevel = newLevel;
- FOREACH_MOD(OnDefconLevel, (newLevel));
+ EventManager::Get()->Dispatch(&Event::DefconLevel::OnDefconLevel, newLevel);
delete timeout;
if (DConfig.timeout)
timeout = new DefConTimeout(this->module, 5);
- source.Reply(_("Services are now at DEFCON \002%d\002."), DConfig.defaultlevel);
+ source.Reply(_("Services are now at defcon \002{0}\002."), DConfig.defaultlevel);
this->SendLevels(source);
Log(LOG_ADMIN, source, this) << "to change defcon level to " << newLevel;
/* Global notice the user what is happening. Also any Message that
the Admin would like to add. Set in config file. */
- if (DConfig.globalondefcon)
+ if (DConfig.globalondefcon && global)
{
if (DConfig.defaultlevel == 5 && !DConfig.offmessage.empty())
- GlobalService->SendGlobal(NULL, "", DConfig.offmessage);
+ global->SendGlobal(NULL, "", DConfig.offmessage);
else if (DConfig.defaultlevel != 5)
{
- GlobalService->SendGlobal(NULL, "", Anope::printf(_("The Defcon level is now at: \002%d\002"), DConfig.defaultlevel));
+ global->SendGlobal(NULL, "", Anope::printf(_("The defcon level is now at \002%d\002"), DConfig.defaultlevel));
if (!DConfig.message.empty())
- GlobalService->SendGlobal(NULL, "", DConfig.message);
+ global->SendGlobal(NULL, "", DConfig.message);
}
}
/* Run any defcon functions, e.g. FORCE CHAN MODE */
runDefCon();
- return;
}
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
{
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("The defcon system can be used to implement a pre-defined\n"
- "set of restrictions to services useful during an attempted\n"
- "attack on the network."));
+ source.Reply(_("The defcon system can be used to implement a pre-defined set of restrictions to services useful during an attempted attack on the network."));
+ // XXX actually explain what this does
return true;
}
};
class OSDefcon : public Module
+ , public EventHook<Event::ChannelModeSet>
+ , public EventHook<Event::ChannelModeUnset>
+ , public EventHook<Event::PreCommand>
+ , public EventHook<Event::UserConnect>
+ , public EventHook<Event::ChannelModeAdd>
+ , public EventHook<Event::ChannelSync>
{
ServiceReference<SessionService> session_service;
ServiceReference<XLineManager> akills;
@@ -324,42 +337,55 @@ class OSDefcon : public Module
if ((cm = ModeManager::FindChannelModeByName("REDIRECT")) && DConfig.DefConModesOn.count(cm->name) && !DConfig.DefConModesOn.count("LIMIT"))
{
DConfig.DefConModesOn.erase("REDIRECT");
-
+
Log(this) << "DefConChanModes must lock mode +l as well to lock mode +L";
}
}
public:
- OSDefcon(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), session_service("SessionService", "session"), akills("XLineManager", "xlinemanager/sgline"), commandosdefcon(this)
+ OSDefcon(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , EventHook<Event::ChannelModeSet>(this)
+ , EventHook<Event::ChannelModeUnset>(this)
+ , EventHook<Event::PreCommand>(this)
+ , EventHook<Event::UserConnect>(this)
+ , EventHook<Event::ChannelModeAdd>(this)
+ , EventHook<Event::ChannelSync>(this)
+ , akills("xlinemanager/sgline")
+ , commandosdefcon(this)
{
}
- void OnReload(Configuration::Conf *conf) anope_override
+ ~OSDefcon()
+ {
+ delete timeout;
+ }
+
+ void OnReload(Configuration::Conf *conf) override
{
Configuration::Block *block = conf->GetModule(this);
DefconConfig dconfig;
dconfig.defaultlevel = block->Get<int>("defaultlevel");
- dconfig.defcons[4] = block->Get<const Anope::string>("level4");
- dconfig.defcons[3] = block->Get<const Anope::string>("level3");
- dconfig.defcons[2] = block->Get<const Anope::string>("level2");
- dconfig.defcons[1] = block->Get<const Anope::string>("level1");
+ dconfig.defcons[4] = block->Get<Anope::string>("level4");
+ dconfig.defcons[3] = block->Get<Anope::string>("level3");
+ dconfig.defcons[2] = block->Get<Anope::string>("level2");
+ dconfig.defcons[1] = block->Get<Anope::string>("level1");
dconfig.sessionlimit = block->Get<int>("sessionlimit");
- dconfig.akillreason = block->Get<const Anope::string>("akillreason");
+ dconfig.akillreason = block->Get<Anope::string>("akillreason");
dconfig.akillexpire = block->Get<time_t>("akillexpire");
- dconfig.chanmodes = block->Get<const Anope::string>("chanmodes");
+ dconfig.chanmodes = block->Get<Anope::string>("chanmodes");
dconfig.timeout = block->Get<time_t>("timeout");
dconfig.globalondefcon = block->Get<bool>("globalondefcon");
- dconfig.message = block->Get<const Anope::string>("message");
- dconfig.offmessage = block->Get<const Anope::string>("offmessage");
+ dconfig.message = block->Get<Anope::string>("message");
+ dconfig.offmessage = block->Get<Anope::string>("offmessage");
block = conf->GetModule("os_session");
dconfig.max_session_kill = block->Get<int>("maxsessionkill");
dconfig.session_autokill_expiry = block->Get<time_t>("sessionautokillexpiry");
- dconfig.sle_reason = block->Get<const Anope::string>("sessionlimitexceeded");
- dconfig.sle_detailsloc = block->Get<const Anope::string>("sessionlimitdetailsloc");
+ dconfig.sle_reason = block->Get<Anope::string>("sessionlimitexceeded");
+ dconfig.sle_detailsloc = block->Get<Anope::string>("sessionlimitdetailsloc");
if (dconfig.defaultlevel < 1 || dconfig.defaultlevel > 5)
throw ConfigException("The value for <defcon:defaultlevel> must be between 1 and 5");
@@ -406,7 +432,7 @@ class OSDefcon : public Module
this->ParseModeString();
}
- EventReturn OnChannelModeSet(Channel *c, MessageSource &source, ChannelMode *mode, const Anope::string &param) anope_override
+ EventReturn OnChannelModeSet(Channel *c, const MessageSource &source, ChannelMode *mode, const Anope::string &param) override
{
if (DConfig.Check(DEFCON_FORCE_CHAN_MODES) && DConfig.DefConModesOff.count(mode->name) && source.GetUser() && !source.GetBot())
{
@@ -418,7 +444,7 @@ class OSDefcon : public Module
return EVENT_CONTINUE;
}
- EventReturn OnChannelModeUnset(Channel *c, MessageSource &source, ChannelMode *mode, const Anope::string &) anope_override
+ EventReturn OnChannelModeUnset(Channel *c, const MessageSource &source, ChannelMode *mode, const Anope::string &) override
{
if (DConfig.Check(DEFCON_FORCE_CHAN_MODES) && DConfig.DefConModesOn.count(mode->name) && source.GetUser() && !source.GetBot())
{
@@ -436,7 +462,7 @@ class OSDefcon : public Module
return EVENT_CONTINUE;
}
- EventReturn OnPreCommand(CommandSource &source, Command *command, std::vector<Anope::string> &params) anope_override
+ EventReturn OnPreCommand(CommandSource &source, Command *command, std::vector<Anope::string> &params) override
{
if (DConfig.Check(DEFCON_OPER_ONLY) && !source.IsOper())
{
@@ -447,35 +473,35 @@ class OSDefcon : public Module
{
return EVENT_STOP;
}
- else if (command->name == "nickserv/register" || command->name == "nickserv/group")
+ else if (command->GetName() == "nickserv/register" || command->GetName() == "nickserv/group")
{
if (DConfig.Check(DEFCON_NO_NEW_NICKS))
{
- source.Reply(_("Services are in DefCon mode, please try again later."));
+ source.Reply(_("Services are in defcon mode, please try again later."));
return EVENT_STOP;
}
}
- else if (command->name == "chanserv/mode" && params.size() > 1 && params[1].equals_ci("LOCK"))
+ else if (command->GetName() == "chanserv/mode" && params.size() > 1 && params[1].equals_ci("LOCK"))
{
if (DConfig.Check(DEFCON_NO_MLOCK_CHANGE))
{
- source.Reply(_("Services are in DefCon mode, please try again later."));
+ source.Reply(_("Services are in defcon mode, please try again later."));
return EVENT_STOP;
}
}
- else if (command->name == "chanserv/register")
+ else if (command->GetName() == "chanserv/register")
{
if (DConfig.Check(DEFCON_NO_NEW_CHANNELS))
{
- source.Reply(_("Services are in DefCon mode, please try again later."));
+ source.Reply(_("Services are in defcon mode, please try again later."));
return EVENT_STOP;
}
}
- else if (command->name == "memoserv/send")
+ else if (command->GetName() == "memoserv/send")
{
if (DConfig.Check(DEFCON_NO_NEW_MEMOS))
{
- source.Reply(_("Services are in DefCon mode, please try again later."));
+ source.Reply(_("Services are in defcon mode, please try again later."));
return EVENT_STOP;
}
}
@@ -483,17 +509,20 @@ class OSDefcon : public Module
return EVENT_CONTINUE;
}
- void OnUserConnect(User *u, bool &exempt) anope_override
+ void OnUserConnect(User *u, bool &exempt) override
{
if (exempt || u->Quitting() || !u->server->IsSynced() || u->server->IsULined())
return;
- BotInfo *OperServ = Config->GetClient("OperServ");
+ ServiceBot *OperServ = Config->GetClient("OperServ");
if (DConfig.Check(DEFCON_AKILL_NEW_CLIENTS) && akills)
{
Log(OperServ, "operserv/defcon") << "DEFCON: adding akill for *@" << u->host;
+#warning "xline allocated on stack"
+#if 0
XLine x("*@" + u->host, OperServ ? OperServ->nick : "defcon", Anope::CurTime + DConfig.akillexpire, DConfig.akillreason, XLineManager::GenerateUID());
akills->Send(NULL, &x);
+#endif
}
if (DConfig.Check(DEFCON_NO_NEW_CLIENTS) || DConfig.Check(DEFCON_AKILL_NEW_CLIENTS))
@@ -506,9 +535,9 @@ class OSDefcon : public Module
return;
Session *session = session_service->FindSession(u->ip.addr());
- Exception *exception = session_service->FindException(u);
+ Exception *e = session_service->FindException(u);
- if (DConfig.Check(DEFCON_REDUCE_SESSION) && !exception)
+ if (DConfig.Check(DEFCON_REDUCE_SESSION) && !e)
{
if (session && session->count > static_cast<unsigned>(DConfig.sessionlimit))
{
@@ -523,9 +552,12 @@ class OSDefcon : public Module
++session->hits;
if (akills && DConfig.max_session_kill && session->hits >= DConfig.max_session_kill)
{
+#warning "xline allocated on stack"
+#if 0
XLine x("*@" + session->addr.mask(), OperServ ? OperServ->nick : "", Anope::CurTime + DConfig.session_autokill_expiry, "Defcon session limit exceeded", XLineManager::GenerateUID());
akills->Send(NULL, &x);
Log(OperServ, "akill/defcon") << "[DEFCON] Added a temporary AKILL for \002*@" << session->addr.mask() << "\002 due to excessive connections";
+#endif
}
else
{
@@ -535,13 +567,13 @@ class OSDefcon : public Module
}
}
- void OnChannelModeAdd(ChannelMode *cm) anope_override
+ void OnChannelModeAdd(ChannelMode *cm) override
{
if (DConfig.chanmodes.find(cm->mchar) != Anope::string::npos)
this->ParseModeString();
}
- void OnChannelSync(Channel *c) anope_override
+ void OnChannelSync(Channel *c) override
{
if (DConfig.Check(DEFCON_FORCE_CHAN_MODES))
c->SetModes(Config->GetClient("OperServ"), false, "%s", DConfig.chanmodes.c_str());
@@ -550,7 +582,7 @@ class OSDefcon : public Module
static void runDefCon()
{
- BotInfo *OperServ = Config->GetClient("OperServ");
+ ServiceBot *OperServ = Config->GetClient("OperServ");
if (DConfig.Check(DEFCON_FORCE_CHAN_MODES))
{
if (!DConfig.chanmodes.empty() && !DefConModesSet)
diff --git a/modules/operserv/dns.cpp b/modules/operserv/dns.cpp
new file mode 100644
index 000000000..af07a2499
--- /dev/null
+++ b/modules/operserv/dns.cpp
@@ -0,0 +1,1051 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/dns.h"
+#include "modules/operserv/dns.h"
+
+static std::map<Anope::string, std::list<time_t> > server_quit_times;
+
+class DNSZoneImpl : public DNSZone
+{
+ friend class DNSZoneType;
+
+ Anope::string name;
+
+ public:
+ DNSZoneImpl(Serialize::TypeBase *type) : DNSZone(type) { }
+ DNSZoneImpl(Serialize::TypeBase *type, Serialize::ID id) : DNSZone(type, id) { }
+
+ Anope::string GetName() override;
+ void SetName(const Anope::string &) override;
+
+ static DNSZone *Find(const Anope::string &name)
+ {
+ for (DNSZone *zone : Serialize::GetObjects<DNSZone *>())
+ if (zone->GetName().equals_ci(name))
+ return zone;
+ return nullptr;
+ }
+};
+
+class DNSZoneType : public Serialize::Type<DNSZoneImpl>
+{
+ public:
+ Serialize::Field<DNSZoneImpl, Anope::string> name;
+
+ DNSZoneType(Module *creator) : Serialize::Type<DNSZoneImpl>(creator)
+ , name(this, "name", &DNSZoneImpl::name)
+ {
+ }
+};
+
+Anope::string DNSZoneImpl::GetName()
+{
+ return Get(&DNSZoneType::name);
+}
+
+void DNSZoneImpl::SetName(const Anope::string &name)
+{
+ Set(&DNSZoneType::name, name);
+}
+
+class DNSServerImpl : public DNSServer
+{
+ friend class DNSServerType;
+
+ ServiceReference<DNS::Manager> manager;
+
+ DNSZone *zone = nullptr;
+ Anope::string name;
+ unsigned int limit = 0;
+ bool pooled = false;
+
+ /* is actually in the pool */
+ bool active = false;
+
+ public:
+ std::set<Anope::string, ci::less> zones;
+ time_t repool = 0;
+
+ DNSServerImpl(Serialize::TypeBase *type) : DNSServer(type) { }
+ DNSServerImpl(Serialize::TypeBase *type, Serialize::ID id) : DNSServer(type, id) { }
+
+ DNSZone *GetZone() override;
+ void SetZone(DNSZone *) override;
+
+ Anope::string GetName() override;
+ void SetName(const Anope::string &) override;
+
+ unsigned int GetLimit() override;
+ void SetLimit(const unsigned int &) override;
+
+ bool GetPooled() override;
+ void SetPool(const bool &) override;
+
+ bool Active()
+ {
+ return GetPooled() && active;
+ }
+
+ void SetActive(bool p)
+ {
+ if (p)
+ this->SetPool(p);
+ active = p;
+
+ if (manager)
+ {
+ manager->UpdateSerial();
+ for (std::set<Anope::string, ci::less>::iterator it = zones.begin(), it_end = zones.end(); it != it_end; ++it)
+ manager->Notify(*it);
+ }
+ }
+
+ static DNSServerImpl *Find(const Anope::string &s)
+ {
+ for (DNSServerImpl *server : Serialize::GetObjects<DNSServerImpl *>())
+ if (server->GetName().equals_ci(s))
+ return server;
+ return nullptr;
+ }
+};
+
+class DNSServerType : public Serialize::Type<DNSServerImpl>
+{
+ public:
+ Serialize::ObjectField<DNSServerImpl, DNSZone *> zone;
+ Serialize::Field<DNSServerImpl, Anope::string> name;
+ Serialize::Field<DNSServerImpl, unsigned int> limit;
+ Serialize::Field<DNSServerImpl, bool> pooled;
+
+ DNSServerType(Module *creator) : Serialize::Type<DNSServerImpl>(creator)
+ , zone(this, "zone", &DNSServerImpl::zone)
+ , name(this, "name", &DNSServerImpl::name)
+ , limit(this, "limit", &DNSServerImpl::limit)
+ , pooled(this, "pooled", &DNSServerImpl::pooled)
+ {
+ }
+};
+
+DNSZone *DNSServerImpl::GetZone()
+{
+ return Get(&DNSServerType::zone);
+}
+
+void DNSServerImpl::SetZone(DNSZone *z)
+{
+ Set(&DNSServerType::zone, z);
+}
+
+Anope::string DNSServerImpl::GetName()
+{
+ return Get(&DNSServerType::name);
+}
+
+void DNSServerImpl::SetName(const Anope::string &n)
+{
+ Set(&DNSServerType::name, n);
+}
+
+unsigned int DNSServerImpl::GetLimit()
+{
+ return Get(&DNSServerType::limit);
+}
+
+void DNSServerImpl::SetLimit(const unsigned int &l)
+{
+ Set(&DNSServerType::limit, l);
+}
+
+bool DNSServerImpl::GetPooled()
+{
+ return Get(&DNSServerType::pooled);
+}
+
+void DNSServerImpl::SetPool(const bool &p)
+{
+ Set(&DNSServerType::pooled, p);
+}
+
+class DNSZoneMembershipImpl : public DNSZoneMembership
+{
+ friend class DNSZoneMembershipType;
+
+ DNSServer *server = nullptr;
+ DNSZone *zone = nullptr;
+
+ public:
+ DNSZoneMembershipImpl(Serialize::TypeBase *type) : DNSZoneMembership(type) { }
+ DNSZoneMembershipImpl(Serialize::TypeBase *type, Serialize::ID id) : DNSZoneMembership(type, id) { }
+
+ DNSServer *GetServer() override;
+ void SetServer(DNSServer *) override;
+
+ DNSZone *GetZone() override;
+ void SetZone(DNSZone *) override;
+
+ static DNSZoneMembership *Find(DNSServer *server, DNSZone *zone)
+ {
+ for (DNSZoneMembership *mem : Serialize::GetObjects<DNSZoneMembership *>())
+ if (mem->GetServer() == server && mem->GetZone() == zone)
+ return mem;
+ return nullptr;
+ }
+};
+
+class DNSZoneMembershipType : public Serialize::Type<DNSZoneMembershipImpl>
+{
+ public:
+ Serialize::ObjectField<DNSZoneMembershipImpl, DNSServer *> server;
+ Serialize::ObjectField<DNSZoneMembershipImpl, DNSZone *> zone;
+
+ DNSZoneMembershipType(Module *creator) : Serialize::Type<DNSZoneMembershipImpl>(creator)
+ , server(this, "server", &DNSZoneMembershipImpl::server)
+ , zone(this, "zone", &DNSZoneMembershipImpl::zone)
+ {
+ }
+};
+
+DNSServer *DNSZoneMembershipImpl::GetServer()
+{
+ return Get(&DNSZoneMembershipType::server);
+}
+
+void DNSZoneMembershipImpl::SetServer(DNSServer *s)
+{
+ Set(&DNSZoneMembershipType::server, s);
+}
+
+DNSZone *DNSZoneMembershipImpl::GetZone()
+{
+ return Get(&DNSZoneMembershipType::zone);
+}
+
+void DNSZoneMembershipImpl::SetZone(DNSZone *z)
+{
+ Set(&DNSZoneMembershipType::zone, z);
+}
+
+class DNSIPImpl : public DNSIP
+{
+ friend class DNSIPType;
+
+ DNSServer *server = nullptr;
+ Anope::string ip;
+
+ public:
+ DNSIPImpl(Serialize::TypeBase *type) : DNSIP(type) { }
+ DNSIPImpl(Serialize::TypeBase *type, Serialize::ID id) : DNSIP(type, id) { }
+
+ DNSServer *GetServer() override;
+ void SetServer(DNSServer *) override;
+
+ Anope::string GetIP() override;
+ void SetIP(const Anope::string &) override;
+};
+
+class DNSIPType : public Serialize::Type<DNSIPImpl>
+{
+ public:
+ Serialize::ObjectField<DNSIPImpl, DNSServer *> server;
+ Serialize::Field<DNSIPImpl, Anope::string> ip;
+
+ DNSIPType(Module *creator) : Serialize::Type<DNSIPImpl>(creator)
+ , server(this, "server", &DNSIPImpl::server)
+ , ip(this, "ip", &DNSIPImpl::ip)
+ {
+ }
+};
+
+DNSServer *DNSIPImpl::GetServer()
+{
+ return Get(&DNSIPType::server);
+}
+
+void DNSIPImpl::SetServer(DNSServer *s)
+{
+ Set(&DNSIPType::server, s);
+}
+
+Anope::string DNSIPImpl::GetIP()
+{
+ return Get(&DNSIPType::ip);
+}
+
+void DNSIPImpl::SetIP(const Anope::string &ip)
+{
+ Set(&DNSIPType::ip, ip);
+}
+
+class CommandOSDNS : public Command
+{
+ ServiceReference<DNS::Manager> manager;
+
+ void DisplayPoolState(CommandSource &source)
+ {
+ std::vector<DNSServerImpl *> servers = Serialize::GetObjects<DNSServerImpl *>();
+
+ if (servers.empty())
+ {
+ source.Reply(_("There are no configured servers."));
+ return;
+ }
+
+ ListFormatter lf(source.GetAccount());
+ lf.AddColumn(_("Server")).AddColumn(_("IP")).AddColumn(_("Limit")).AddColumn(_("State"));
+ for (DNSServerImpl *s : servers)
+ {
+ Server *srv = Server::Find(s->GetName(), true);
+
+ ListFormatter::ListEntry entry;
+ entry["Server"] = s->GetName();
+ entry["Limit"] = s->GetLimit() ? stringify(s->GetLimit()) : Language::Translate(source.GetAccount(), _("None"));
+
+ Anope::string ip_str;
+ for (DNSIP *ip : s->GetRefs<DNSIP *>())
+ ip_str += ip->GetIP() + " ";
+ ip_str.trim();
+ if (ip_str.empty())
+ ip_str = "None";
+ entry["IP"] = ip_str;
+
+ if (s->Active())
+ entry["State"] = Language::Translate(source.GetAccount(), _("Pooled/Active"));
+ else if (s->GetPooled())
+ entry["State"] = Language::Translate(source.GetAccount(), _("Pooled/Not Active"));
+ else
+ entry["State"] = Language::Translate(source.GetAccount(), _("Unpooled"));
+
+ if (!srv)
+ entry["State"] += Anope::string(" ") + Language::Translate(source.GetAccount(), _("(Split)"));
+
+ lf.AddEntry(entry);
+ }
+
+ std::vector<Anope::string> replies;
+ lf.Process(replies);
+
+ std::vector<DNSZone *> zones = Serialize::GetObjects<DNSZone *>();
+ if (!zones.empty())
+ {
+ ListFormatter lf2(source.GetAccount());
+ lf2.AddColumn(_("Zone")).AddColumn(_("Servers"));
+
+ for (DNSZone *z : zones)
+ {
+ ListFormatter::ListEntry entry;
+ entry["Zone"] = z->GetName();
+
+ Anope::string server_str;
+ for (DNSServer *s : z->GetRefs<DNSServer *>())
+ server_str += s->GetName() + " ";
+ server_str.trim();
+
+ if (server_str.empty())
+ server_str = "None";
+
+ entry["Servers"] = server_str;
+
+ lf2.AddEntry(entry);
+ }
+
+ lf2.Process(replies);
+ }
+
+ for (const Anope::string &r : replies)
+ source.Reply(r);
+ }
+
+ void AddZone(CommandSource &source, const std::vector<Anope::string> &params)
+ {
+ const Anope::string &zone = params[1];
+
+ if (DNSZoneImpl::Find(zone))
+ {
+ source.Reply(_("Zone \002{0}\002 already exists."), zone);
+ return;
+ }
+
+ if (Anope::ReadOnly)
+ source.Reply(_("Services are in read-only mode. Any changes made may not persist."));
+
+ Log(LOG_ADMIN, source, this) << "to add zone " << zone;
+
+ DNSZone *z = Serialize::New<DNSZone *>();
+ z->SetName(zone);
+ source.Reply(_("Added zone \002{0}\002."), zone);
+ }
+
+ void DelZone(CommandSource &source, const std::vector<Anope::string> &params)
+ {
+ const Anope::string &zone = params[1];
+
+ DNSZone *z = DNSZoneImpl::Find(zone);
+ if (!z)
+ {
+ source.Reply(_("Zone \002{0}\002 does not exist."), zone);
+ return;
+ }
+
+ if (Anope::ReadOnly)
+ source.Reply(_("Services are in read-only mode. Any changes made may not persist."));
+
+ Log(LOG_ADMIN, source, this) << "to delete zone " << z->GetName();
+
+ for (DNSZoneMembership *mem : z->GetRefs<DNSZoneMembership *>())
+ mem->Delete();
+
+ if (manager)
+ {
+ manager->UpdateSerial();
+ manager->Notify(z->GetName());
+ }
+
+ source.Reply(_("Zone \002{0}\002 removed."), z->GetName());
+ z->Delete();
+ }
+
+ void AddServer(CommandSource &source, const std::vector<Anope::string> &params)
+ {
+ DNSServer *s = DNSServerImpl::Find(params[1]);
+ const Anope::string &zone = params.size() > 2 ? params[2] : "";
+
+ if (s)
+ {
+ if (zone.empty())
+ {
+ source.Reply(_("Server \002{0}\002 already exists."), s->GetName());
+ }
+ else
+ {
+ DNSZone *z = DNSZoneImpl::Find(zone);
+ if (!z)
+ {
+ source.Reply(_("Zone \002{0}\002 does not exist."), zone);
+ return;
+ }
+
+ DNSZoneMembership *mem = DNSZoneMembershipImpl::Find(s, z);
+ if (mem)
+ {
+ source.Reply(_("Server \002{0}\002 is already in zone \002{1}\002."), s->GetName(), z->GetName());
+ return;
+ }
+
+ if (Anope::ReadOnly)
+ source.Reply(_("Services are in read-only mode. Any changes made may not persist."));
+
+ mem = Serialize::New<DNSZoneMembership *>();
+ mem->SetZone(z);
+ mem->SetServer(s);
+
+ if (manager)
+ {
+ manager->UpdateSerial();
+ manager->Notify(zone);
+ }
+
+ Log(LOG_ADMIN, source, this) << "to add server " << s->GetName() << " to zone " << z->GetName();
+
+ source.Reply(_("Server \002{0}\002 added to zone \002{1}\002."), s->GetName(), z->GetName());
+ }
+
+ return;
+ }
+
+ Server *serv = Server::Find(params[1], true);
+ if (!serv || serv == Me || serv->IsJuped())
+ {
+ source.Reply(_("Server \002{0}\002 is not linked to the network."), params[1]);
+ return;
+ }
+
+ s = Serialize::New<DNSServer *>();
+ s->SetName(params[1]);
+ if (zone.empty())
+ {
+ if (Anope::ReadOnly)
+ source.Reply(_("Services are in read-only mode. Any changes made may not persist."));
+
+ Log(LOG_ADMIN, source, this) << "to add server " << s->GetName();
+ source.Reply(_("Added server \002{0}\002."), s->GetName());
+ }
+ else
+ {
+ DNSZone *z = DNSZoneImpl::Find(zone);
+ if (!z)
+ {
+ source.Reply(_("Zone \002{0}\002 does not exist."), zone);
+ s->Delete();
+ return;
+ }
+
+ if (Anope::ReadOnly)
+ source.Reply(_("Services are in read-only mode. Any changes made may not persist."));
+
+ Log(LOG_ADMIN, source, this) << "to add server " << s->GetName() << " to zone " << zone;
+
+ DNSZoneMembership *mem = Serialize::New<DNSZoneMembership *>();
+ mem->SetServer(s);
+ mem->SetZone(z);
+
+ if (manager)
+ {
+ manager->UpdateSerial();
+ manager->Notify(z->GetName());
+ }
+ }
+ }
+
+ void DelServer(CommandSource &source, const std::vector<Anope::string> &params)
+ {
+ DNSServer *s = DNSServerImpl::Find(params[1]);
+ const Anope::string &zone = params.size() > 2 ? params[2] : "";
+
+ if (!s)
+ {
+ source.Reply(_("Server \002{0}\002 does not exist."), params[1]);
+ return;
+ }
+
+ if (!zone.empty())
+ {
+ DNSZone *z = DNSZoneImpl::Find(zone);
+ if (!z)
+ {
+ source.Reply(_("Zone \002{0}\002 does not exist."), zone);
+ return;
+ }
+
+ DNSZoneMembership *mem = DNSZoneMembershipImpl::Find(s, z);
+ if (!mem)
+ {
+ source.Reply(_("Server \002{0}\002 is not in zone \002{1}\002."), s->GetName(), z->GetName());
+ return;
+ }
+
+ if (Anope::ReadOnly)
+ source.Reply(_("Services are in read-only mode. Any changes made may not persist."));
+
+ Log(LOG_ADMIN, source, this) << "to remove server " << s->GetName() << " from zone " << z->GetName();
+
+ if (manager)
+ {
+ manager->UpdateSerial();
+ manager->Notify(z->GetName());
+ }
+
+ mem->Delete();
+ source.Reply(_("Removed server \002{0}\002 from zone \002{1}\002."), s->GetName(), z->GetName());
+ return;
+ }
+
+ if (Server::Find(s->GetName(), true))
+ {
+ source.Reply(_("Server \002{0}\002 must be quit before it can be deleted."), s->GetName());
+ return;
+ }
+
+ for (DNSZoneMembership *mem : s->GetRefs<DNSZoneMembership *>())
+ mem->Delete();
+
+ if (Anope::ReadOnly)
+ source.Reply(_("Services are in read-only mode. Any changes made may not persist."));
+
+ if (manager)
+ manager->UpdateSerial();
+
+ Log(LOG_ADMIN, source, this) << "to delete server " << s->GetName();
+ source.Reply(_("Removed server \002{0}\002."), s->GetName());
+ s->Delete();
+ }
+
+ void AddIP(CommandSource &source, const std::vector<Anope::string> &params)
+ {
+ DNSServerImpl *s = DNSServerImpl::Find(params[1]);
+
+ if (!s)
+ {
+ source.Reply(_("Server \002{0}\002 does not exist."), params[1]);
+ return;
+ }
+
+ for (DNSIP *ip : s->GetRefs<DNSIP *>())
+ if (params[2].equals_ci(ip->GetIP()))
+ {
+ source.Reply(_("IP \002{0}\002 already exists for \002{1}\002."), ip->GetIP(), s->GetName());
+ return;
+ }
+
+ sockaddrs addr(params[2]);
+ if (!addr.valid())
+ {
+ source.Reply(_("\002{0}\002 is not a valid IP address."), params[2]);
+ return;
+ }
+
+ if (Anope::ReadOnly)
+ source.Reply(_("Services are in read-only mode. Any changes made may not persist."));
+
+ DNSIP *ip = Serialize::New<DNSIP *>();
+ ip->SetServer(s);
+ ip->SetIP(params[2]);
+
+ source.Reply(_("Added IP \002{0}\002 to \002{1}\002."), params[2], s->GetName());
+ Log(LOG_ADMIN, source, this) << "to add IP " << params[2] << " to " << s->GetName();
+
+ if (s->Active() && manager)
+ {
+ manager->UpdateSerial();
+ for (DNSZone *zone : s->GetRefs<DNSZone *>())
+ manager->Notify(zone->GetName());
+ }
+ }
+
+ void DelIP(CommandSource &source, const std::vector<Anope::string> &params)
+ {
+ DNSServerImpl *s = DNSServerImpl::Find(params[1]);
+
+ if (!s)
+ {
+ source.Reply(_("Server \002{0}\002 does not exist."), params[1]);
+ return;
+ }
+
+ if (Anope::ReadOnly)
+ source.Reply(_("Services are in read-only mode. Any changes made may not persist."));
+
+ for (DNSIP *ip : s->GetRefs<DNSIP *>())
+ if (params[2].equals_ci(ip->GetIP()))
+ {
+ ip->Delete();
+
+ source.Reply(_("Removed IP \002{0}\002 from \002{1}\002."), params[2], s->GetName());
+ Log(LOG_ADMIN, source, this) << "to remove IP " << params[2] << " from " << s->GetName();
+
+ if (s->GetRefs<DNSIP *>().empty())
+ {
+ s->repool = 0;
+ s->SetPool(false);
+ }
+
+ if (s->Active() && manager)
+ {
+ manager->UpdateSerial();
+ for (DNSZone *zone : s->GetRefs<DNSZone *>())
+ manager->Notify(zone->GetName());
+ }
+
+ return;
+ }
+
+ source.Reply(_("IP \002{0}\002 does not exist for \002{1}\002."), params[2], s->GetName());
+ }
+
+ void OnSet(CommandSource &source, const std::vector<Anope::string> &params)
+ {
+ DNSServer *s = DNSServerImpl::Find(params[1]);
+
+ if (!s)
+ {
+ source.Reply(_("Server \002{0}\002 does not exist."), params[1]);
+ return;
+ }
+
+ if (Anope::ReadOnly)
+ source.Reply(_("Services are in read-only mode. Any changes made may not persist."));
+
+ if (params[2].equals_ci("LIMIT"))
+ {
+ try
+ {
+ unsigned l = convertTo<unsigned>(params[3]);
+ s->SetLimit(l);
+ if (l)
+ source.Reply(_("User limit for \002{0}\002 set to \002{1}\002."), s->GetName(), l);
+ else
+ source.Reply(_("User limit for \002{0}\002 removed."), s->GetName());
+ }
+ catch (const ConvertException &ex)
+ {
+ source.Reply(_("Invalid value for limit. Must be numerical."));
+ }
+ }
+ else
+ source.Reply(_("Unknown SET option."));
+ }
+
+ void OnPool(CommandSource &source, const std::vector<Anope::string> &params)
+ {
+ DNSServerImpl *s = DNSServerImpl::Find(params[1]);
+
+ if (!s)
+ {
+ source.Reply(_("Server \002{0}\002 does not exist."), params[1]);
+ return;
+ }
+
+ if (!Server::Find(s->GetName(), true))
+ {
+ source.Reply(_("Server \002{0}\002 is not currently linked."), s->GetName());
+ return;
+ }
+
+ if (s->GetPooled())
+ {
+ source.Reply(_("Server \002{0}\002 is already pooled."), s->GetName());
+ return;
+ }
+
+ if (s->GetRefs<DNSIP *>().empty())
+ {
+ source.Reply(_("Server \002{0}\002 has no configured IPs."), s->GetName());
+ return;
+ }
+
+ if (Anope::ReadOnly)
+ source.Reply(_("Services are in read-only mode. Any changes made may not persist."));
+
+ s->SetActive(true);
+
+ source.Reply(_("Pooled \002{0}\002."), s->GetName());
+ Log(LOG_ADMIN, source, this) << "to pool " << s->GetName();
+ }
+
+
+ void OnDepool(CommandSource &source, const std::vector<Anope::string> &params)
+ {
+ DNSServer *s = DNSServerImpl::Find(params[1]);
+
+ if (!s)
+ {
+ source.Reply(_("Server \002{0}\002 does not exist."), params[1]);
+ return;
+ }
+
+ if (!s->GetPooled())
+ {
+ source.Reply(_("Server \002{0}\002 is not pooled."), s->GetName());
+ return;
+ }
+
+ if (Anope::ReadOnly)
+ source.Reply(_("Services are in read-only mode. Any changes made may not persist."));
+
+ s->SetPool(false);
+
+ source.Reply(_("Depooled \002{0}\002."), s->GetName());
+ Log(LOG_ADMIN, source, this) << "to depool " << s->GetName();
+ }
+
+ public:
+ CommandOSDNS(Module *creator) : Command(creator, "operserv/dns", 0, 4)
+ {
+ this->SetDesc(_("Manage DNS zones for this network"));
+ this->SetSyntax(_("ADDZONE \037zone.name\037"));
+ this->SetSyntax(_("DELZONE \037zone.name\037"));
+ this->SetSyntax(_("ADDSERVER \037server.name\037 [\037zone.name\037]"));
+ this->SetSyntax(_("DELSERVER \037server.name\037 [\037zone.name\037]"));
+ this->SetSyntax(_("ADDIP \037server.name\037 \037ip\037"));
+ this->SetSyntax(_("DELIP \037server.name\037 \037ip\037"));
+ this->SetSyntax(_("SET \037server.name\037 \037option\037 \037value\037"));
+ this->SetSyntax(_("POOL \037server.name\037"));
+ this->SetSyntax(_("DEPOOL \037server.name\037"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ if (params.empty())
+ this->DisplayPoolState(source);
+ else if (params[0].equals_ci("ADDZONE") && params.size() > 1)
+ this->AddZone(source, params);
+ else if (params[0].equals_ci("DELZONE") && params.size() > 1)
+ this->DelZone(source, params);
+ else if (params[0].equals_ci("ADDSERVER") && params.size() > 1)
+ this->AddServer(source, params);
+ else if (params[0].equals_ci("DELSERVER") && params.size() > 1)
+ this->DelServer(source, params);
+ else if (params[0].equals_ci("ADDIP") && params.size() > 2)
+ this->AddIP(source, params);
+ else if (params[0].equals_ci("DELIP") && params.size() > 2)
+ this->DelIP(source, params);
+ else if (params[0].equals_ci("SET") && params.size() > 3)
+ this->OnSet(source, params);
+ else if (params[0].equals_ci("POOL") && params.size() > 1)
+ this->OnPool(source, params);
+ else if (params[0].equals_ci("DEPOOL") && params.size() > 1)
+ this->OnDepool(source, params);
+ else
+ this->OnSyntaxError(source, "");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("This command allows managing DNS zones used for controlling what servers users are directed to when connecting."
+ " Omitting all parameters prints out the status of the DNS zone.\n"
+ "\n"
+ "\002{0} ADDZONE\002 adds a zone, eg. us.example.com. Servers can then be added to this zone with the \002ADDSERVER\002 command.\n"
+ "\n"
+ "The \002{0} ADDSERVER\002 command adds a server to the given zone."
+ " When a DNS query is done, the zone in question is served if it exists, otherwise all servers in all zones are served."
+ " A server may be in more than one zone.\n"
+ "\n"
+ "The \002{0} ADDIP\002 command associates an IP with a server.\n"
+ " \n"
+ "The \002{0} POOL\002 and \002{0} DEPOOL\002 commands actually add and remove servers to their given zones."),
+ source.command);
+ return true;
+ }
+};
+
+class ModuleDNS : public Module
+ , public EventHook<Event::NewServer>
+ , public EventHook<Event::ServerQuit>
+ , public EventHook<Event::UserConnect>
+ , public EventHook<Event::PreUserLogoff>
+ , public EventHook<Event::DnsRequest>
+{
+ DNSZoneType zonetype;
+ DNSServerType servertype;
+ DNSZoneMembershipType zonemembtype;
+ DNSIPType iptype;
+ CommandOSDNS commandosdns;
+
+ time_t ttl = 0;
+ int user_drop_mark = 0;
+ time_t user_drop_time = 0;
+ time_t user_drop_readd_time = 0;
+ bool remove_split_servers = 0;
+ bool readd_connected_servers = 0;
+
+ time_t last_warn = 0;
+
+ public:
+ ModuleDNS(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, EXTRA | VENDOR)
+ , EventHook<Event::NewServer>(this)
+ , EventHook<Event::ServerQuit>(this)
+ , EventHook<Event::UserConnect>(this)
+ , EventHook<Event::PreUserLogoff>(this)
+ , EventHook<Event::DnsRequest>(this)
+ , zonetype(this)
+ , servertype(this)
+ , zonemembtype(this)
+ , iptype(this)
+ , commandosdns(this)
+ {
+ /* enable active flag for linked servers */
+ std::vector<DNSServerImpl *> servers = Serialize::GetObjects<DNSServerImpl *>();
+
+ for (DNSServerImpl *s : servers)
+ if (s->GetPooled() && Server::Find(s->GetName(), true))
+ s->SetActive(true);
+ }
+
+ ~ModuleDNS()
+ {
+ }
+
+ void OnReload(Configuration::Conf *conf) override
+ {
+ Configuration::Block *block = conf->GetModule(this);
+ this->ttl = block->Get<time_t>("ttl");
+ this->user_drop_mark = block->Get<int>("user_drop_mark");
+ this->user_drop_time = block->Get<time_t>("user_drop_time");
+ this->user_drop_readd_time = block->Get<time_t>("user_drop_readd_time");
+ this->remove_split_servers = block->Get<bool>("remove_split_servers");
+ this->readd_connected_servers = block->Get<bool>("readd_connected_servers");
+ }
+
+ void OnNewServer(Server *s) override
+ {
+ if (s == Me || s->IsJuped())
+ return;
+ if (!Me->IsSynced() || this->readd_connected_servers)
+ {
+ DNSServerImpl *dns = DNSServerImpl::Find(s->GetName());
+ if (dns && dns->GetPooled() && !dns->Active() && !dns->GetRefs<DNSIP *>().empty())
+ {
+ dns->SetActive(true);
+ Log(this) << "Pooling server " << s->GetName();
+ }
+ }
+ }
+
+ void OnServerQuit(Server *s) override
+ {
+ DNSServerImpl *dns = DNSServerImpl::Find(s->GetName());
+ if (remove_split_servers && dns && dns->GetPooled() && dns->Active())
+ {
+ if (readd_connected_servers)
+ dns->SetActive(false); // Will be reactivated when it comes back
+ else
+ dns->SetPool(false); // Otherwise permanently pull this
+ Log(this) << "Depooling delinked server " << s->GetName();
+ }
+ }
+
+ void OnUserConnect(User *u, bool &exempt) override
+ {
+ if (!u->Quitting() && u->server)
+ {
+ DNSServerImpl *s = DNSServerImpl::Find(u->server->GetName());
+ /* Check for user limit reached */
+ if (s && s->GetPooled() && s->Active() && s->GetLimit() && u->server->users >= s->GetLimit())
+ {
+ Log(this) << "Depooling full server " << s->GetName() << ": " << u->server->users << " users";
+ s->SetActive(false);
+ }
+ }
+ }
+
+ void OnPreUserLogoff(User *u) override
+ {
+ if (u && u->server)
+ {
+ DNSServerImpl *s = DNSServerImpl::Find(u->server->GetName());
+ if (!s || !s->GetPooled())
+ return;
+
+ /* Check for dropping under userlimit */
+ if (s->GetLimit() && !s->Active() && s->GetLimit() > u->server->users)
+ {
+ Log(this) << "Pooling server " << s->GetName();
+ s->SetActive(true);
+ }
+
+ if (this->user_drop_mark > 0)
+ {
+ std::list<time_t>& times = server_quit_times[u->server->GetName()];
+ times.push_back(Anope::CurTime);
+ if (times.size() > static_cast<unsigned>(this->user_drop_mark))
+ times.pop_front();
+
+ if (times.size() == static_cast<unsigned>(this->user_drop_mark))
+ {
+ time_t diff = Anope::CurTime - *times.begin();
+
+ /* Check for very fast user drops */
+ if (s->Active() && diff <= this->user_drop_time)
+ {
+ Log(this) << "Depooling server " << s->GetName() << ": dropped " << this->user_drop_mark << " users in " << diff << " seconds";
+ s->repool = Anope::CurTime + this->user_drop_readd_time;
+ s->SetActive(false);
+ }
+ /* Check for needing to re-pool a server that dropped users */
+ else if (!s->Active() && s->repool && s->repool <= Anope::CurTime)
+ {
+ s->SetActive(true);
+ s->repool = 0;
+ Log(this) << "Pooling server " << s->GetName();
+ }
+ }
+ }
+ }
+ }
+
+ void OnDnsRequest(DNS::Query &req, DNS::Query *packet) override
+ {
+ if (req.questions.empty())
+ return;
+ /* Currently we reply to any QR for A/AAAA */
+ const DNS::Question& q = req.questions[0];
+ if (q.type != DNS::QUERY_A && q.type != DNS::QUERY_AAAA && q.type != DNS::QUERY_AXFR && q.type != DNS::QUERY_ANY)
+ return;
+
+ DNSZone *zone = DNSZoneImpl::Find(q.name);
+ size_t answer_size = packet->answers.size();
+ if (zone)
+ {
+ for (DNSServerImpl *s : zone->GetRefs<DNSServerImpl *>())
+ {
+ if (!s->Active())
+ continue;
+
+ for (DNSIP *ip : s->GetRefs<DNSIP *>())
+ {
+ DNS::QueryType q_type = ip->GetIP().find(':') != Anope::string::npos ? DNS::QUERY_AAAA : DNS::QUERY_A;
+
+ if (q.type == DNS::QUERY_AXFR || q.type == DNS::QUERY_ANY || q_type == q.type)
+ {
+ DNS::ResourceRecord rr(q.name, q_type);
+ rr.ttl = this->ttl;
+ rr.rdata = ip->GetIP();
+ packet->answers.push_back(rr);
+ }
+ }
+ }
+ }
+
+ if (packet->answers.size() == answer_size)
+ {
+ /* Default zone */
+ for (DNSServerImpl *s : Serialize::GetObjects<DNSServerImpl *>())
+ {
+ if (!s->Active())
+ continue;
+
+ for (DNSIP *ip : s->GetRefs<DNSIP *>())
+ {
+ DNS::QueryType q_type = ip->GetIP().find(':') != Anope::string::npos ? DNS::QUERY_AAAA : DNS::QUERY_A;
+
+ if (q.type == DNS::QUERY_AXFR || q.type == DNS::QUERY_ANY || q_type == q.type)
+ {
+ DNS::ResourceRecord rr(q.name, q_type);
+ rr.ttl = this->ttl;
+ rr.rdata = ip->GetIP();
+ packet->answers.push_back(rr);
+ }
+ }
+ }
+ }
+
+ if (packet->answers.size() == answer_size)
+ {
+ if (last_warn + 60 < Anope::CurTime)
+ {
+ last_warn = Anope::CurTime;
+ Log(this) << "Warning! There are no pooled servers!";
+ }
+
+ /* Something messed up, just return them all and hope one is available */
+ for (DNSServer *s : Serialize::GetObjects<DNSServer *>())
+ for (DNSIP *ip : s->GetRefs<DNSIP *>())
+ {
+ DNS::QueryType q_type = ip->GetIP().find(':') != Anope::string::npos ? DNS::QUERY_AAAA : DNS::QUERY_A;
+
+ if (q.type == DNS::QUERY_AXFR || q.type == DNS::QUERY_ANY || q_type == q.type)
+ {
+ DNS::ResourceRecord rr(q.name, q_type);
+ rr.ttl = this->ttl;
+ rr.rdata = ip->GetIP();
+ packet->answers.push_back(rr);
+ }
+ }
+
+ if (packet->answers.size() == answer_size)
+ {
+ Log(this) << "Error! There are no servers with any IPs of type " << q.type;
+ /* Send back an empty answer anyway */
+ }
+ }
+ }
+};
+
+MODULE_INIT(ModuleDNS)
diff --git a/modules/operserv/forbid.cpp b/modules/operserv/forbid.cpp
new file mode 100644
index 000000000..a4234fbbf
--- /dev/null
+++ b/modules/operserv/forbid.cpp
@@ -0,0 +1,602 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2011-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/operserv/forbid.h"
+#include "modules/nickserv.h"
+#include "modules/chanserv.h"
+
+class ForbidDataImpl : public ForbidData
+{
+ friend class ForbidDataType;
+
+ Anope::string mask, creator, reason;
+ time_t created = 0, expires = 0;
+ ForbidType type = static_cast<ForbidType>(0);
+
+ public:
+ ForbidDataImpl(Serialize::TypeBase *type) : ForbidData(type) { }
+ ForbidDataImpl(Serialize::TypeBase *type, Serialize::ID id) : ForbidData(type, id) { }
+
+ Anope::string GetMask() override;
+ void SetMask(const Anope::string &) override;
+
+ Anope::string GetCreator() override;
+ void SetCreator(const Anope::string &) override;
+
+ Anope::string GetReason() override;
+ void SetReason(const Anope::string &) override;
+
+ time_t GetCreated() override;
+ void SetCreated(const time_t &) override;
+
+ time_t GetExpires() override;
+ void SetExpires(const time_t &) override;
+
+ ForbidType GetType() override;
+ void SetType(const ForbidType &) override;
+};
+
+class ForbidDataType : public Serialize::Type<ForbidDataImpl>
+{
+ public:
+ Serialize::Field<ForbidDataImpl, Anope::string> mask, creator, reason;
+ Serialize::Field<ForbidDataImpl, time_t> created, expires;
+ Serialize::Field<ForbidDataImpl, ForbidType> type;
+
+ ForbidDataType(Module *me) : Serialize::Type<ForbidDataImpl>(me)
+ , mask(this, "mask", &ForbidDataImpl::mask)
+ , creator(this, "creator", &ForbidDataImpl::creator)
+ , reason(this, "reason", &ForbidDataImpl::reason)
+ , created(this, "created", &ForbidDataImpl::created)
+ , expires(this, "expires", &ForbidDataImpl::expires)
+ , type(this, "type", &ForbidDataImpl::type)
+ {
+ }
+};
+
+Anope::string ForbidDataImpl::GetMask()
+{
+ return Get(&ForbidDataType::mask);
+}
+
+void ForbidDataImpl::SetMask(const Anope::string &m)
+{
+ Set(&ForbidDataType::mask, m);
+}
+
+Anope::string ForbidDataImpl::GetCreator()
+{
+ return Get(&ForbidDataType::creator);
+}
+
+void ForbidDataImpl::SetCreator(const Anope::string &c)
+{
+ Set(&ForbidDataType::creator, c);
+}
+
+Anope::string ForbidDataImpl::GetReason()
+{
+ return Get(&ForbidDataType::reason);
+}
+
+void ForbidDataImpl::SetReason(const Anope::string &r)
+{
+ Set(&ForbidDataType::reason, r);
+}
+
+time_t ForbidDataImpl::GetCreated()
+{
+ return Get(&ForbidDataType::created);
+}
+
+void ForbidDataImpl::SetCreated(const time_t &t)
+{
+ Set(&ForbidDataType::created, t);
+}
+
+time_t ForbidDataImpl::GetExpires()
+{
+ return Get(&ForbidDataType::expires);
+}
+
+void ForbidDataImpl::SetExpires(const time_t &t)
+{
+ Set(&ForbidDataType::expires, t);
+}
+
+ForbidType ForbidDataImpl::GetType()
+{
+ return Get(&ForbidDataType::type);
+}
+
+void ForbidDataImpl::SetType(const ForbidType &t)
+{
+ Set(&ForbidDataType::type, t);
+}
+
+class MyForbidService : public ForbidService
+{
+ public:
+ MyForbidService(Module *m) : ForbidService(m)
+ {
+ }
+
+ ForbidData *FindForbid(const Anope::string &mask, ForbidType ftype) override
+ {
+ for (ForbidData *d : GetForbids())
+ if (d->GetType() == ftype && Anope::Match(mask, d->GetMask(), false, true))
+ return d;
+ return NULL;
+ }
+
+ std::vector<ForbidData *> GetForbids() override
+ {
+ for (ForbidData *d : Serialize::GetObjects<ForbidData *>())
+ if (d->GetExpires() && !Anope::NoExpire && Anope::CurTime >= d->GetExpires())
+ {
+ Anope::string ftype = "none";
+ if (d->GetType() == FT_NICK)
+ ftype = "nick";
+ else if (d->GetType() == FT_CHAN)
+ ftype = "chan";
+ else if (d->GetType() == FT_EMAIL)
+ ftype = "email";
+
+ Log(LOG_NORMAL, "expire/forbid", Config->GetClient("OperServ")) << "Expiring forbid for " << d->GetMask() << " type " << ftype;
+ d->Delete();
+ }
+ return Serialize::GetObjects<ForbidData *>();
+ }
+};
+
+class CommandOSForbid : public Command
+{
+ ServiceReference<ForbidService> fs;
+
+ public:
+ CommandOSForbid(Module *creator) : Command(creator, "operserv/forbid", 1, 5)
+ {
+ this->SetDesc(_("Forbid usage of nicknames, channels, and emails"));
+ this->SetSyntax(_("ADD {NICK|CHAN|EMAIL|REGISTER} [+\037expiry\037] \037entry\037 \037reason\037"));
+ this->SetSyntax(_("DEL {NICK|CHAN|EMAIL|REGISTER} \037entry\037"));
+ this->SetSyntax("LIST [NICK|CHAN|EMAIL|REGISTER]");
+ }
+
+ void OnUserNickChange(User *u)
+ {
+ if (u->HasMode("OPER"))
+ return;
+
+ ForbidData *d = fs->FindForbid(u->nick, FT_NICK);
+ if (d != NULL)
+ {
+ ServiceBot *bi = Config->GetClient("NickServ");
+ if (!bi)
+ bi = Config->GetClient("OperServ");
+ if (bi)
+ u->SendMessage(bi, _("This nickname has been forbidden: \002{0}\002"), d->GetReason());
+ if (NickServ::service)
+ NickServ::service->Collide(u, NULL);
+ }
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ if (!this->fs)
+ return;
+
+ const Anope::string &command = params[0];
+ const Anope::string &subcommand = params.size() > 1 ? params[1] : "";
+
+ ForbidType ftype = FT_SIZE;
+ if (subcommand.equals_ci("NICK"))
+ ftype = FT_NICK;
+ else if (subcommand.equals_ci("CHAN"))
+ ftype = FT_CHAN;
+ else if (subcommand.equals_ci("EMAIL"))
+ ftype = FT_EMAIL;
+ else if (subcommand.equals_ci("REGISTER"))
+ ftype = FT_REGISTER;
+
+ if (command.equals_ci("ADD") && params.size() > 3 && ftype != FT_SIZE)
+ {
+ const Anope::string &expiry = params[2][0] == '+' ? params[2] : "";
+ const Anope::string &entry = !expiry.empty() ? params[3] : params[2];
+ Anope::string reason;
+ if (expiry.empty())
+ reason = params[3] + " ";
+ if (params.size() > 4)
+ reason += params[4];
+ reason.trim();
+
+ if (entry.replace_all_cs("?*", "").empty())
+ {
+ source.Reply(_("The mask must contain at least one non wildcard character."));
+ return;
+ }
+
+ time_t expiryt = 0;
+
+ if (!expiry.empty())
+ {
+ expiryt = Anope::DoTime(expiry);
+ if (expiryt == -1)
+ {
+ source.Reply(_("Invalid expiry time \002{0}\002."), expiry);
+ return;
+ }
+ else if (expiryt)
+ expiryt += Anope::CurTime;
+ }
+
+ NickServ::Nick *target = NickServ::FindNick(entry);
+ if (target != NULL && Config->GetModule("nickserv")->Get<bool>("secureadmins", "yes") && target->GetAccount()->IsServicesOper())
+ {
+ source.Reply(_("Access denied."));
+ return;
+ }
+
+ ForbidData *d = this->fs->FindForbid(entry, ftype);
+ bool created = false;
+ if (d == NULL)
+ {
+ d = Serialize::New<ForbidData *>();
+ created = true;
+ }
+
+ d->SetMask(entry);
+ d->SetCreator(source.GetNick());
+ d->SetReason(reason);
+ d->SetCreated(Anope::CurTime);
+ d->SetExpires(expiryt);
+ d->SetType(ftype);
+
+ if (Anope::ReadOnly)
+ source.Reply(_("Services are in read-only mode. Any changes made may not persist."));
+
+ Log(LOG_ADMIN, source, this) << "to add a forbid on " << entry << " of type " << subcommand;
+ source.Reply(_("Added a forbid on \002{0}\002 of type \002{1}\002 to expire on \002{2}\002."), entry, subcommand.lower(), expiryt ? Anope::strftime(expiryt, source.GetAccount()) : "never");
+
+ /* apply forbid */
+ switch (ftype)
+ {
+ case FT_NICK:
+ {
+ int na_matches = 0;
+
+ for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it)
+ this->OnUserNickChange(it->second);
+
+ for (auto it = NickServ::service->GetNickList().begin(); it != NickServ::service->GetNickList().end();)
+ {
+ NickServ::Nick *na = *it;
+ ++it;
+
+ d = this->fs->FindForbid(na->GetNick(), FT_NICK);
+ if (d == NULL)
+ continue;
+
+ ++na_matches;
+
+ delete na;
+ }
+
+ source.Reply(_("\002{0}\002 nickname(s) dropped."), na_matches);
+ break;
+ }
+ case FT_CHAN:
+ {
+ int chan_matches = 0, ci_matches = 0;
+
+ for (channel_map::const_iterator it = ChannelList.begin(), it_end = ChannelList.end(); it != it_end;)
+ {
+ Channel *c = it->second;
+ ++it;
+
+ d = this->fs->FindForbid(c->name, FT_CHAN);
+ if (d == NULL)
+ continue;
+
+ ServiceBot *OperServ = Config->GetClient("OperServ");
+ if (IRCD->CanSQLineChannel && OperServ)
+ {
+ time_t inhabit = Config->GetModule("chanserv")->Get<time_t>("inhabit", "15s");
+#warning "xline allocated on stack"
+#if 0
+ XLine x(c->name, OperServ->nick, Anope::CurTime + inhabit, d->GetReason());
+ IRCD->SendSQLine(NULL, &x);
+#endif
+ }
+ else if (ChanServ::service)
+ {
+ ChanServ::service->Hold(c);
+ }
+
+ ++chan_matches;
+
+ for (Channel::ChanUserList::const_iterator cit = c->users.begin(), cit_end = c->users.end(); cit != cit_end;)
+ {
+ User *u = cit->first;
+ ++cit;
+
+ if (u->server == Me || u->HasMode("OPER"))
+ continue;
+
+ reason = Anope::printf(Language::Translate(u, _("This channel has been forbidden: \002%s\002")), d->GetReason().c_str());
+
+ c->Kick(source.service, u, "%s", reason.c_str());
+ }
+ }
+
+ for (auto it = ChanServ::service->GetChannels().begin(); it != ChanServ::service->GetChannels().end();)
+ {
+ ChanServ::Channel *ci = it->second;
+ ++it;
+
+ d = this->fs->FindForbid(ci->GetName(), FT_CHAN);
+ if (d == NULL)
+ continue;
+
+ ++ci_matches;
+
+ delete ci;
+ }
+
+ source.Reply(_("\002{0}\002 channel(s) cleared, and \002{1}\002 channel(s) dropped."), chan_matches, ci_matches);
+
+ break;
+ }
+ default:
+ break;
+ }
+
+ }
+ else if (command.equals_ci("DEL") && params.size() > 2 && ftype != FT_SIZE)
+ {
+ const Anope::string &entry = params[2];
+
+ ForbidData *d = this->fs->FindForbid(entry, ftype);
+ if (d == nullptr)
+ {
+ source.Reply(_("Forbid on \002{0}\002 was not found."), entry);
+ return;
+ }
+
+ if (Anope::ReadOnly)
+ source.Reply(_("Services are in read-only mode. Any changes made may not persist."));
+
+ Log(LOG_ADMIN, source, this) << "to remove forbid on " << d->GetMask() << " of type " << subcommand;
+ source.Reply(_("\002{0}\002 deleted from the \002{1}\002 forbid list."), d->GetMask(), subcommand);
+ d->Delete();
+ }
+ else if (command.equals_ci("LIST"))
+ {
+ const std::vector<ForbidData *> &forbids = this->fs->GetForbids();
+ if (forbids.empty())
+ {
+ source.Reply(_("Forbid list is empty."));
+ return;
+ }
+
+ ListFormatter list(source.GetAccount());
+ list.AddColumn(_("Mask")).AddColumn(_("Type")).AddColumn(_("Creator")).AddColumn(_("Expires")).AddColumn(_("Reason"));
+
+ unsigned shown = 0;
+ for (unsigned i = 0; i < forbids.size(); ++i)
+ {
+ ForbidData *d = forbids[i];
+
+ if (ftype != FT_SIZE && ftype != d->GetType())
+ continue;
+
+ Anope::string stype;
+ if (d->GetType() == FT_NICK)
+ stype = "NICK";
+ else if (d->GetType() == FT_CHAN)
+ stype = "CHAN";
+ else if (d->GetType() == FT_EMAIL)
+ stype = "EMAIL";
+ else if (d->GetType() == FT_REGISTER)
+ stype = "REGISTER";
+ else
+ continue;
+
+ ListFormatter::ListEntry entry;
+ entry["Mask"] = d->GetMask();
+ entry["Type"] = stype;
+ entry["Creator"] = d->GetCreator();
+ entry["Expires"] = d->GetExpires() ? Anope::strftime(d->GetExpires(), NULL, true).c_str() : Language::Translate(source.GetAccount(), _("Never"));
+ entry["Reason"] = d->GetReason();
+ list.AddEntry(entry);
+ ++shown;
+ }
+
+ if (!shown)
+ {
+ source.Reply(_("There are no forbids of type \002{0}\002."), subcommand.upper());
+ return;
+ }
+
+ source.Reply(_("Forbid list:"));
+
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+
+ if (shown >= forbids.size())
+ source.Reply(_("End of forbid list."));
+ else
+ source.Reply(_("End of forbid list - \002{0}\002/\002{1}\002 entries shown."), shown, forbids.size());
+ }
+ else
+ this->OnSyntaxError(source, command);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Forbid allows you to forbid usage of certain nicknames, channels, and email addresses. Wildcards are accepted for all entries."));
+
+ const Anope::string &regexengine = Config->GetBlock("options")->Get<Anope::string>("regexengine");
+ if (!regexengine.empty())
+ {
+ source.Reply(" ");
+ source.Reply(_("Regex matches are also supported using the %s engine. Enclose your pattern in // if this is desired."), regexengine);
+ }
+
+ return true;
+ }
+};
+
+class OSForbid : public Module
+ , public EventHook<Event::UserConnect>
+ , public EventHook<Event::UserNickChange>
+ , public EventHook<Event::CheckKick>
+ , public EventHook<Event::PreCommand>
+{
+ MyForbidService forbid_service;
+ ForbidDataType type;
+ CommandOSForbid commandosforbid;
+
+ public:
+ OSForbid(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , EventHook<Event::UserConnect>(this)
+ , EventHook<Event::UserNickChange>(this)
+ , EventHook<Event::CheckKick>(this)
+ , EventHook<Event::PreCommand>(this)
+ , forbid_service(this)
+ , type(this)
+ , commandosforbid(this)
+ {
+ }
+
+ void OnUserConnect(User *u, bool &exempt) override
+ {
+ if (u->Quitting() || exempt)
+ return;
+
+ commandosforbid.OnUserNickChange(u);
+ }
+
+ void OnUserNickChange(User *u, const Anope::string &) override
+ {
+ commandosforbid.OnUserNickChange(u);
+ }
+
+ EventReturn OnCheckKick(User *u, Channel *c, Anope::string &mask, Anope::string &reason) override
+ {
+ ServiceBot *OperServ = Config->GetClient("OperServ");
+ if (u->HasMode("OPER") || !OperServ)
+ return EVENT_CONTINUE;
+
+ ForbidData *d = this->forbid_service.FindForbid(c->name, FT_CHAN);
+ if (d != NULL)
+ {
+ if (IRCD->CanSQLineChannel)
+ {
+ time_t inhabit = Config->GetModule("chanserv")->Get<time_t>("inhabit", "15s");
+#warning "xline allocated on stack"
+#if 0
+ XLine x(c->name, OperServ->nick, Anope::CurTime + inhabit, d->GetReason());
+ IRCD->SendSQLine(NULL, &x);
+#endif
+ }
+ else if (ChanServ::service)
+ {
+ ChanServ::service->Hold(c);
+ }
+
+ reason = Anope::printf(Language::Translate(u, _("This channel has been forbidden: %s")), d->GetReason().c_str());
+
+ return EVENT_STOP;
+ }
+
+ return EVENT_CONTINUE;
+ }
+
+ EventReturn OnPreCommand(CommandSource &source, Command *command, std::vector<Anope::string> &params) override
+ {
+ if (command->GetName() == "nickserv/info" && params.size() > 0)
+ {
+ ForbidData *d = this->forbid_service.FindForbid(params[0], FT_NICK);
+ if (d != NULL)
+ {
+ if (source.IsOper())
+ source.Reply(_("Nick \002%s\002 is forbidden by %s: %s"), params[0], d->GetCreator(), d->GetReason());
+ else
+ source.Reply(_("Nick \002%s\002 is forbidden."), params[0]);
+ return EVENT_STOP;
+ }
+ }
+ else if (command->GetName() == "chanserv/info" && params.size() > 0)
+ {
+ ForbidData *d = this->forbid_service.FindForbid(params[0], FT_CHAN);
+ if (d != NULL)
+ {
+ if (source.IsOper())
+ source.Reply(_("Channel \002%s\002 is forbidden by %s: %s"), params[0], d->GetCreator(), d->GetReason());
+ else
+ source.Reply(_("Channel \002%s\002 is forbidden."), params[0]);
+ return EVENT_STOP;
+ }
+ }
+ else if (source.IsOper())
+ return EVENT_CONTINUE;
+ else if (command->GetName() == "nickserv/register" && params.size() > 1)
+ {
+ ForbidData *d = this->forbid_service.FindForbid(source.GetNick(), FT_REGISTER);
+ if (d != NULL)
+ {
+ source.Reply(_("\002{0}\002 may not be registered."), source.GetNick());
+ return EVENT_STOP;
+ }
+
+ d = this->forbid_service.FindForbid(params[1], FT_EMAIL);
+ if (d != NULL)
+ {
+ source.Reply(_("Your email address is not allowed, choose a different one."));
+ return EVENT_STOP;
+ }
+ }
+ else if (command->GetName() == "nickserv/set/email" && params.size() > 0)
+ {
+ ForbidData *d = this->forbid_service.FindForbid(params[0], FT_EMAIL);
+ if (d != NULL)
+ {
+ source.Reply(_("Your email address is not allowed, choose a different one."));
+ return EVENT_STOP;
+ }
+ }
+ else if (command->GetName() == "chanserv/register" && !params.empty())
+ {
+ ForbidData *d = this->forbid_service.FindForbid(params[0], FT_REGISTER);
+ if (d != NULL)
+ {
+ source.Reply(_("Channel \002{0}\002 is currently suspended."), params[0]);
+ return EVENT_STOP;
+ }
+ }
+
+ return EVENT_CONTINUE;
+ }
+};
+
+MODULE_INIT(OSForbid)
diff --git a/modules/operserv/ignore.cpp b/modules/operserv/ignore.cpp
new file mode 100644
index 000000000..607163fb9
--- /dev/null
+++ b/modules/operserv/ignore.cpp
@@ -0,0 +1,410 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "module.h"
+#include "modules/operserv/ignore.h"
+
+class IgnoreImpl : public Ignore
+{
+ friend class IgnoreType;
+
+ Anope::string mask, creator, reason;
+ time_t time = 0;
+
+ public:
+ IgnoreImpl(Serialize::TypeBase *type) : Ignore(type) { }
+ IgnoreImpl(Serialize::TypeBase *type, Serialize::ID id) : Ignore(type, id) { }
+
+ Anope::string GetMask() override;
+ void SetMask(const Anope::string &) override;
+
+ Anope::string GetCreator() override;
+ void SetCreator(const Anope::string &) override;
+
+ Anope::string GetReason() override;
+ void SetReason(const Anope::string &) override;
+
+ time_t GetTime() override;
+ void SetTime(const time_t &) override;
+};
+
+class IgnoreType : public Serialize::Type<IgnoreImpl>
+{
+ public:
+ Serialize::Field<IgnoreImpl, Anope::string> mask, creator, reason;
+ Serialize::Field<IgnoreImpl, time_t> time;
+
+ IgnoreType(Module *me) : Serialize::Type<IgnoreImpl>(me)
+ , mask(this, "mask", &IgnoreImpl::mask)
+ , creator(this, "creator", &IgnoreImpl::creator)
+ , reason(this, "reason", &IgnoreImpl::reason)
+ , time(this, "time", &IgnoreImpl::time)
+ {
+ }
+};
+
+Anope::string IgnoreImpl::GetMask()
+{
+ return Get(&IgnoreType::mask);
+}
+
+void IgnoreImpl::SetMask(const Anope::string &m)
+{
+ Set(&IgnoreType::mask, m);
+}
+
+Anope::string IgnoreImpl::GetCreator()
+{
+ return Get(&IgnoreType::creator);
+}
+
+void IgnoreImpl::SetCreator(const Anope::string &c)
+{
+ Set(&IgnoreType::creator, c);
+}
+
+Anope::string IgnoreImpl::GetReason()
+{
+ return Get(&IgnoreType::reason);
+}
+
+void IgnoreImpl::SetReason(const Anope::string &r)
+{
+ Set(&IgnoreType::reason, r);
+}
+
+time_t IgnoreImpl::GetTime()
+{
+ return Get(&IgnoreType::time);
+}
+
+void IgnoreImpl::SetTime(const time_t &t)
+{
+ Set(&IgnoreType::time, t);
+}
+
+class OSIgnoreService : public IgnoreService
+{
+ public:
+ OSIgnoreService(Module *o) : IgnoreService(o)
+ {
+ }
+
+ Ignore *Find(const Anope::string &mask) override
+ {
+ User *u = User::Find(mask, true);
+ std::vector<Ignore *> ignores = Serialize::GetObjects<Ignore *>();
+ std::vector<Ignore *>::iterator ign = ignores.begin(), ign_end = ignores.end();
+
+ if (u)
+ {
+ for (; ign != ign_end; ++ign)
+ {
+ Entry ignore_mask("", (*ign)->GetMask());
+ if (ignore_mask.Matches(u, true))
+ break;
+ }
+ }
+ else
+ {
+ size_t user, host;
+ Anope::string tmp;
+ /* We didn't get a user.. generate a valid mask. */
+ if ((host = mask.find('@')) != Anope::string::npos)
+ {
+ if ((user = mask.find('!')) != Anope::string::npos)
+ {
+ /* this should never happen */
+ if (user > host)
+ return NULL;
+ tmp = mask;
+ }
+ else
+ /* We have user@host. Add nick wildcard. */
+ tmp = "*!" + mask;
+ }
+ /* We only got a nick.. */
+ else
+ tmp = mask + "!*@*";
+
+ for (; ign != ign_end; ++ign)
+ if (Anope::Match(tmp, (*ign)->GetMask(), false, true))
+ break;
+ }
+
+ /* Check whether the entry has timed out */
+ if (ign != ign_end)
+ {
+ Ignore *id = *ign;
+
+ if (id->GetTime() && !Anope::NoExpire && id->GetTime() <= Anope::CurTime)
+ {
+ Log(LOG_NORMAL, "expire/ignore", Config->GetClient("OperServ")) << "Expiring ignore entry " << id->GetMask();
+ id->Delete();
+ }
+ else
+ return id;
+ }
+
+ return NULL;
+ }
+};
+
+class CommandOSIgnore : public Command
+{
+ ServiceReference<IgnoreService> ignore_service;
+
+ private:
+ Anope::string RealMask(const Anope::string &mask)
+ {
+ /* If it s an existing user, we ignore the hostmask. */
+ User *u = User::Find(mask, true);
+ if (u)
+ return "*!*@" + u->host;
+
+ size_t host = mask.find('@');
+ /* Determine whether we get a nick or a mask. */
+ if (host != Anope::string::npos)
+ {
+ size_t user = mask.find('!');
+ /* Check whether we have a nick too.. */
+ if (user != Anope::string::npos)
+ {
+ if (user > host)
+ /* this should never happen */
+ return "";
+ else
+ return mask;
+ }
+ else
+ /* We have user@host. Add nick wildcard. */
+ return "*!" + mask;
+ }
+
+ /* We only got a nick.. */
+ return mask + "!*@*";
+ }
+
+ void DoAdd(CommandSource &source, const std::vector<Anope::string> &params)
+ {
+ const Anope::string &time = params.size() > 1 ? params[1] : "";
+ const Anope::string &nick = params.size() > 2 ? params[2] : "";
+ const Anope::string &reason = params.size() > 3 ? params[3] : "";
+
+ if (time.empty() || nick.empty())
+ {
+ this->OnSyntaxError(source, "ADD");
+ return;
+ }
+
+ time_t t = Anope::DoTime(time);
+
+ if (t <= -1)
+ {
+ source.Reply(_("Invalid expiry time \002{0}\002."), time);
+ return;
+ }
+
+ Anope::string mask = RealMask(nick);
+ if (mask.empty())
+ {
+ source.Reply(_("Mask must be in the form \037user\037@\037host\037."));
+ return;
+ }
+
+ if (Anope::ReadOnly)
+ source.Reply(_("Services are in read-only mode. Any changes made may not persist."));
+
+ Ignore *ign = Serialize::New<Ignore *>();
+ ign->SetMask(mask);
+ ign->SetCreator(source.GetNick());
+ ign->SetReason(reason);
+ ign->SetTime(t ? Anope::CurTime + t : 0);
+
+ if (!t)
+ {
+ source.Reply(_("\002{0}\002 will now permanently be ignored."), mask);
+ Log(LOG_ADMIN, source, this) << "to add a permanent ignore for " << mask;
+ }
+ else
+ {
+ source.Reply(_("\002{0}\002 will now be ignored for \002{1}\002."), mask, Anope::Duration(t, source.GetAccount()));
+ Log(LOG_ADMIN, source, this) << "to add an ignore on " << mask << " for " << Anope::Duration(t);
+ }
+ }
+
+ void DoList(CommandSource &source)
+ {
+ std::vector<Ignore *> ignores = Serialize::GetObjects<Ignore *>();
+
+ for (Ignore *id : ignores)
+ {
+ if (id->GetTime() && !Anope::NoExpire && id->GetTime() <= Anope::CurTime)
+ {
+ Log(LOG_NORMAL, "expire/ignore", Config->GetClient("OperServ")) << "Expiring ignore entry " << id->GetMask();
+ id->Delete();
+ }
+ }
+
+ ignores = Serialize::GetObjects<Ignore *>();
+ if (ignores.empty())
+ {
+ source.Reply(_("Ignore list is empty."));
+ return;
+ }
+
+ ListFormatter list(source.GetAccount());
+ list.AddColumn(_("Mask")).AddColumn(_("Creator")).AddColumn(_("Reason")).AddColumn(_("Expires"));
+
+ for (Ignore *ignore : ignores)
+ {
+ ListFormatter::ListEntry entry;
+ entry["Mask"] = ignore->GetMask();
+ entry["Creator"] = ignore->GetCreator();
+ entry["Reason"] = ignore->GetReason();
+ entry["Expires"] = Anope::Expires(ignore->GetTime(), source.GetAccount());
+ list.AddEntry(entry);
+ }
+
+ source.Reply(_("Services ignore list:"));
+
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+ for (const Anope::string &r : replies)
+ source.Reply(r);
+ }
+
+ void DoDel(CommandSource &source, const std::vector<Anope::string> &params)
+ {
+ const Anope::string nick = params.size() > 1 ? params[1] : "";
+ if (nick.empty())
+ {
+ this->OnSyntaxError(source, "DEL");
+ return;
+ }
+
+ Anope::string mask = RealMask(nick);
+ if (mask.empty())
+ {
+ source.Reply(_("Mask must be in the form \037user\037@\037host\037."));
+ return;
+ }
+
+ Ignore *ign = ignore_service->Find(mask);
+ if (!ign)
+ {
+ source.Reply(_("\002{0}\002 not found on ignore list."), mask);
+ return;
+ }
+
+ if (Anope::ReadOnly)
+ source.Reply(_("Services are in read-only mode. Any changes made may not persist."));
+
+ Log(LOG_ADMIN, source, this) << "to remove an ignore on " << mask;
+ source.Reply(_("\002{0}\002 will no longer be ignored."), mask);
+ ign->Delete();
+ }
+
+ void DoClear(CommandSource &source)
+ {
+ if (Anope::ReadOnly)
+ source.Reply(_("Services are in read-only mode. Any changes made may not persist."));
+
+ for (Ignore *ign : Serialize::GetObjects<Ignore *>())
+ ign->Delete();
+
+ Log(LOG_ADMIN, source, this) << "to CLEAR the list";
+ source.Reply(_("Ignore list has been cleared."));
+ }
+
+ public:
+ CommandOSIgnore(Module *creator) : Command(creator, "operserv/ignore", 1, 4)
+ {
+ this->SetDesc(_("Modify the Services ignore list"));
+ this->SetSyntax(_("ADD \037expiry\037 {\037nick\037|\037mask\037} [\037reason\037]"));
+ this->SetSyntax(_("DEL {\037nick\037|\037mask\037}"));
+ this->SetSyntax("LIST");
+ this->SetSyntax("CLEAR");
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &cmd = params[0];
+
+ if (cmd.equals_ci("ADD"))
+ return this->DoAdd(source, params);
+ else if (cmd.equals_ci("LIST"))
+ return this->DoList(source);
+ else if (cmd.equals_ci("DEL"))
+ return this->DoDel(source, params);
+ else if (cmd.equals_ci("CLEAR"))
+ return this->DoClear(source);
+ else
+ this->OnSyntaxError(source, "");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("The \002{0}\002 command allows you to make services ignore a user or hostmask."
+ " \037expiry\037 must be an integer and can be followed by one of \037d\037 (days), \037h\037 (hours), or \037m\037 (minutes)."
+ " If a unit specifier is not included, the default is seconds."
+ " To make services permanently ignore the user, use 0 as the expiry time."
+ " When adding a \037mask\037, it should be in the format nick!user@host, everything else will be considered a nicknames. Wildcards are permitted."),
+ source.command);
+
+ const Anope::string &regexengine = Config->GetBlock("options")->Get<Anope::string>("regexengine");
+ if (!regexengine.empty())
+ {
+ source.Reply(" ");
+ source.Reply(_("Regex matches are also supported using the \002{0}\002 engine. Enclose your pattern in // if this is desired."), regexengine);
+ }
+
+ return true;
+ }
+};
+
+class OSIgnore : public Module
+ , public EventHook<Event::BotPrivmsg>
+{
+ IgnoreType ignoretype;
+ OSIgnoreService osignoreservice;
+ CommandOSIgnore commandosignore;
+
+ public:
+ OSIgnore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , EventHook<Event::BotPrivmsg>(this)
+ , ignoretype(this)
+ , osignoreservice(this)
+ , commandosignore(this)
+ {
+
+ }
+
+ EventReturn OnBotPrivmsg(User *u, ServiceBot *bi, Anope::string &message) override
+ {
+ if (!u->HasMode("OPER") && this->osignoreservice.Find(u->nick))
+ return EVENT_STOP;
+
+ return EVENT_CONTINUE;
+ }
+};
+
+MODULE_INIT(OSIgnore)
diff --git a/modules/operserv/info.cpp b/modules/operserv/info.cpp
new file mode 100644
index 000000000..ab97b9b14
--- /dev/null
+++ b/modules/operserv/info.cpp
@@ -0,0 +1,280 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2013-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "module.h"
+#include "modules/nickserv/info.h"
+#include "modules/chanserv/info.h"
+#include "modules/operserv/info.h"
+
+class OperInfoImpl : public OperInfo
+{
+ friend class OperInfoType;
+
+ Serialize::Object *target = nullptr;
+ Anope::string info, creator;
+ time_t created = 0;
+
+ public:
+ OperInfoImpl(Serialize::TypeBase *type) : OperInfo(type) { }
+ OperInfoImpl(Serialize::TypeBase *type, Serialize::ID id) : OperInfo(type, id) { }
+
+ Serialize::Object *GetTarget() override;
+ void SetTarget(Serialize::Object *) override;
+
+ Anope::string GetInfo() override;
+ void SetInfo(const Anope::string &) override;
+
+ Anope::string GetCreator() override;
+ void SetCreator(const Anope::string &) override;
+
+ time_t GetCreated() override;
+ void SetCreated(const time_t &) override;
+};
+
+class OperInfoType : public Serialize::Type<OperInfoImpl>
+{
+ public:
+ Serialize::ObjectField<OperInfoImpl, Serialize::Object *> target;
+ Serialize::Field<OperInfoImpl, Anope::string> info, creator;
+ Serialize::Field<OperInfoImpl, time_t> created;
+
+ OperInfoType(Module *c) : Serialize::Type<OperInfoImpl>(c)
+ , target(this, "target", &OperInfoImpl::target, true)
+ , info(this, "info", &OperInfoImpl::info)
+ , creator(this, "adder", &OperInfoImpl::creator)
+ , created(this, "created", &OperInfoImpl::created)
+ {
+ }
+};
+
+Serialize::Object *OperInfoImpl::GetTarget()
+{
+ return Get(&OperInfoType::target);
+}
+
+void OperInfoImpl::SetTarget(Serialize::Object *t)
+{
+ Set(&OperInfoType::target, t);
+}
+
+Anope::string OperInfoImpl::GetInfo()
+{
+ return Get(&OperInfoType::info);
+}
+
+void OperInfoImpl::SetInfo(const Anope::string &i)
+{
+ Set(&OperInfoType::info, i);
+}
+
+Anope::string OperInfoImpl::GetCreator()
+{
+ return Get(&OperInfoType::creator);
+}
+
+void OperInfoImpl::SetCreator(const Anope::string &c)
+{
+ Set(&OperInfoType::creator, c);
+}
+
+time_t OperInfoImpl::GetCreated()
+{
+ return Get(&OperInfoType::created);
+}
+
+void OperInfoImpl::SetCreated(const time_t &t)
+{
+ Set(&OperInfoType::created, t);
+}
+
+class CommandOSInfo : public Command
+{
+ public:
+ CommandOSInfo(Module *creator) : Command(creator, "operserv/info", 2, 3)
+ {
+ this->SetDesc(_("Associate oper info with a nick or channel"));
+ this->SetSyntax(_("ADD \037target\037 \037info\037"));
+ this->SetSyntax(_("DEL \037target\037 \037info\037"));
+ this->SetSyntax(_("CLEAR \037target\037"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &cmd = params[0], target = params[1], info = params.size() > 2 ? params[2] : "";
+
+ Serialize::Object *e;
+ if (IRCD->IsChannelValid(target))
+ {
+ ChanServ::Channel *ci = ChanServ::Find(target);
+ if (!ci)
+ {
+ source.Reply(_("Channel \002{0}\002 isn't registered."), target);
+ return;
+ }
+
+ e = ci;
+ }
+ else
+ {
+ NickServ::Nick *na = NickServ::FindNick(target);
+ if (!na)
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), target);
+ return;
+ }
+
+ e = na->GetAccount();
+ }
+
+ if (cmd.equals_ci("ADD"))
+ {
+ if (info.empty())
+ {
+ this->OnSyntaxError(source, cmd);
+ return;
+ }
+
+ std::vector<OperInfo *> oinfos = e->GetRefs<OperInfo *>();
+ if (oinfos.size() >= Config->GetModule(this->module)->Get<unsigned int>("max", "10"))
+ {
+ source.Reply(_("The oper info list for \002{0}\002 is full."), target);
+ return;
+ }
+
+ for (OperInfo *o : oinfos)
+ if (o->GetInfo().equals_ci(info))
+ {
+ source.Reply(_("The oper info already exists on \002{0}\002."), target);
+ return;
+ }
+
+ OperInfo *o = Serialize::New<OperInfo *>();
+ o->SetTarget(e);
+ o->SetInfo(info);
+ o->SetCreator(source.GetNick());
+ o->SetCreated(Anope::CurTime);
+
+ source.Reply(_("Added info to \002{0}\002."), target);
+ Log(LOG_ADMIN, source, this) << "to add information to " << target;
+
+ if (Anope::ReadOnly)
+ source.Reply(_("Services are in read-only mode. Any changes made may not persist."));
+ }
+ else if (cmd.equals_ci("DEL"))
+ {
+ if (info.empty())
+ {
+ this->OnSyntaxError(source, cmd);
+ return;
+ }
+
+ std::vector<OperInfo *> oinfos = e->GetRefs<OperInfo *>();
+ if (oinfos.empty())
+ {
+ source.Reply(_("Oper info list for \002{0}\002 is empty."), target);
+ return;
+ }
+
+ for (OperInfo *o : oinfos)
+ {
+ if (o->GetInfo().equals_ci(info))
+ {
+ o->Delete();
+
+ source.Reply(_("Deleted info from \002{0}\002."), target);
+ Log(LOG_ADMIN, source, this) << "to remove information from " << target;
+
+ if (Anope::ReadOnly)
+ source.Reply(_("Services are in read-only mode. Any changes made may not persist."));
+ return;
+ }
+ }
+
+ source.Reply(_("No such info \002{0}\002 on \002{1}\002."), info, target);
+ }
+ else if (cmd.equals_ci("CLEAR"))
+ {
+ std::vector<OperInfo *> oinfos = e->GetRefs<OperInfo *>();
+
+ if (oinfos.empty())
+ {
+ source.Reply(_("Oper info list for \002{0}\002 is empty."), target);
+ return;
+ }
+
+ for (OperInfo *o : oinfos)
+ o->Delete();
+
+ source.Reply(_("Cleared info from \002{0}\002."), target);
+ Log(LOG_ADMIN, source, this) << "to clear information for " << target;
+
+ if (Anope::ReadOnly)
+ source.Reply(_("Services are in read-only mode. Any changes made may not persist."));
+ }
+ else
+ {
+ this->OnSyntaxError(source, cmd);
+ }
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Add or delete oper information for a given accpint or channel. This informaition will show to opers in the respective info command for the account or channel."));
+ return true;
+ }
+};
+
+class OSInfo : public Module
+ , public EventHook<Event::NickInfo>
+ , public EventHook<Event::ChanInfo>
+{
+ CommandOSInfo commandosinfo;
+ OperInfoType oinfotype;
+
+ void OnInfo(CommandSource &source, Serialize::Object *e, InfoFormatter &info)
+ {
+ if (!source.IsOper())
+ return;
+
+ for (OperInfo *o : e->GetRefs<OperInfo *>())
+ info[_("Oper Info")] = Anope::printf(_("(by %s on %s) %s"), o->GetCreator().c_str(), Anope::strftime(o->GetCreated(), source.GetAccount(), true).c_str(), o->GetInfo().c_str());
+ }
+
+ public:
+ OSInfo(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , EventHook<Event::NickInfo>(this)
+ , EventHook<Event::ChanInfo>(this)
+ , commandosinfo(this)
+ , oinfotype(this)
+ {
+ }
+
+ void OnNickInfo(CommandSource &source, NickServ::Nick *na, InfoFormatter &info, bool show_hidden) override
+ {
+ OnInfo(source, na->GetAccount(), info);
+ }
+
+ void OnChanInfo(CommandSource &source, ChanServ::Channel *ci, InfoFormatter &info, bool show_hidden) override
+ {
+ OnInfo(source, ci, info);
+ }
+};
+
+MODULE_INIT(OSInfo)
diff --git a/modules/operserv/jupe.cpp b/modules/operserv/jupe.cpp
new file mode 100644
index 000000000..ccffcaf2a
--- /dev/null
+++ b/modules/operserv/jupe.cpp
@@ -0,0 +1,80 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+
+class CommandOSJupe : public Command
+{
+ public:
+ CommandOSJupe(Module *creator) : Command(creator, "operserv/jupe", 1, 2)
+ {
+ this->SetDesc(_("\"Jupiter\" a server"));
+ this->SetSyntax(_("\037server\037 [\037reason\037]"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &jserver = params[0];
+ const Anope::string &reason = params.size() > 1 ? params[1] : "";
+ Server *server = Server::Find(jserver, true);
+
+ if (!IRCD->IsHostValid(jserver) || jserver.find('.') == Anope::string::npos)
+ source.Reply(_("\002{0}\002 is not a valid server name."), jserver);
+ else if (server == Me || server == Servers::GetUplink() || server->IsULined())
+ source.Reply(_("You can not jupe Servoces or its uplink server."));
+ else if (server && server->IsJuped())
+ source.Reply(_("You can not jupe an already juped server."));
+ else
+ {
+ Anope::string rbuf = "Juped by " + source.GetNick() + (!reason.empty() ? ": " + reason : "");
+ /* Generate the new sid before quitting the old server, so they can't collide */
+ Anope::string sid = IRCD->SID_Retrieve();
+ if (server)
+ {
+ IRCD->SendSquit(server, rbuf);
+ server->Delete(rbuf);
+ }
+ Server *juped_server = new Server(Me, jserver, 1, rbuf, sid, true);
+ IRCD->SendServer(juped_server);
+
+ Log(LOG_ADMIN, source, this) << "on " << jserver << " (" << rbuf << ")";
+ }
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Tells services to jupiter a server -- that is, to create a fake \"server\" connected to Services which prevents the real server of that name from connecting."
+ " The jupe may be removed using a standard \002SQUIT\002. If a reason is given, it is placed in the server information field; otherwise, the server information field will contain the text \"Juped by <nick>\", showing the nickname of the person who jupitered the server."));
+ return true;
+ }
+};
+
+class OSJupe : public Module
+{
+ CommandOSJupe commandosjupe;
+
+ public:
+ OSJupe(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandosjupe(this)
+ {
+
+ }
+};
+
+MODULE_INIT(OSJupe)
diff --git a/modules/operserv/kick.cpp b/modules/operserv/kick.cpp
new file mode 100644
index 000000000..7f1c20670
--- /dev/null
+++ b/modules/operserv/kick.cpp
@@ -0,0 +1,89 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+
+class CommandOSKick : public Command
+{
+ public:
+ CommandOSKick(Module *creator) : Command(creator, "operserv/kick", 3, 3)
+ {
+ this->SetDesc(_("Kick a user from a channel"));
+ this->SetSyntax(_("\037channel\037 \037user\037 \037reason\037"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &chan = params[0];
+ const Anope::string &nick = params[1];
+ const Anope::string &s = params[2];
+ Channel *c;
+ User *u2;
+
+ if (!(c = Channel::Find(chan)))
+ {
+ source.Reply(_("Channel \002%s\002 doesn't exist."), chan);
+ return;
+ }
+
+ if (c->bouncy_modes)
+ {
+ source.Reply(_("Services is unable to change modes. Are your servers' U:lines configured correctly?"));
+ return;
+ }
+
+ if (!(u2 = User::Find(nick, true)))
+ {
+ source.Reply(_("\002{0}\002 isn't currently online."), nick);
+ return;
+ }
+
+ if (!c->Kick(source.service, u2, "%s (%s)", source.GetNick().c_str(), s.c_str()))
+ {
+ source.Reply(_("Access denied."));
+ return;
+ }
+
+ Log(LOG_ADMIN, source, this) << "on " << u2->nick << " in " << c->name << " (" << s << ")";
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Allows you to kick a user from any channel. Parameters are the same as for the standard /KICK command."
+ " The kick message will have the nickname of the IRCop sending the KICK command prepended; for example:\n"
+ "\n"
+ "*** SpamMan has been kicked off channel #my_channel by {0} (Alcan (Flood))"),
+ source.service->nick);
+ return true;
+ }
+};
+
+class OSKick : public Module
+{
+ CommandOSKick commandoskick;
+
+ public:
+ OSKick(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandoskick(this)
+ {
+
+ }
+};
+
+MODULE_INIT(OSKick)
diff --git a/modules/commands/os_kill.cpp b/modules/operserv/kill.cpp
index 12fcda99e..b2745272e 100644
--- a/modules/commands/os_kill.cpp
+++ b/modules/operserv/kill.cpp
@@ -1,12 +1,20 @@
-/* OperServ core functions
+/*
+ * Anope IRC Services
*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2011-2016 Anope Team <team@anope.org>
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
*
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "module.h"
@@ -20,16 +28,16 @@ class CommandOSKill : public Command
this->SetSyntax(_("\037user\037 [\037reason\037]"));
}
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
{
const Anope::string &nick = params[0];
Anope::string reason = params.size() > 1 ? params[1] : "";
User *u2 = User::Find(nick, true);
if (u2 == NULL)
- source.Reply(NICK_X_NOT_IN_USE, nick.c_str());
+ source.Reply(_("\002{0}\002 isn't currently online."), nick);
else if (u2->IsProtected() || u2->server == Me)
- source.Reply(ACCESS_DENIED);
+ source.Reply(_("\002{0}\002 is protected and cannot be killed."), u2->nick);
else
{
if (reason.empty())
@@ -41,13 +49,9 @@ class CommandOSKill : public Command
}
}
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
{
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Allows you to kill a user from the network.\n"
- "Parameters are the same as for the standard /KILL\n"
- "command."));
+ source.Reply(_("Allows you to kill a user from the network. Parameters are the same as for the standard /KILL command."));
return true;
}
};
@@ -57,8 +61,8 @@ class OSKill : public Module
CommandOSKill commandoskill;
public:
- OSKill(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandoskill(this)
+ OSKill(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandoskill(this)
{
}
diff --git a/modules/commands/os_list.cpp b/modules/operserv/list.cpp
index 27e02cb0a..00b7bf746 100644
--- a/modules/commands/os_list.cpp
+++ b/modules/operserv/list.cpp
@@ -1,12 +1,20 @@
-/* OperServ core functions
+/*
+ * Anope IRC Services
*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
*
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "module.h"
@@ -20,7 +28,7 @@ class CommandOSChanList : public Command
this->SetSyntax(_("[{\037pattern\037 | \037nick\037} [\037SECRET\037]]"));
}
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
{
const Anope::string &pattern = !params.empty() ? params[0] : "";
const Anope::string &opt = params.size() > 1 ? params[1] : "";
@@ -43,7 +51,7 @@ class CommandOSChanList : public Command
if (!pattern.empty() && (u2 = User::Find(pattern, true)))
{
- source.Reply(_("\002%s\002 channel list:"), u2->nick.c_str());
+ source.Reply(_("Channel list for \002{0}\002:"), u2->nick);
for (User::ChanUserList::iterator uit = u2->chans.begin(), uit_end = u2->chans.end(); uit != uit_end; ++uit)
{
@@ -95,24 +103,18 @@ class CommandOSChanList : public Command
source.Reply(_("End of channel list."));
}
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
{
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Lists all channels currently in use on the IRC network, whether they\n"
- "are registered or not.\n"
- " \n"
- "If \002pattern\002 is given, lists only channels that match it. If a nickname\n"
- "is given, lists only the channels the user using it is on. If SECRET is\n"
- "specified, lists only channels matching \002pattern\002 that have the +s or\n"
- "+p mode."));
-
- const Anope::string &regexengine = Config->GetBlock("options")->Get<const Anope::string>("regexengine");
+ source.Reply(_("Lists all channels currently in use on the IRC network, whether they are registered or not.\n"
+ "\n"
+ "If \002pattern\002 is given, lists only channels that match it. If a nicknam is given, lists only the channels the user using it is on."
+ " If SECRET is specified, lists only channels matching \002pattern\002 that have the +s or +p mode."));
+
+ const Anope::string &regexengine = Config->GetBlock("options")->Get<Anope::string>("regexengine");
if (!regexengine.empty())
{
source.Reply(" ");
- source.Reply(_("Regex matches are also supported using the %s engine.\n"
- "Enclose your pattern in // if this is desired."), regexengine.c_str());
+ source.Reply(_("Regex matches are also supported using the {0} engine. Enclose your pattern in // if this is desired."), regexengine);
}
return true;
@@ -128,7 +130,7 @@ class CommandOSUserList : public Command
this->SetSyntax(_("[{\037pattern\037 | \037channel\037} [\037INVISIBLE\037]]"));
}
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
{
const Anope::string &pattern = !params.empty() ? params[0] : "";
const Anope::string &opt = params.size() > 1 ? params[1] : "";
@@ -148,7 +150,7 @@ class CommandOSUserList : public Command
if (!pattern.empty() && (c = Channel::Find(pattern)))
{
- source.Reply(_("\002%s\002 users list:"), pattern.c_str());
+ source.Reply(_("User list for \002{0}\002:"), pattern);
for (Channel::ChanUserList::iterator cuit = c->users.begin(), cuit_end = c->users.end(); cuit != cuit_end; ++cuit)
{
@@ -206,19 +208,16 @@ class CommandOSUserList : public Command
return;
}
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
{
this->SendSyntax(source);
source.Reply(" ");
- source.Reply(_("Lists all users currently online on the IRC network, whether their\n"
- "nick is registered or not.\n"
- " \n"
- "If \002pattern\002 is given, lists only users that match it (it must be in\n"
- "the format nick!user@host). If \002channel\002 is given, lists only users\n"
- "that are on the given channel. If INVISIBLE is specified, only users\n"
- "with the +i flag will be listed."));
-
- const Anope::string &regexengine = Config->GetBlock("options")->Get<const Anope::string>("regexengine");
+ source.Reply(_("Lists all users currently online on the IRC network, whether their nickname is registered or not.\n"
+ "\n"
+ "If \002pattern\002 is given, lists only users that match it (it must be in the format nick!user@host)."
+ " If \002channel\002 is given, lists only users that are on the given channel. If INVISIBLE is specified, only users with the +i flag will be listed."));
+
+ const Anope::string &regexengine = Config->GetBlock("options")->Get<Anope::string>("regexengine");
if (!regexengine.empty())
{
source.Reply(" ");
@@ -236,8 +235,9 @@ class OSList : public Module
CommandOSUserList commandosuserlist;
public:
- OSList(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandoschanlist(this), commandosuserlist(this)
+ OSList(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandoschanlist(this)
+ , commandosuserlist(this)
{
}
diff --git a/modules/operserv/login.cpp b/modules/operserv/login.cpp
new file mode 100644
index 000000000..180024eac
--- /dev/null
+++ b/modules/operserv/login.cpp
@@ -0,0 +1,156 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2011-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+
+class CommandOSLogin : public Command
+{
+ public:
+ CommandOSLogin(Module *creator) : Command(creator, "operserv/login", 1, 1)
+ {
+ this->SetSyntax(_("\037password\037"));
+ this->RequireUser(true);
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &password = params[0];
+
+ User *u = source.GetUser();
+ Oper *o = source.nc->o;
+ if (o == NULL)
+ {
+ source.Reply(_("No oper block for your nickname."));
+ return;
+ }
+
+ if (o->GetPassword().empty())
+ {
+ source.Reply(_("Your oper block doesn't require logging in."));
+ return;
+ }
+
+ if (u->HasExtOK("os_login"))
+ {
+ source.Reply(_("You are already logged in."));
+ return;
+ }
+
+ if (o->GetPassword() != password)
+ {
+ source.Reply(_("Password incorrect."));
+ u->BadPassword();
+ return;
+ }
+
+ Log(LOG_ADMIN, source, this) << "and successfully identified to " << source.service->nick;
+ u->Extend<bool>("os_login", true);
+ source.Reply(_("Password accepted."));
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Logs you in to {0} so you gain Services Operator privileges. This command is unnecessary if your oper block is configured without a password."), source.service->nick);
+ return true;
+ }
+
+ const Anope::string GetDesc(CommandSource &source) const override
+ {
+ return Anope::printf(Language::Translate(source.GetAccount(), _("Login to %s")), source.service->nick.c_str());
+ }
+};
+
+class CommandOSLogout : public Command
+{
+ public:
+ CommandOSLogout(Module *creator) : Command(creator, "operserv/logout", 0, 0)
+ {
+ this->RequireUser(true);
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ User *u = source.GetUser();
+ Oper *o = source.nc->o;
+ if (o == NULL)
+ {
+ source.Reply(_("No oper block for your nick."));
+ return;
+ }
+
+ if (o->GetPassword().empty())
+ {
+ source.Reply(_("Your oper block doesn't require logging in."));
+ return;
+ }
+
+ if (!u->HasExtOK("os_login"))
+ {
+ source.Reply(_("You are not identified."));
+ return;
+ }
+
+ Log(LOG_ADMIN, source, this);
+ u->Shrink<bool>("os_login");
+ source.Reply(_("You have been logged out."));
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Logs you out from %s so you lose Services Operator privileges. This command is only useful if your oper block is configured with a password."), source.service->nick);
+ return true;
+ }
+
+ const Anope::string GetDesc(CommandSource &source) const override
+ {
+ return Anope::printf(Language::Translate(source.GetAccount(), _("Logout from %s")), source.service->nick.c_str());
+ }
+};
+
+class OSLogin : public Module
+ , public EventHook<Event::IsServicesOperEvent>
+{
+ CommandOSLogin commandoslogin;
+ CommandOSLogout commandoslogout;
+ ExtensibleItem<bool> os_login;
+
+ public:
+ OSLogin(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , EventHook<Event::IsServicesOperEvent>(this)
+ , commandoslogin(this)
+ , commandoslogout(this)
+ , os_login(this, "os_login")
+ {
+
+ }
+
+ EventReturn IsServicesOper(User *u) override
+ {
+ if (!u->Account()->o->GetPassword().empty())
+ {
+ if (os_login.HasExt(u))
+ return EVENT_ALLOW;
+ return EVENT_STOP;
+ }
+
+ return EVENT_CONTINUE;
+ }
+};
+
+MODULE_INIT(OSLogin)
diff --git a/modules/commands/os_logsearch.cpp b/modules/operserv/logsearch.cpp
index f8ee99d4e..85a5834a7 100644
--- a/modules/commands/os_logsearch.cpp
+++ b/modules/operserv/logsearch.cpp
@@ -1,12 +1,20 @@
-/* OperServ core functions
+/*
+ * Anope IRC Services
*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
*
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "module.h"
@@ -18,7 +26,7 @@ class CommandOSLogSearch : public Command
char timestamp[32];
tm *tm = localtime(&t);
-
+
strftime(timestamp, sizeof(timestamp), "%Y%m%d", tm);
return Anope::LogDir + "/" + file + "." + timestamp;
@@ -31,7 +39,7 @@ class CommandOSLogSearch : public Command
this->SetSyntax(_("[+\037days\037d] [+\037limit\037l] \037pattern\037"));
}
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
{
int days = 7, replies = 50;
@@ -52,7 +60,7 @@ class CommandOSLogSearch : public Command
}
catch (const ConvertException &)
{
- source.Reply(_("Invalid duration %s, using %d days."), dur.c_str(), days);
+ source.Reply(_("Invalid duration \002{0}\002, using \002{1}\002 days."), dur, days);
}
}
break;
@@ -68,12 +76,12 @@ class CommandOSLogSearch : public Command
}
catch (const ConvertException &)
{
- source.Reply(_("Invalid limit %s, using %d."), dur.c_str(), replies);
+ source.Reply(_("Invalid limit \002{0}\002, using \002{1}\002."), dur, replies);
}
}
break;
default:
- source.Reply(_("Unknown parameter: %s"), params[i].c_str());
+ source.Reply(_("Unknown parameter: {0}"), params[i]);
}
}
@@ -89,7 +97,7 @@ class CommandOSLogSearch : public Command
Log(LOG_ADMIN, source, this) << "for " << search_string;
- const Anope::string &logfile_name = Config->GetModule(this->owner)->Get<const Anope::string>("logname");
+ const Anope::string &logfile_name = Config->GetModule(this->GetOwner())->Get<Anope::string>("logname");
std::list<Anope::string> matches;
for (int d = days - 1; d >= 0; --d)
{
@@ -109,35 +117,30 @@ class CommandOSLogSearch : public Command
unsigned found = matches.size();
if (!found)
{
- source.Reply(_("No matches for \002%s\002 found."), search_string.c_str());
+ source.Reply(_("No matches for \002{0}\002 found."), search_string);
return;
}
while (matches.size() > static_cast<unsigned>(replies))
matches.pop_front();
- source.Reply(_("Matches for \002%s\002:"), search_string.c_str());
+ source.Reply(_("Matches for \002{0}\002:"), search_string);
unsigned count = 0;
for (std::list<Anope::string>::iterator it = matches.begin(), it_end = matches.end(); it != it_end; ++it)
source.Reply("#%d: %s", ++count, it->c_str());
- source.Reply(_("Showed %d/%d matches for \002%s\002."), matches.size(), found, search_string.c_str());
+ source.Reply(_("Showed \002{0}/{1}\002 matches for \002{2}\002."), matches.size(), found, search_string);
}
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
{
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("This command searches the Services logfiles for messages\n"
- "that match the given pattern. The day and limit argument\n"
- "may be used to specify how many days of logs to search\n"
- "and the number of replies to limit to. By default this\n"
- "command searches one week of logs, and limits replies\n"
- "to 50.\n"
- " \n"
- "For example:\n"
- " \002LOGSEARCH +21d +500l Anope\002\n"
- " Searches the last 21 days worth of logs for messages\n"
- " containing Anope and lists the most recent 500 of them."));
+ source.Reply(_("This command searches the Services logfiles for messages that match the given pattern."
+ " The day and limit argument may be used to specify how many days of logs to search and the number of replies to limit to."
+ " By default this command searches one week of logs, and limits replies to 50.\n"
+ "\n"
+ "Example:\n"
+ " {0} +21d +500l Anope\n"
+ " Searches the last 21 days worth of logs for messages containing \"Anope\" and lists the most recent 500 of them."),
+ source.command);
return true;
}
};
@@ -147,8 +150,8 @@ class OSLogSearch : public Module
CommandOSLogSearch commandoslogsearch;
public:
- OSLogSearch(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandoslogsearch(this)
+ OSLogSearch(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandoslogsearch(this)
{
}
};
diff --git a/modules/operserv/main/CMakeLists.txt b/modules/operserv/main/CMakeLists.txt
new file mode 100644
index 000000000..781f0ef1f
--- /dev/null
+++ b/modules/operserv/main/CMakeLists.txt
@@ -0,0 +1 @@
+build_subdir(${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/modules/operserv/main/operserv.cpp b/modules/operserv/main/operserv.cpp
new file mode 100644
index 000000000..09dc3bbac
--- /dev/null
+++ b/modules/operserv/main/operserv.cpp
@@ -0,0 +1,329 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2011-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/help.h"
+#include "modules/nickserv.h"
+
+class SGLineManager : public XLineManager
+{
+ public:
+ SGLineManager(Module *creator) : XLineManager(creator, "xlinemanager/sgline", 'G') { }
+
+ void OnMatch(User *u, XLine *x) override
+ {
+ this->Send(u, x);
+ }
+
+ void OnExpire(XLine *x) override
+ {
+ ::Log(Config->GetClient("OperServ"), "expire/akill") << "AKILL on \002" << x->GetMask() << "\002 has expired";
+ }
+
+ void Send(User *u, XLine *x) override
+ {
+ IRCD->SendAkill(u, x);
+ }
+
+ void SendDel(XLine *x) override
+ {
+ IRCD->SendAkillDel(x);
+ }
+
+ bool Check(User *u, XLine *x) override
+ {
+ if (x->regex)
+ {
+ Anope::string uh = u->GetIdent() + "@" + u->host, nuhr = u->nick + "!" + uh + "#" + u->realname;
+ return std::regex_match(uh.str(), *x->regex) || std::regex_match(nuhr.str(), *x->regex);
+ }
+
+ if (!x->GetNick().empty() && !Anope::Match(u->nick, x->GetNick()))
+ return false;
+
+ if (!x->GetUser().empty() && !Anope::Match(u->GetIdent(), x->GetUser()))
+ return false;
+
+ if (!x->GetReal().empty() && !Anope::Match(u->realname, x->GetReal()))
+ return false;
+
+ if (x->c && x->c->match(u->ip))
+ return true;
+
+ if (x->GetHost().empty() || Anope::Match(u->host, x->GetHost()) || Anope::Match(u->ip.addr(), x->GetHost()))
+ return true;
+
+ return false;
+ }
+};
+
+class SQLineManager : public XLineManager
+{
+ public:
+ SQLineManager(Module *creator) : XLineManager(creator, "xlinemanager/sqline", 'Q') { }
+
+ void OnMatch(User *u, XLine *x) override
+ {
+ this->Send(u, x);
+ }
+
+ void OnExpire(XLine *x) override
+ {
+ ::Log(Config->GetClient("OperServ"), "expire/sqline") << "SQLINE on \002" << x->GetMask() << "\002 has expired";
+ }
+
+ void Send(User *u, XLine *x) override
+ {
+ if (!IRCD->CanSQLine)
+ {
+ if (!u)
+ ;
+ else if (NickServ::service)
+ NickServ::service->Collide(u, NULL);
+ else
+ u->Kill(Config->GetClient("OperServ"), "Q-Lined: " + x->GetReason());
+ }
+ else if (x->IsRegex())
+ {
+ if (u)
+ u->Kill(Config->GetClient("OperServ"), "Q-Lined: " + x->GetReason());
+ }
+ else if (x->GetMask()[0] != '#' || IRCD->CanSQLineChannel)
+ {
+ IRCD->SendSQLine(u, x);
+ /* If it is an oper, assume they're walking it, otherwise kill for good measure */
+ if (u && !u->HasMode("OPER"))
+ u->Kill(Config->GetClient("OperServ"), "Q-Lined: " + x->GetReason());
+ }
+ }
+
+ void SendDel(XLine *x) override
+ {
+ if (!IRCD->CanSQLine || x->IsRegex())
+ ;
+ else if (x->GetMask()[0] != '#' || IRCD->CanSQLineChannel)
+ IRCD->SendSQLineDel(x);
+ }
+
+ bool Check(User *u, XLine *x) override
+ {
+ if (x->regex)
+ return std::regex_match(u->nick.c_str(), *x->regex);
+ return Anope::Match(u->nick, x->GetMask());
+ }
+
+ XLine *CheckChannel(Channel *c)
+ {
+ for (XLine *x : this->GetXLines())
+ {
+ if (x->regex)
+ {
+ if (std::regex_match(c->name.str(), *x->regex))
+ return x;
+ }
+ else
+ {
+ if (x->GetMask().empty() || x->GetMask()[0] != '#')
+ continue;
+
+ if (Anope::Match(c->name, x->GetMask(), false, true))
+ return x;
+ }
+ }
+ return nullptr;
+ }
+};
+
+class SNLineManager : public XLineManager
+{
+ public:
+ SNLineManager(Module *creator) : XLineManager(creator, "xlinemanager/snline", 'N') { }
+
+ void OnMatch(User *u, XLine *x) override
+ {
+ this->Send(u, x);
+ }
+
+ void OnExpire(XLine *x) override
+ {
+ ::Log(Config->GetClient("OperServ"), "expire/snline") << "SNLINE on \002" << x->GetMask() << "\002 has expired";
+ }
+
+ void Send(User *u, XLine *x) override
+ {
+ if (IRCD->CanSNLine && !x->IsRegex())
+ IRCD->SendSGLine(u, x);
+
+ if (u)
+ u->Kill(Config->GetClient("OperServ"), "SNLined: " + x->GetReason());
+ }
+
+ void SendDel(XLine *x) override
+ {
+ if (IRCD->CanSNLine && !x->IsRegex())
+ IRCD->SendSGLineDel(x);
+ }
+
+ bool Check(User *u, XLine *x) override
+ {
+ if (x->regex)
+ return std::regex_match(u->realname.str(), *x->regex);
+ return Anope::Match(u->realname, x->GetMask(), false, true);
+ }
+};
+
+class OperServCore : public Module
+ , public EventHook<Event::BotPrivmsg>
+ , public EventHook<Event::ServerQuit>
+ , public EventHook<Event::UserModeSet>
+ , public EventHook<Event::UserModeUnset>
+ , public EventHook<Event::UserConnect>
+ , public EventHook<Event::UserNickChange>
+ , public EventHook<Event::CheckKick>
+ , public EventHook<Event::Help>
+ , public EventHook<Event::Log>
+{
+ Reference<ServiceBot> OperServ;
+ SGLineManager sglines;
+ SQLineManager sqlines;
+ SNLineManager snlines;
+
+ public:
+ OperServCore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, PSEUDOCLIENT | VENDOR)
+ , EventHook<Event::BotPrivmsg>(this)
+ , EventHook<Event::ServerQuit>(this)
+ , EventHook<Event::UserModeSet>(this)
+ , EventHook<Event::UserModeUnset>(this)
+ , EventHook<Event::UserConnect>(this)
+ , EventHook<Event::UserNickChange>(this)
+ , EventHook<Event::CheckKick>(this)
+ , EventHook<Event::Help>(this)
+ , EventHook<Event::Log>(this)
+ , sglines(this)
+ , sqlines(this)
+ , snlines(this)
+ {
+
+ /* Yes, these are in this order for a reason. Most violent->least violent. */
+ XLineManager::RegisterXLineManager(&sglines);
+ XLineManager::RegisterXLineManager(&sqlines);
+ XLineManager::RegisterXLineManager(&snlines);
+ }
+
+ ~OperServCore()
+ {
+ this->sglines.Clear();
+ this->sqlines.Clear();
+ this->snlines.Clear();
+
+ XLineManager::UnregisterXLineManager(&sglines);
+ XLineManager::UnregisterXLineManager(&sqlines);
+ XLineManager::UnregisterXLineManager(&snlines);
+ }
+
+ void OnReload(Configuration::Conf *conf) override
+ {
+ const Anope::string &osnick = conf->GetModule(this)->Get<Anope::string>("client");
+
+ if (osnick.empty())
+ throw ConfigException(Module::name + ": <client> must be defined");
+
+ ServiceBot *bi = ServiceBot::Find(osnick, true);
+ if (!bi)
+ throw ConfigException(Module::name + ": no bot named " + osnick);
+
+ OperServ = bi;
+ }
+
+ EventReturn OnBotPrivmsg(User *u, ServiceBot *bi, Anope::string &message) override
+ {
+ if (bi == OperServ && !u->HasMode("OPER") && Config->GetModule(this)->Get<bool>("opersonly"))
+ {
+ u->SendMessage(bi, "Access denied.");
+ ::Log(bi, "bados") << "Denied access to " << bi->nick << " from " << u->GetMask() << " (non-oper)";
+ return EVENT_STOP;
+ }
+
+ return EVENT_CONTINUE;
+ }
+
+ void OnServerQuit(Server *server) override
+ {
+ if (server->IsJuped())
+ ::Log(server, "squit", OperServ) << "Received SQUIT for juped server " << server->GetName();
+ }
+
+ void OnUserModeSet(const MessageSource &setter, User *u, const Anope::string &mname) override
+ {
+ if (mname == "OPER")
+ ::Log(u, "oper", OperServ) << "is now an IRC operator.";
+ }
+
+ void OnUserModeUnset(const MessageSource &setter, User *u, const Anope::string &mname) override
+ {
+ if (mname == "OPER")
+ ::Log(u, "oper", OperServ) << "is no longer an IRC operator";
+ }
+
+ void OnUserConnect(User *u, bool &exempt) override
+ {
+ if (!u->Quitting() && !exempt)
+ XLineManager::CheckAll(u);
+ }
+
+ void OnUserNickChange(User *u, const Anope::string &oldnick) override
+ {
+ if (!u->HasMode("OPER"))
+ this->sqlines.CheckAllXLines(u);
+ }
+
+ EventReturn OnCheckKick(User *u, Channel *c, Anope::string &mask, Anope::string &reason) override
+ {
+ XLine *x = this->sqlines.CheckChannel(c);
+ if (x)
+ {
+ this->sqlines.OnMatch(u, x);
+ reason = x->GetReason();
+ return EVENT_STOP;
+ }
+
+ return EVENT_CONTINUE;
+ }
+
+ EventReturn OnPreHelp(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ if (!params.empty() || source.c || source.service != *OperServ)
+ return EVENT_CONTINUE;
+ source.Reply(_("%s commands:"), OperServ->nick.c_str());
+ return EVENT_CONTINUE;
+ }
+
+ void OnPostHelp(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ }
+
+ void OnLog(::Log *l) override
+ {
+ if (l->type == LOG_SERVER)
+ l->bi = OperServ;
+ }
+};
+
+MODULE_INIT(OperServCore)
+
diff --git a/modules/commands/os_mode.cpp b/modules/operserv/mode.cpp
index 378f6b082..a159dc3e3 100644
--- a/modules/commands/os_mode.cpp
+++ b/modules/operserv/mode.cpp
@@ -1,12 +1,20 @@
-/* OperServ core functions
+/*
+ * Anope IRC Services
*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
*
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "module.h"
@@ -21,14 +29,14 @@ class CommandOSMode : public Command
this->SetSyntax(_("\037channel\037 CLEAR [ALL]"));
}
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
{
const Anope::string &target = params[0];
const Anope::string &modes = params[1];
Reference<Channel> c = Channel::Find(target);
if (!c)
- source.Reply(CHAN_X_NOT_IN_USE, target.c_str());
+ source.Reply(_("Channel \002{0}\002 doesn't exist."), target);
else if (c->bouncy_modes)
source.Reply(_("Services is unable to change modes. Are your servers' U:lines configured correctly?"));
else if (modes.equals_ci("CLEAR"))
@@ -58,10 +66,10 @@ class CommandOSMode : public Command
c->RemoveMode(c->ci->WhoSends(), ModeManager::FindChannelModeByChar(uc->status.Modes()[i - 1]), uc->user->GetUID(), false);
}
- source.Reply(_("All modes cleared on %s."), c->name.c_str());
+ source.Reply(_("All modes cleared on \002{0}\002."), c->name);
}
else
- source.Reply(_("Non-status modes cleared on %s."), c->name.c_str());
+ source.Reply(_("Non-status modes cleared on \002{0}\002."), c->name);
}
else
{
@@ -126,12 +134,9 @@ class CommandOSMode : public Command
}
}
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
{
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Allows Services Operators to change modes for any channel.\n"
- "Parameters are the same as for the standard /MODE command.\n"
+ source.Reply(_("Allows Services Operators to change modes for any channel. Parameters are the same as for the standard /MODE command.\n"
"Alternatively, CLEAR may be given to clear all modes on the channel.\n"
"If CLEAR ALL is given then all modes, including user status, is removed."));
return true;
@@ -147,31 +152,28 @@ class CommandOSUMode : public Command
this->SetSyntax(_("\037user\037 \037modes\037"));
}
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
{
const Anope::string &target = params[0];
const Anope::string &modes = params[1];
User *u2 = User::Find(target, true);
if (!u2)
- source.Reply(NICK_X_NOT_IN_USE, target.c_str());
+ source.Reply(_("\002{0}\002 isn't currently online."), target);
else
{
u2->SetModes(source.service, "%s", modes.c_str());
- source.Reply(_("Changed usermodes of \002%s\002 to %s."), u2->nick.c_str(), modes.c_str());
+ source.Reply(_("Changed usermodes of \002{0}\002 to \002{1}\002."), u2->nick.c_str(), modes.c_str());
- u2->SendMessage(source.service, _("\002%s\002 changed your usermodes to %s."), source.GetNick().c_str(), modes.c_str());
+ u2->SendMessage(*source.service, _("\002{0}\002 changed your usermodes to \002{1}\002."), source.GetNick(), modes);
Log(LOG_ADMIN, source, this) << modes << " on " << target;
}
}
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
{
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Allows Services Operators to change modes for any user.\n"
- "Parameters are the same as for the standard /MODE command."));
+ source.Reply(_("Allows Services Operators to change modes for any user. Parameters are the same as for the standard /MODE command."));
return true;
}
};
@@ -182,8 +184,9 @@ class OSMode : public Module
CommandOSUMode commandosumode;
public:
- OSMode(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandosmode(this), commandosumode(this)
+ OSMode(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandosmode(this)
+ , commandosumode(this)
{
}
diff --git a/modules/commands/os_modinfo.cpp b/modules/operserv/modinfo.cpp
index b1f0c7470..57edc5c40 100644
--- a/modules/commands/os_modinfo.cpp
+++ b/modules/operserv/modinfo.cpp
@@ -1,12 +1,20 @@
-/* OperServ core functions
+/*
+ * Anope IRC Services
*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
*
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "module.h"
@@ -20,7 +28,7 @@ class CommandOSModInfo : public Command
this->SetSyntax(_("\037modname\037"));
}
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
{
const Anope::string &file = params[0];
@@ -29,44 +37,44 @@ class CommandOSModInfo : public Command
Module *m = ModuleManager::FindModule(file);
if (m)
{
- source.Reply(_("Module: \002%s\002 Version: \002%s\002 Author: \002%s\002 Loaded: \002%s\002"), m->name.c_str(), !m->version.empty() ? m->version.c_str() : "?", !m->author.empty() ? m->author.c_str() : "Unknown", Anope::strftime(m->created, source.GetAccount()).c_str());
+ source.Reply(_("Module: \002{0}\002 Version: \002{1}\002 Author: \002{2}\002 Loaded: \002{3}\002"), m->name, !m->version.empty() ? m->version : "?", !m->author.empty() ? m->author : "Unknown", Anope::strftime(m->created, source.GetAccount()));
if (Anope::Debug)
- source.Reply(_(" Loaded at: %p"), m->handle);
+ source.Reply(_(" Loaded at: {0}"), Anope::printf("0x%x", m->handle));
- std::vector<Anope::string> servicekeys = Service::GetServiceKeys("Command");
- for (unsigned i = 0; i < servicekeys.size(); ++i)
+ std::vector<Command *> commands = ServiceManager::Get()->FindServices<Command *>();
+ for (Command *c : commands)
{
- ServiceReference<Command> c("Command", servicekeys[i]);
- if (!c || c->owner != m)
+ if (c->GetOwner() != m)
continue;
- source.Reply(_(" Providing service: \002%s\002"), c->name.c_str());
+ source.Reply(_(" Providing service: \002{0}\002"), c->GetName());
- for (botinfo_map::const_iterator it = BotListByNick->begin(), it_end = BotListByNick->end(); it != it_end; ++it)
+ for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it)
{
- const BotInfo *bi = it->second;
+ User *u = it->second;
+
+ if (u->type != UserType::BOT)
+ continue;
+
+ ServiceBot *bi = anope_dynamic_static_cast<ServiceBot *>(u);
for (CommandInfo::map::const_iterator cit = bi->commands.begin(), cit_end = bi->commands.end(); cit != cit_end; ++cit)
{
const Anope::string &c_name = cit->first;
const CommandInfo &info = cit->second;
- if (info.name != c->name)
+ if (info.name != c->GetName())
continue;
- source.Reply(_(" Command \002%s\002 on \002%s\002 is linked to \002%s\002"), c_name.c_str(), bi->nick.c_str(), c->name.c_str());
+ source.Reply(_(" Command \002{0}\002 on \002{1}\002 is linked to \002{2}\002"), c_name, bi->nick, c->GetName());
}
}
}
}
else
- source.Reply(_("No information about module \002%s\002 is available."), file.c_str());
-
- return;
+ source.Reply(_("No information about module \002{0}\002 is available."), file);
}
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
{
- this->SendSyntax(source);
- source.Reply(" ");
source.Reply(_("This command lists information about the specified loaded module."));
return true;
}
@@ -81,7 +89,7 @@ class CommandOSModList : public Command
this->SetSyntax("[all|third|vendor|extra|database|encryption|pseudoclient|protocol]");
}
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
{
const Anope::string &param = !params.empty() ? params[0] : "";
@@ -180,19 +188,17 @@ class CommandOSModList : public Command
++count;
- source.Reply(_("Module: \002%s\002 [%s] [%s]"), m->name.c_str(), m->version.c_str(), mtype.c_str());
+ source.Reply(_("Module: \002{0}\002 [{1}] [{2}]"), m->name, m->version, mtype);
}
if (!count)
source.Reply(_("No modules currently loaded matching that criteria."));
else
- source.Reply(_("%d modules loaded."), count);
+ source.Reply(_("\002{0}\002 modules loaded."), count);
}
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
{
- this->SendSyntax(source);
- source.Reply(" ");
source.Reply(_("Lists currently loaded modules."));
return true;
}
@@ -204,8 +210,9 @@ class OSModInfo : public Module
CommandOSModList commandosmodlist;
public:
- OSModInfo(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandosmodinfo(this), commandosmodlist(this)
+ OSModInfo(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandosmodinfo(this)
+ , commandosmodlist(this)
{
}
diff --git a/modules/commands/os_module.cpp b/modules/operserv/module.cpp
index f271ed2c9..88d1def03 100644
--- a/modules/commands/os_module.cpp
+++ b/modules/operserv/module.cpp
@@ -1,12 +1,20 @@
-/* OperServ core functions
+/*
+ * Anope IRC Services
*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
*
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "module.h"
@@ -20,7 +28,7 @@ class CommandOSModLoad : public Command
this->SetSyntax(_("\037modname\037"));
}
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
{
const Anope::string &mname = params[0];
@@ -28,22 +36,17 @@ class CommandOSModLoad : public Command
if (status == MOD_ERR_OK)
{
Log(LOG_ADMIN, source, this) << "to load module " << mname;
- source.Reply(_("Module \002%s\002 loaded."), mname.c_str());
+ source.Reply(_("Module \002{0}\002 loaded."), mname);
}
else if (status == MOD_ERR_EXISTS)
- source.Reply(_("Module \002%s\002 is already loaded."), mname.c_str());
+ source.Reply(_("Module \002{0}\002 is already loaded."), mname);
else
- source.Reply(_("Unable to load module \002%s\002."), mname.c_str());
-
- return;
+ source.Reply(_("Unable to load module \002{0}\002."), mname);
}
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
{
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("This command loads the module named \037modname\037 from the modules\n"
- "directory."));
+ source.Reply(_("This command loads the module named \037modname\037."));
return true;
}
};
@@ -57,27 +60,27 @@ class CommandOSModReLoad : public Command
this->SetSyntax(_("\037modname\037"));
}
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
{
const Anope::string &mname = params[0];
Module *m = ModuleManager::FindModule(mname);
if (!m)
{
- source.Reply(_("Module \002%s\002 isn't loaded."), mname.c_str());
+ source.Reply(_("Module \002{0}\002 isn't loaded."), mname);
return;
}
if (!m->handle || m->GetPermanent())
{
- source.Reply(_("Unable to remove module \002%s\002."), m->name.c_str());
+ source.Reply(_("Unable to remove module \002{0}\002."), m->name);
return;
}
Module *protocol = ModuleManager::FindFirstOf(PROTOCOL);
if (m->type == PROTOCOL && m != protocol)
{
- source.Reply(_("You can not reload this module directly, instead reload %s."), protocol ? protocol->name.c_str() : "(unknown)");
+ source.Reply(_("You can not reload this module directly, instead reload {0}."), protocol ? protocol->name : "(unknown)");
return;
}
@@ -87,7 +90,7 @@ class CommandOSModReLoad : public Command
if (status != MOD_ERR_OK)
{
- source.Reply(_("Unable to remove module \002%s\002."), mname.c_str());
+ source.Reply(_("Unable to remove module \002{0}\002."), mname);
return;
}
@@ -95,7 +98,7 @@ class CommandOSModReLoad : public Command
if (status == MOD_ERR_OK)
{
Log(LOG_ADMIN, source, this) << "to reload module " << mname;
- source.Reply(_("Module \002%s\002 reloaded."), mname.c_str());
+ source.Reply(_("Module \002{0}\002 reloaded."), mname);
}
else
{
@@ -105,16 +108,12 @@ class CommandOSModReLoad : public Command
Anope::Quitting = true;
}
else
- source.Reply(_("Unable to load module \002%s\002."), mname.c_str());
+ source.Reply(_("Unable to load module \002{0}\002."), mname);
}
-
- return;
}
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
{
- this->SendSyntax(source);
- source.Reply(" ");
source.Reply(_("This command reloads the module named \037modname\037."));
return true;
}
@@ -129,42 +128,40 @@ class CommandOSModUnLoad : public Command
this->SetSyntax(_("\037modname\037"));
}
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
{
const Anope::string &mname = params[0];
Module *m = ModuleManager::FindModule(mname);
if (!m)
{
- source.Reply(_("Module \002%s\002 isn't loaded."), mname.c_str());
+ source.Reply(_("Module \002{0}\002 isn't loaded."), mname);
return;
}
-
+
if (!m->handle || m->GetPermanent() || m->type == PROTOCOL)
{
- source.Reply(_("Unable to remove module \002%s\002."), m->name.c_str());
+ source.Reply(_("Unable to remove module \002{0}\002."), m->name);
return;
}
- Log(this->owner) << "Trying to unload module [" << mname << "]";
+ Log(this->GetOwner()) << "Trying to unload module [" << mname << "]";
ModuleReturn status = ModuleManager::UnloadModule(m, source.GetUser());
if (status == MOD_ERR_OK)
{
Log(LOG_ADMIN, source, this) << "to unload module " << mname;
- source.Reply(_("Module \002%s\002 unloaded."), mname.c_str());
+ source.Reply(_("Module \002{0}\002 unloaded."), mname);
}
else
- source.Reply(_("Unable to remove module \002%s\002."), mname.c_str());
+ source.Reply(_("Unable to remove module \002{0}\002."), mname);
return;
}
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
{
- this->SendSyntax(source);
- source.Reply(" ");
source.Reply(_("This command unloads the module named \037modname\037."));
return true;
}
@@ -177,8 +174,10 @@ class OSModule : public Module
CommandOSModUnLoad commandosmodunload;
public:
- OSModule(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandosmodload(this), commandosmodreload(this), commandosmodunload(this)
+ OSModule(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandosmodload(this)
+ , commandosmodreload(this)
+ , commandosmodunload(this)
{
this->SetPermanent(true);
diff --git a/modules/operserv/news.cpp b/modules/operserv/news.cpp
new file mode 100644
index 000000000..91e5a175a
--- /dev/null
+++ b/modules/operserv/news.cpp
@@ -0,0 +1,478 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/operserv/news.h"
+
+/* List of messages for each news type. This simplifies message sending. */
+
+enum
+{
+ MSG_SYNTAX,
+ MSG_LIST_HEADER,
+ MSG_LIST_NONE,
+ MSG_ADDED,
+ MSG_DEL_NOT_FOUND,
+ MSG_DELETED,
+ MSG_DEL_NONE,
+ MSG_DELETED_ALL,
+ MSG_SIZE
+};
+
+static struct NewsMessages
+{
+ NewsType type;
+ Anope::string name;
+ const char *msgs[MSG_SIZE];
+} msgarray[] = {
+ {NEWS_LOGON, "LOGON",
+ {_("LOGONNEWS {ADD|DEL|LIST} [\037text\037|\037num\037]\002"),
+ _("Logon news items:"),
+ _("There is no logon news."),
+ _("Added new logon news item."),
+ _("Logon news item #{0} not found!"),
+ _("Logon news item #{0} deleted."),
+ _("No logon news items to delete!"),
+ _("All logon news items deleted.")}
+ },
+ {NEWS_OPER, "OPER",
+ {_("OPERNEWS {ADD|DEL|LIST} [\037text\037|\037num\037]\002"),
+ _("Oper news items:"),
+ _("There is no oper news."),
+ _("Added new oper news item."),
+ _("Oper news item #{0} not found!"),
+ _("Oper news item #{0} deleted."),
+ _("No oper news items to delete!"),
+ _("All oper news items deleted.")}
+ },
+ {NEWS_RANDOM, "RANDOM",
+ {_("RANDOMNEWS {ADD|DEL|LIST} [\037text\037|\037num\037]\002"),
+ _("Random news items:"),
+ _("There is no random news."),
+ _("Added new random news item."),
+ _("Random news item #{0} not found!"),
+ _("Random news item #{0} deleted."),
+ _("No random news items to delete!"),
+ _("All random news items deleted.")}
+ }
+};
+
+class NewsItemImpl : public NewsItem
+{
+ friend class NewsItemType;
+
+ NewsType type;
+ Anope::string text, who;
+ time_t time = 0;
+
+ public:
+ NewsItemImpl(Serialize::TypeBase *type) : NewsItem(type) { }
+ NewsItemImpl(Serialize::TypeBase *type, Serialize::ID id) : NewsItem(type, id) { }
+
+ NewsType GetNewsType() override;
+ void SetNewsType(const NewsType &) override;
+
+ Anope::string GetText() override;
+ void SetText(const Anope::string &) override;
+
+ Anope::string GetWho() override;
+ void SetWho(const Anope::string &) override;
+
+ time_t GetTime() override;
+ void SetTime(const time_t &) override;
+};
+
+class NewsItemType : public Serialize::Type<NewsItemImpl>
+{
+ public:
+ Serialize::Field<NewsItemImpl, NewsType> type;
+ Serialize::Field<NewsItemImpl, Anope::string> text;
+ Serialize::Field<NewsItemImpl, Anope::string> who;
+ Serialize::Field<NewsItemImpl, time_t> time;
+
+ NewsItemType(Module *me) : Serialize::Type<NewsItemImpl>(me)
+ , type(this, "type", &NewsItemImpl::type)
+ , text(this, "text", &NewsItemImpl::text)
+ , who(this, "who", &NewsItemImpl::who)
+ , time(this, "time", &NewsItemImpl::time)
+ {
+ }
+};
+
+NewsType NewsItemImpl::GetNewsType()
+{
+ return Get(&NewsItemType::type);
+}
+
+void NewsItemImpl::SetNewsType(const NewsType &t)
+{
+ Set(&NewsItemType::type, t);
+}
+
+Anope::string NewsItemImpl::GetText()
+{
+ return Get(&NewsItemType::text);
+}
+
+void NewsItemImpl::SetText(const Anope::string &t)
+{
+ Set(&NewsItemType::text, t);
+}
+
+Anope::string NewsItemImpl::GetWho()
+{
+ return Get(&NewsItemType::who);
+}
+
+void NewsItemImpl::SetWho(const Anope::string &w)
+{
+ Set(&NewsItemType::who, w);
+}
+
+time_t NewsItemImpl::GetTime()
+{
+ return Get(&NewsItemType::time);
+}
+
+void NewsItemImpl::SetTime(const time_t &t)
+{
+ Set(&NewsItemType::time, t);
+}
+
+static const char **findmsgs(NewsType type)
+{
+ for (unsigned i = 0; i < sizeof(msgarray) / sizeof(*msgarray); ++i)
+ if (msgarray[i].type == type)
+ return msgarray[i].msgs;
+ return NULL;
+}
+
+class NewsBase : public Command
+{
+ protected:
+ void DoList(CommandSource &source, NewsType ntype, const char **msgs)
+ {
+ std::vector<NewsItem *> list = Serialize::GetObjects<NewsItem *>();
+
+ if (list.empty())
+ {
+ source.Reply(msgs[MSG_LIST_NONE]);
+ return;
+ }
+
+ ListFormatter lflist(source.GetAccount());
+ lflist.AddColumn(_("Number")).AddColumn(_("Creator")).AddColumn(_("Created")).AddColumn(_("Text"));
+
+ unsigned int i = 1;
+ for (NewsItem *n : list)
+ {
+ ListFormatter::ListEntry entry;
+ entry["Number"] = stringify(i++ + 1);
+ entry["Creator"] = n->GetWho();
+ entry["Created"] = Anope::strftime(n->GetTime(), NULL, true);
+ entry["Text"] = n->GetText();
+ lflist.AddEntry(entry);
+ }
+
+ source.Reply(msgs[MSG_LIST_HEADER]);
+
+ std::vector<Anope::string> replies;
+ lflist.Process(replies);
+
+ for (i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+
+ source.Reply(_("End of news list."));
+ }
+
+ void DoAdd(CommandSource &source, const std::vector<Anope::string> &params, NewsType ntype, const char **msgs)
+ {
+ const Anope::string text = params.size() > 1 ? params[1] : "";
+
+ if (text.empty())
+ {
+ this->OnSyntaxError(source, "ADD");
+ return;
+ }
+
+ if (Anope::ReadOnly)
+ source.Reply(_("Services are in read-only mode. Any changes made may not persist."));
+
+ NewsItem *ni = Serialize::New<NewsItem *>();
+ ni->SetNewsType(ntype);
+ ni->SetText(text);
+ ni->SetTime(Anope::CurTime);
+ ni->SetWho(source.GetNick());
+
+ source.Reply(msgs[MSG_ADDED]);
+ Log(LOG_ADMIN, source, this) << "to add a news item";
+ }
+
+ void DoDel(CommandSource &source, const std::vector<Anope::string> &params, NewsType ntype, const char **msgs)
+ {
+ const Anope::string &text = params.size() > 1 ? params[1] : "";
+ std::vector<NewsItem *> list = Serialize::GetObjects<NewsItem *>();
+
+ if (text.empty())
+ {
+ this->OnSyntaxError(source, "DEL");
+ return;
+ }
+
+ if (list.empty())
+ {
+ source.Reply(msgs[MSG_LIST_NONE]);
+ return;
+ }
+
+ if (Anope::ReadOnly)
+ source.Reply(_("Services are in read-only mode. Any changes made may not persist."));
+
+ if (!text.equals_ci("ALL"))
+ {
+ try
+ {
+ unsigned num = convertTo<unsigned>(text);
+ if (num > 0 && num <= list.size())
+ {
+ list[num - 1]->Delete();
+ source.Reply(msgs[MSG_DELETED], num);
+ Log(LOG_ADMIN, source, this) << "to delete a news item";
+ return;
+ }
+ }
+ catch (const ConvertException &) { }
+
+ source.Reply(msgs[MSG_DEL_NOT_FOUND], text.c_str());
+ }
+ else
+ {
+ for (NewsItem *n : list)
+ n->Delete();
+ source.Reply(msgs[MSG_DELETED_ALL]);
+ Log(LOG_ADMIN, source, this) << "to delete all news items";
+ }
+ }
+
+ void DoNews(CommandSource &source, const std::vector<Anope::string> &params, NewsType ntype)
+ {
+ const Anope::string &cmd = params[0];
+
+ const char **msgs = findmsgs(ntype);
+ if (!msgs)
+ throw CoreException("news: Invalid type to do_news()");
+
+ if (cmd.equals_ci("LIST"))
+ return this->DoList(source, ntype, msgs);
+ else if (cmd.equals_ci("ADD"))
+ return this->DoAdd(source, params, ntype, msgs);
+ else if (cmd.equals_ci("DEL"))
+ return this->DoDel(source, params, ntype, msgs);
+ else
+ this->OnSyntaxError(source, "");
+ }
+ public:
+ NewsBase(Module *creator, const Anope::string &newstype) : Command(creator, newstype, 1, 2)
+ {
+ this->SetSyntax(_("ADD \037text\037"));
+ this->SetSyntax(_("DEL {\037num\037 | ALL}"));
+ this->SetSyntax("LIST");
+ }
+
+ virtual void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_abstract;
+
+ virtual bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_abstract;
+};
+
+class CommandOSLogonNews : public NewsBase
+{
+ public:
+ CommandOSLogonNews(Module *creator) : NewsBase(creator, "operserv/logonnews")
+ {
+ this->SetDesc(_("Define messages to be shown to users at logon"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ return this->DoNews(source, params, NEWS_LOGON);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Edits or displays the list of logon news messages. When a user connects to the network, these messages will be sent to them."
+ " However, no more than \002{0}\002 messages will be sent in order to avoid flooding the user."
+ " If there are more news messages, only the most recent will be sent."),
+ Config->GetModule(this->GetOwner())->Get<unsigned>("newscount", "3"));
+ return true;
+ }
+};
+
+class CommandOSOperNews : public NewsBase
+{
+ public:
+ CommandOSOperNews(Module *creator) : NewsBase(creator, "operserv/opernews")
+ {
+ this->SetDesc(_("Define messages to be shown to users who oper"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ return this->DoNews(source, params, NEWS_OPER);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Edits or displays the list of oper news messages."
+ " When a user opers up (with the /OPER command), these messages will be sent to them."
+ " However, no more than \002{0}\002 messages will be sent in order to avoid flooding the user."
+ " If there are more news messages, only the most recent will be sent."),
+ Config->GetModule(this->GetOwner())->Get<unsigned>("newscount", "3"));
+ return true;
+ }
+};
+
+class CommandOSRandomNews : public NewsBase
+{
+ public:
+ CommandOSRandomNews(Module *creator) : NewsBase(creator, "operserv/randomnews")
+ {
+ this->SetDesc(_("Define messages to be randomly shown to users at logon"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ return this->DoNews(source, params, NEWS_RANDOM);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Edits or displays the list of random news messages. When a user connects to the network, one (and only one) of the random news will be randomly chosen and sent to them."));
+ return true;
+ }
+};
+
+class OSNews : public Module
+ , public EventHook<Event::UserModeSet>
+ , public EventHook<Event::UserConnect>
+{
+ NewsItemType newsitemtype;
+
+ CommandOSLogonNews commandoslogonnews;
+ CommandOSOperNews commandosopernews;
+ CommandOSRandomNews commandosrandomnews;
+
+ Anope::string oper_announcer, announcer;
+ unsigned int news_count;
+ unsigned int cur_rand_news = 0;
+
+ void DisplayNews(User *u, NewsType Type)
+ {
+ std::vector<NewsItem *> list = Serialize::GetObjects<NewsItem *>();
+ if (list.empty())
+ return;
+
+ ServiceBot *bi = NULL;
+ if (Type == NEWS_OPER)
+ bi = ServiceBot::Find(Config->GetModule(this)->Get<Anope::string>("oper_announcer", "OperServ"), true);
+ else
+ bi = ServiceBot::Find(Config->GetModule(this)->Get<Anope::string>("announcer", "Global"), true);
+ if (bi == NULL)
+ return;
+
+ Anope::string msg;
+ if (Type == NEWS_LOGON)
+ msg = _("[\002Logon News\002 - {0}] {1}");
+ else if (Type == NEWS_OPER)
+ msg = _("[\002Oper News\002 - {0}] {1}");
+ else if (Type == NEWS_RANDOM)
+ msg = _("[\002Random News\002 - {0}] {1}");
+ else
+ return;
+
+ unsigned int start;
+ unsigned int end;
+
+ if (Type == NEWS_RANDOM)
+ {
+ /* Reset to head of list to get first random news value */
+ if (cur_rand_news >= list.size())
+ cur_rand_news = 0;
+
+ /* only show one news entry */
+ start = cur_rand_news;
+ end = cur_rand_news + 1;
+
+ ++cur_rand_news;
+ }
+ else
+ {
+ if (list.size() < news_count)
+ start = 0;
+ else
+ start = list.size() - news_count;
+
+ end = start + news_count;
+
+ if (end > list.size())
+ end = list.size();
+ }
+
+ for (unsigned int i = start; i < end; ++i)
+ {
+ NewsItem *n = list[i];
+
+ u->SendMessage(bi, msg.c_str(), Anope::strftime(n->GetTime(), u->Account(), true), n->GetText());
+ }
+ }
+
+ public:
+ OSNews(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , EventHook<Event::UserModeSet>(this)
+ , EventHook<Event::UserConnect>(this)
+ , newsitemtype(this)
+ , commandoslogonnews(this)
+ , commandosopernews(this)
+ , commandosrandomnews(this)
+ {
+ }
+
+ void OnReload(Configuration::Conf *conf) override
+ {
+ oper_announcer = conf->GetModule(this)->Get<Anope::string>("oper_announcer", "OperServ");
+ announcer = conf->GetModule(this)->Get<Anope::string>("announcer", "Global");
+ news_count = conf->GetModule(this)->Get<unsigned>("newscount", "3");
+ }
+
+ void OnUserModeSet(const MessageSource &setter, User *u, const Anope::string &mname) override
+ {
+ if (mname == "OPER")
+ DisplayNews(u, NEWS_OPER);
+ }
+
+ void OnUserConnect(User *user, bool &) override
+ {
+ if (user->Quitting() || !user->server->IsSynced())
+ return;
+
+ DisplayNews(user, NEWS_LOGON);
+ DisplayNews(user, NEWS_RANDOM);
+ }
+};
+
+MODULE_INIT(OSNews)
diff --git a/modules/commands/os_noop.cpp b/modules/operserv/noop.cpp
index c0beb678a..7c4a178b4 100644
--- a/modules/commands/os_noop.cpp
+++ b/modules/operserv/noop.cpp
@@ -1,12 +1,20 @@
-/* OperServ core functions
+/*
+ * Anope IRC Services
*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
*
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "module.h"
@@ -21,15 +29,15 @@ class CommandOSNOOP : public Command
this->SetSyntax(_("REVOKE \037server\037"));
}
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
{
const Anope::string &cmd = params[0];
const Anope::string &server = params[1];
Server *s = Server::Find(server, true);
if (s == NULL)
- source.Reply(_("Server %s does not exist."), server.c_str());
- else if (s == Me || s->IsJuped())
+ source.Reply(_("Server \002{0}\002 does not exist."), server);
+ else if (s == Me || s->IsJuped() || s->IsULined())
source.Reply(_("You can not NOOP Services."));
else if (cmd.equals_ci("SET"))
{
@@ -38,7 +46,7 @@ class CommandOSNOOP : public Command
s->Extend<Anope::string>("noop", source.GetNick());
Log(LOG_ADMIN, source, this) << "SET on " << s->GetName();
- source.Reply(_("All operators from \002%s\002 have been removed."), s->GetName().c_str());
+ source.Reply(_("All operators from \002{0}\002 have been removed."), s->GetName());
Anope::string reason = "NOOP command used by " + source.GetNick();
/* Kill all the IRCops of the server */
@@ -55,43 +63,42 @@ class CommandOSNOOP : public Command
s->Shrink<Anope::string>("noop");
IRCD->SendSVSNOOP(s, false);
Log(LOG_ADMIN, source, this) << "REVOKE on " << s->GetName();
- source.Reply(_("All O:lines of \002%s\002 have been reset."), s->GetName().c_str());
+ source.Reply(_("All O:lines of \002{0}\002 have been reset."), s->GetName());
}
else
this->OnSyntaxError(source, "");
}
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
{
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("\002SET\002 kills all operators from the given\n"
- "\002server\002 and prevents operators from opering\n"
- "up on the given server. \002REVOKE\002 removes this\n"
- "restriction."));
+ source.Reply(_("\002{0} SET\002 kills all operators from the given \002server\002 and prevents operators from opering up on the given server."
+ " \002{0} REVOKE\002 removes this restriction."));
return true;
}
};
class OSNOOP : public Module
+ , public EventHook<Event::UserModeSet>
{
CommandOSNOOP commandosnoop;
- PrimitiveExtensibleItem<Anope::string> noop;
+ ExtensibleItem<Anope::string> noop;
public:
- OSNOOP(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandosnoop(this), noop(this, "noop")
+ OSNOOP(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , EventHook<Event::UserModeSet>(this)
+ , commandosnoop(this)
+ , noop(this, "noop")
{
}
- void OnUserModeSet(const MessageSource &, User *u, const Anope::string &mname) anope_override
+ void OnUserModeSet(const MessageSource &, User *u, const Anope::string &mname) override
{
Anope::string *setter;
if (mname == "OPER" && (setter = noop.Get(u->server)))
{
Anope::string reason = "NOOP command used by " + *setter;
- BotInfo *OperServ = Config->GetClient("OperServ");
+ ServiceBot *OperServ = Config->GetClient("OperServ");
u->Kill(OperServ, reason);
}
}
diff --git a/modules/commands/os_oline.cpp b/modules/operserv/oline.cpp
index 5270d3f78..a0b968325 100644
--- a/modules/commands/os_oline.cpp
+++ b/modules/operserv/oline.cpp
@@ -1,12 +1,20 @@
-/* OperServ core functions
+/*
+ * Anope IRC Services
*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
*
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "module.h"
@@ -20,7 +28,7 @@ class CommandOSOLine : public Command
this->SetSyntax(_("\037nick\037 \037flags\037"));
}
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
{
const Anope::string &nick = params[0];
const Anope::string &flag = params[1];
@@ -28,19 +36,19 @@ class CommandOSOLine : public Command
/* let's check whether the user is online */
if (!(u2 = User::Find(nick, true)))
- source.Reply(NICK_X_NOT_IN_USE, nick.c_str());
+ source.Reply(_("\002{0}\002 isn't currently online."), nick);
else if (u2 && flag[0] == '+')
{
IRCD->SendSVSO(source.service, nick, flag);
u2->SetMode(source.service, "OPER");
- u2->SendMessage(source.service, _("You are now an IRC Operator."));
- source.Reply(_("Operflags \002%s\002 have been added for \002%s\002."), flag.c_str(), nick.c_str());
+ u2->SendMessage(*source.service, _("You are now an IRC Operator."));
+ source.Reply(_("Operflags \002{0}\002 have been added for \002{1}\002."), flag, nick);
Log(LOG_ADMIN, source, this) << "for " << nick;
}
else if (u2 && flag[0] == '-')
{
IRCD->SendSVSO(source.service, nick, flag);
- source.Reply(_("Operflags \002%s\002 have been removed from \002%s\002."), flag.c_str(), nick.c_str());
+ source.Reply(_("Operflags \002{0}\002 have been removed from \002{1}\002."), flag, nick);
Log(LOG_ADMIN, source, this) << "for " << nick;
}
else
@@ -49,13 +57,9 @@ class CommandOSOLine : public Command
return;
}
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
{
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Allows Services Operators to give Operflags to any user.\n"
- "Flags have to be prefixed with a \"+\" or a \"-\". To\n"
- "remove all flags simply type a \"-\" instead of any flags."));
+ source.Reply(_("Allows Services Operators to give Operflags to any user. Flags have to be prefixed with a \"+\" or a \"-\". To remove all flags simply type a \"-\" instead of any flags."));
return true;
}
};
@@ -65,8 +69,8 @@ class OSOLine : public Module
CommandOSOLine commandosoline;
public:
- OSOLine(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandosoline(this)
+ OSOLine(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandosoline(this)
{
if (!IRCD || !IRCD->CanSVSO)
diff --git a/modules/operserv/oper.cpp b/modules/operserv/oper.cpp
new file mode 100644
index 000000000..40880d8fc
--- /dev/null
+++ b/modules/operserv/oper.cpp
@@ -0,0 +1,257 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+
+class CommandOSOper : public Command
+{
+ bool HasPrivs(CommandSource &source, OperType *ot) const
+ {
+ std::list<Anope::string> commands = ot->GetCommands(), privs = ot->GetPrivs();
+
+ for (std::list<Anope::string>::iterator it = commands.begin(); it != commands.end(); ++it)
+ if (!source.HasCommand(*it))
+ return false;
+
+ for (std::list<Anope::string>::iterator it = privs.begin(); it != privs.end(); ++it)
+ if (!source.HasPriv(*it))
+ return false;
+
+ return true;
+ }
+
+ public:
+ CommandOSOper(Module *creator) : Command(creator, "operserv/oper", 1, 3)
+ {
+ this->SetDesc(_("View and change Services Operators"));
+ this->SetSyntax(_("ADD \037oper\037 \037type\037"));
+ this->SetSyntax(_("DEL \037oper\037"));
+ this->SetSyntax(_("INFO [\037type\037]"));
+ this->SetSyntax("LIST");
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &subcommand = params[0];
+
+ if (subcommand.equals_ci("ADD") && params.size() > 2)
+ {
+ const Anope::string &oper = params[1];
+ const Anope::string &otype = params[2];
+
+ if (!source.HasPriv("operserv/oper/modify"))
+ {
+ source.Reply(_("Access denied. You do not have the operator privilege \002{0}\002."), "operserv/oper/modify");
+ return;
+ }
+
+ NickServ::Nick *na = NickServ::FindNick(oper);
+ if (na == NULL)
+ {
+ source.Reply(_("\002{0}\002 isn't currently online."), oper);
+ return;
+ }
+
+ OperType *ot = OperType::Find(otype);
+ if (ot == NULL)
+ {
+ source.Reply(_("Oper type \002{0}\002 has not been configured."), otype);
+ return;
+ }
+
+ if (!HasPrivs(source, ot))
+ {
+ source.Reply(_("Access denied."));
+ return;
+ }
+
+ if (na->GetAccount()->o)
+ {
+ na->GetAccount()->o->Delete();
+ na->GetAccount()->o = nullptr;
+ }
+
+ Oper *o = Serialize::New<Oper *>();
+ o->SetName(na->GetAccount()->GetDisplay());
+ o->SetType(ot);
+ o->SetRequireOper(true);
+
+ na->GetAccount()->o = o;
+
+ if (Anope::ReadOnly)
+ source.Reply(_("Services are in read-only mode. Any changes made may not persist."));
+
+ Log(LOG_ADMIN, source, this) << "ADD " << na->GetNick() << " as type " << ot->GetName();
+ source.Reply("\002{0}\002 (\002{1}\002) added to the \002{2}\002 list.", na->GetNick(), na->GetAccount()->GetDisplay(), ot->GetName());
+ }
+ else if (subcommand.equals_ci("DEL") && params.size() > 1)
+ {
+ const Anope::string &oper = params[1];
+
+ if (!source.HasPriv("operserv/oper/modify"))
+ {
+ source.Reply(_("Access denied. You do not have the operator privilege \002{0}\002."), "operserv/oper/modify");
+ return;
+ }
+
+ NickServ::Nick *na = NickServ::FindNick(oper);
+ if (na == nullptr || na->GetAccount() == nullptr)
+ {
+ source.Reply(_("\002{0}\002 isn't registered."), oper);
+ return;
+ }
+
+ Oper *o = na->GetAccount()->o;
+
+ if (o == nullptr)
+ {
+ source.Reply(_("Nick \002{0}\002 is not a Services Operator."), oper);
+ return;
+ }
+
+ if (!HasPrivs(source, o->GetType()))
+ {
+ source.Reply(_("Access denied."));
+ return;
+ }
+
+ if (o->conf != nullptr)
+ {
+ source.Reply(_("Oper \002{0}\002 is configured in the configuration file(s) and can not be removed by this command."), na->GetNick());
+ return;
+ }
+
+ na->GetAccount()->o->Delete();
+ na->GetAccount()->o = NULL;
+
+ if (Anope::ReadOnly)
+ source.Reply(_("Services are in read-only mode. Any changes made may not persist."));
+
+ Log(LOG_ADMIN, source, this) << "DEL " << na->GetNick();
+ source.Reply(_("Oper privileges removed from \002{0}\002 (\002{1}\002)."), na->GetNick(), na->GetAccount()->GetDisplay());
+ }
+ else if (subcommand.equals_ci("LIST"))
+ {
+ source.Reply(_("Name Type"));
+ for (NickServ::Account *nc : NickServ::service->GetAccountList())
+ {
+ if (!nc->o)
+ continue;
+
+ source.Reply(Anope::printf("%-8s %s", nc->o->GetName().c_str(), nc->o->GetType()->GetName().c_str()));
+ if (nc->o->conf)
+ source.Reply(_(" This oper is configured in the configuration file."));
+ for (User *u : nc->users)
+ source.Reply(_(" \002{0}\002 is online using this oper block."), u->nick);
+ }
+ }
+ else if (subcommand.equals_ci("INFO"))
+ {
+ if (params.size() < 2)
+ {
+ source.Reply(_("Available opertypes:"));
+ for (unsigned i = 0; i < Config->MyOperTypes.size(); ++i)
+ {
+ OperType *ot = Config->MyOperTypes[i];
+ source.Reply("%s", ot->GetName().c_str());
+ }
+ return;
+ }
+
+ Anope::string fulltype = params[1];
+ if (params.size() > 2)
+ fulltype += " " + params[2];
+ OperType *ot = OperType::Find(fulltype);
+ if (ot == NULL)
+ {
+ source.Reply(_("Oper type \002{0}\002 has not been configured."), fulltype);
+ return;
+ }
+
+ if (ot->GetCommands().empty())
+ source.Reply(_("Opertype \002{0}\002 has no allowed commands."), ot->GetName());
+ else
+ {
+ source.Reply(_("Available commands for \002{0}\002:"), ot->GetName());
+ Anope::string buf;
+ std::list<Anope::string> cmds = ot->GetCommands();
+ for (std::list<Anope::string>::const_iterator it = cmds.begin(), it_end = cmds.end(); it != it_end; ++it)
+ {
+ buf += *it + " ";
+ if (buf.length() > 400)
+ {
+ source.Reply("%s", buf.c_str());
+ buf.clear();
+ }
+ }
+ if (!buf.empty())
+ {
+ source.Reply("%s", buf.c_str());
+ buf.clear();
+ }
+ }
+ if (ot->GetPrivs().empty())
+ source.Reply(_("Opertype \002{0}\002 has no allowed privileges."), ot->GetName());
+ else
+ {
+ source.Reply(_("Available privileges for \002{0}\002:"), ot->GetName());
+ Anope::string buf;
+ std::list<Anope::string> privs = ot->GetPrivs();
+ for (std::list<Anope::string>::const_iterator it = privs.begin(), it_end = privs.end(); it != it_end; ++it)
+ {
+ buf += *it + " ";
+ if (buf.length() > 400)
+ {
+ source.Reply("%s", buf.c_str());
+ buf.clear();
+ }
+ }
+ if (!buf.empty())
+ {
+ source.Reply("%s", buf.c_str());
+ buf.clear();
+ }
+ }
+ if (!ot->modes.empty())
+ source.Reply(_("Opertype \002{0}\002 receives modes \002{1}\002 once identified."), ot->GetName(), ot->modes);
+ }
+ else
+ this->OnSyntaxError(source, subcommand);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Allows you to change and view Services Operators.\n"
+ " Note that operators removed by this command but are still set in the configuration file are not permanently affected by this."));
+ return true;
+ }
+};
+
+class OSOper : public Module
+{
+ CommandOSOper commandosoper;
+
+ public:
+ OSOper(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandosoper(this)
+ {
+ }
+};
+
+MODULE_INIT(OSOper)
diff --git a/modules/operserv/reload.cpp b/modules/operserv/reload.cpp
new file mode 100644
index 000000000..ecff66ac2
--- /dev/null
+++ b/modules/operserv/reload.cpp
@@ -0,0 +1,70 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+
+class CommandOSReload : public Command
+{
+ public:
+ CommandOSReload(Module *creator) : Command(creator, "operserv/reload", 0, 0)
+ {
+ this->SetDesc(_("Reload services' configuration file"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ try
+ {
+ Log(LOG_ADMIN, source, this);
+
+ Configuration::Conf *new_config = new Configuration::Conf();
+ Configuration::Conf *old = Config;
+ Config = new_config;
+ Config->Post(old);
+ delete old;
+
+ source.Reply(_("Services' configuration has been reloaded."));
+ }
+ catch (const ConfigException &ex)
+ {
+ Log(this->GetOwner()) << "Error reloading configuration file: " << ex.GetReason();
+ source.Reply(_("Error reloading configuration file: {0}"), ex.GetReason());
+ }
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Causes Services to reload the configuration file."));
+ return true;
+ }
+};
+
+class OSReload : public Module
+{
+ CommandOSReload commandosreload;
+
+ public:
+ OSReload(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandosreload(this)
+ {
+
+ }
+};
+
+MODULE_INIT(OSReload)
diff --git a/modules/operserv/session.cpp b/modules/operserv/session.cpp
new file mode 100644
index 000000000..158d3c342
--- /dev/null
+++ b/modules/operserv/session.cpp
@@ -0,0 +1,785 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/operserv/session.h"
+
+namespace
+{
+ /* The default session limit */
+ unsigned session_limit;
+ /* How many times to kill before adding an AKILL */
+ unsigned max_session_kill;
+ /* How long session akills should last */
+ time_t session_autokill_expiry;
+ /* Reason to use for session kills */
+ Anope::string sle_reason;
+ /* Optional second reason */
+ Anope::string sle_detailsloc;
+
+ /* Max limit that can be used for exceptions */
+ unsigned max_exception_limit;
+ /* How long before exceptions expire by default */
+ time_t exception_expiry;
+
+ /* Number of bits to use when comparing session IPs */
+ unsigned ipv4_cidr;
+ unsigned ipv6_cidr;
+}
+
+class ExceptionImpl : public Exception
+{
+ friend class ExceptionType;
+
+ Anope::string mask, who, reason;
+ unsigned int limit = 0;
+ time_t time = 0, expires = 0;
+
+ public:
+ ExceptionImpl(Serialize::TypeBase *type) : Exception(type) { }
+ ExceptionImpl(Serialize::TypeBase *type, Serialize::ID id) : Exception(type, id) { }
+
+ Anope::string GetMask() override;
+ void SetMask(const Anope::string &) override;
+
+ unsigned int GetLimit() override;
+ void SetLimit(unsigned int) override;
+
+ Anope::string GetWho() override;
+ void SetWho(const Anope::string &) override;
+
+ Anope::string GetReason() override;
+ void SetReason(const Anope::string &) override;
+
+ time_t GetTime() override;
+ void SetTime(const time_t &) override;
+
+ time_t GetExpires() override;
+ void SetExpires(const time_t &) override;
+};
+
+class ExceptionType : public Serialize::Type<ExceptionImpl>
+{
+ public:
+ Serialize::Field<ExceptionImpl, Anope::string> mask, who, reason;
+ Serialize::Field<ExceptionImpl, unsigned int> limit;
+ Serialize::Field<ExceptionImpl, time_t> time, expires;
+
+ ExceptionType(Module *me) : Serialize::Type<ExceptionImpl>(me)
+ , mask(this, "mask", &ExceptionImpl::mask)
+ , who(this, "who", &ExceptionImpl::who)
+ , reason(this, "reason", &ExceptionImpl::reason)
+ , limit(this, "limit", &ExceptionImpl::limit)
+ , time(this, "time", &ExceptionImpl::time)
+ , expires(this, "expires", &ExceptionImpl::expires)
+ {
+ }
+};
+
+Anope::string ExceptionImpl::GetMask()
+{
+ return Get(&ExceptionType::mask);
+}
+
+void ExceptionImpl::SetMask(const Anope::string &m)
+{
+ Set(&ExceptionType::mask, m);
+}
+
+unsigned int ExceptionImpl::GetLimit()
+{
+ return Get(&ExceptionType::limit);
+}
+
+void ExceptionImpl::SetLimit(unsigned int l)
+{
+ Set(&ExceptionType::limit, l);
+}
+
+Anope::string ExceptionImpl::GetWho()
+{
+ return Get(&ExceptionType::who);
+}
+
+void ExceptionImpl::SetWho(const Anope::string &w)
+{
+ Set(&ExceptionType::who, w);
+}
+
+Anope::string ExceptionImpl::GetReason()
+{
+ return Get(&ExceptionType::reason);
+}
+
+void ExceptionImpl::SetReason(const Anope::string &r)
+{
+ Set(&ExceptionType::reason, r);
+}
+
+time_t ExceptionImpl::GetTime()
+{
+ return Get(&ExceptionType::time);
+}
+
+void ExceptionImpl::SetTime(const time_t &t)
+{
+ Set(&ExceptionType::time, t);
+}
+
+time_t ExceptionImpl::GetExpires()
+{
+ return Get(&ExceptionType::expires);
+}
+
+void ExceptionImpl::SetExpires(const time_t &e)
+{
+ Set(&ExceptionType::expires, e);
+}
+
+class MySessionService : public SessionService
+{
+ SessionMap Sessions;
+
+ public:
+ MySessionService(Module *m) : SessionService(m) { }
+
+ Exception *FindException(User *u) override
+ {
+ for (Exception *e : Serialize::GetObjects<Exception *>())
+ {
+ if (Anope::Match(u->host, e->GetMask()) || Anope::Match(u->ip.addr(), e->GetMask()))
+ return e;
+
+ if (cidr(e->GetMask()).match(u->ip))
+ return e;
+ }
+ return nullptr;
+ }
+
+ Exception *FindException(const Anope::string &host) override
+ {
+ for (Exception *e : Serialize::GetObjects<Exception *>())
+ {
+ if (Anope::Match(host, e->GetMask()))
+ return e;
+
+ if (cidr(e->GetMask()).match(sockaddrs(host)))
+ return e;
+ }
+
+ return nullptr;
+ }
+
+ void DelSession(Session *s)
+ {
+ this->Sessions.erase(s->addr);
+ }
+
+ Session *FindSession(const Anope::string &ip) override
+ {
+ cidr c(ip, ip.find(':') != Anope::string::npos ? ipv6_cidr : ipv4_cidr);
+ if (!c.valid())
+ return NULL;
+ SessionMap::iterator it = this->Sessions.find(c);
+ if (it != this->Sessions.end())
+ return it->second;
+ return NULL;
+ }
+
+ SessionMap::iterator FindSessionIterator(const sockaddrs &ip)
+ {
+ cidr c(ip, ip.ipv6() ? ipv6_cidr : ipv4_cidr);
+ if (!c.valid())
+ return this->Sessions.end();
+ return this->Sessions.find(c);
+ }
+
+ Session* &FindOrCreateSession(const cidr &ip)
+ {
+ return this->Sessions[ip];
+ }
+
+ SessionMap &GetSessions() override
+ {
+ return this->Sessions;
+ }
+};
+
+class CommandOSSession : public Command
+{
+ ServiceReference<SessionService> session_service;
+
+ private:
+ void DoList(CommandSource &source, const std::vector<Anope::string> &params)
+ {
+ Anope::string param = params[1];
+
+ unsigned mincount = 0;
+ try
+ {
+ mincount = convertTo<unsigned>(param);
+ }
+ catch (const ConvertException &) { }
+
+ if (mincount <= 1)
+ {
+ source.Reply(_("Invalid threshold value. It must be a valid integer greater than 1."));
+ return;
+ }
+
+ ListFormatter list(source.GetAccount());
+ list.AddColumn(_("Session")).AddColumn(_("Host"));
+
+ for (SessionService::SessionMap::iterator it = session_service->GetSessions().begin(), it_end = session_service->GetSessions().end(); it != it_end; ++it)
+ {
+ Session *session = it->second;
+
+ if (session->count >= mincount)
+ {
+ ListFormatter::ListEntry entry;
+ entry["Session"] = stringify(session->count);
+ entry["Host"] = session->addr.mask();
+ list.AddEntry(entry);
+ }
+ }
+
+ source.Reply(_("Hosts with at least \002{0}\002 sessions:"), mincount);
+
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+ }
+
+ void DoView(CommandSource &source, const std::vector<Anope::string> &params)
+ {
+ Anope::string param = params[1];
+ Session *session = session_service->FindSession(param);
+
+ Exception *e = session_service->FindException(param);
+ Anope::string entry = "no entry";
+ unsigned limit = session_limit;
+ if (e)
+ {
+ if (!e->GetLimit())
+ limit = 0;
+ else if (e->GetLimit() > limit)
+ limit = e->GetLimit();
+ entry = e->GetMask();
+ }
+
+ if (!session)
+ source.Reply(_("\002{0}\002 not found on the session list, but has a limit of \002{1}\002 because it matches entry: \002{2}\002."), param, limit, entry);
+ else
+ source.Reply(_("The host \002{0}\002 currently has \002{1}\002 sessions with a limit of \002{2}\002 because it matches entry: \002{3}\002."), session->addr.mask(), session->count, limit, entry);
+ }
+
+ public:
+ CommandOSSession(Module *creator) : Command(creator, "operserv/session", 2, 2)
+ {
+ this->SetDesc(_("View the list of host sessions"));
+ this->SetSyntax(_("LIST \037threshold\037"));
+ this->SetSyntax(_("VIEW \037host\037"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &cmd = params[0];
+
+ Log(LOG_ADMIN, source, this) << cmd << " " << params[1];
+
+ if (!session_limit)
+ source.Reply(_("Session limiting is disabled."));
+ else if (cmd.equals_ci("LIST"))
+ this->DoList(source, params);
+ else if (cmd.equals_ci("VIEW"))
+ this->DoView(source, params);
+ else
+ this->OnSyntaxError(source, "");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Allows Services Operators to view the session list.\n"
+ "\n"
+ "\002{0} LIST\002 lists hosts with at least \037threshold\037 sessions. The threshold must be a number greater than 1.\n"
+ "\n"
+ "\002{0} VIEW\002 displays detailed information about a specific host - including the current session count and session limit.\n"
+ "\n"
+ "See the \002EXCEPTION\002 help for more information about session limiting and how to set session limits specific to certain hosts and groups thereof."), // XXX
+ source.command);
+ return true;
+ }
+};
+
+class CommandOSException : public Command
+{
+ static void DoDel(CommandSource &source, Exception *e)
+ {
+ EventManager::Get()->Dispatch(&Event::Exception::OnExceptionDel, source, e);
+ e->Delete();
+ }
+
+ void DoAdd(CommandSource &source, const std::vector<Anope::string> &params)
+ {
+ Anope::string mask, expiry, limitstr;
+ unsigned last_param = 3;
+
+ mask = params.size() > 1 ? params[1] : "";
+ if (!mask.empty() && mask[0] == '+')
+ {
+ expiry = mask;
+ mask = params.size() > 2 ? params[2] : "";
+ last_param = 4;
+ }
+
+ limitstr = params.size() > last_param - 1 ? params[last_param - 1] : "";
+
+ if (params.size() <= last_param)
+ {
+ this->OnSyntaxError(source, "ADD");
+ return;
+ }
+
+ Anope::string reason = params[last_param];
+ if (last_param == 3 && params.size() > 4)
+ reason += " " + params[4];
+ if (reason.empty())
+ {
+ this->OnSyntaxError(source, "ADD");
+ return;
+ }
+
+ time_t expires = !expiry.empty() ? Anope::DoTime(expiry) : exception_expiry;
+ if (expires < 0)
+ {
+ source.Reply(_("Invalid expiry time \002{0}\002."), expiry);
+ return;
+ }
+ else if (expires > 0)
+ expires += Anope::CurTime;
+
+ unsigned limit = -1;
+ try
+ {
+ limit = convertTo<unsigned>(limitstr);
+ }
+ catch (const ConvertException &) { }
+
+ if (limit > max_exception_limit)
+ {
+ source.Reply(_("Invalid session limit. It must be a valid integer greater than or equal to zero and less than \002{0}\002."), max_exception_limit);
+ return;
+ }
+ else
+ {
+ if (mask.find('!') != Anope::string::npos || mask.find('@') != Anope::string::npos)
+ {
+ source.Reply(_("Invalid hostmask. Only real hostmasks are valid, as exceptions are not matched against nicks or usernames."));
+ return;
+ }
+
+ for (Exception *e : Serialize::GetObjects<Exception *>())
+ if (e->GetMask().equals_ci(mask))
+ {
+ if (e->GetLimit() != limit)
+ {
+ e->SetLimit(limit);
+ source.Reply(_("Exception for \002{0}\002 has been updated to \002{1}\002."), mask, e->GetLimit());
+ }
+ else
+ source.Reply(_("\002{0}\002 already exists on the session-limit exception list."), mask);
+ return;
+ }
+
+ Exception *e = Serialize::New<Exception *>();
+ e->SetMask(mask);
+ e->SetLimit(limit);
+ e->SetReason(reason);
+ e->SetTime(Anope::CurTime);
+ e->SetWho(source.GetNick());
+ e->SetExpires(expires);
+
+ EventReturn MOD_RESULT;
+ MOD_RESULT = EventManager::Get()->Dispatch(&Event::Exception::OnExceptionAdd, e);
+ if (MOD_RESULT == EVENT_STOP)
+ return;
+
+ Log(LOG_ADMIN, source, this) << "to set the session limit for " << mask << " to " << limit;
+ source.Reply(_("Session limit for \002{0}\002 set to \002{1}\002."), mask, limit);
+ if (Anope::ReadOnly)
+ source.Reply(_("Services are in read-only mode. Any changes made may not persist."));
+ }
+ }
+
+ void DoDel(CommandSource &source, const std::vector<Anope::string> &params)
+ {
+ const Anope::string &mask = params.size() > 1 ? params[1] : "";
+
+ if (mask.empty())
+ {
+ this->OnSyntaxError(source, "DEL");
+ return;
+ }
+
+ if (isdigit(mask[0]) && mask.find_first_not_of("1234567890,-") == Anope::string::npos)
+ {
+ unsigned int deleted = 0;
+
+ NumberList(mask, true,
+ [&](unsigned int number)
+ {
+ std::vector<Exception *> exceptions = Serialize::GetObjects<Exception *>();
+ if (!number || number > exceptions.size())
+ return;
+
+ Exception *e = exceptions[number - 1];
+
+ Log(LOG_ADMIN, source, this) << "to remove the session limit exception for " << e->GetMask();
+
+ ++deleted;
+ DoDel(source, e);
+ },
+ [&]()
+ {
+ if (!deleted)
+ source.Reply(_("No matching entries on session-limit exception list."));
+ else if (deleted == 1)
+ source.Reply(_("Deleted \0021\002 entry from session-limit exception list."));
+ else
+ source.Reply(_("Deleted \002{0}\002 entries from session-limit exception list."), deleted);
+ });
+ }
+ else
+ {
+ bool found = false;
+ for (Exception *e : Serialize::GetObjects<Exception *>())
+ if (mask.equals_ci(e->GetMask()))
+ {
+ Log(LOG_ADMIN, source, this) << "to remove the session limit exception for " << mask;
+ DoDel(source, e);
+ source.Reply(_("\002{0}\002 deleted from session-limit exception list."), mask);
+ found = true;
+ break;
+ }
+ if (!found)
+ source.Reply(_("\002{0}\002 not found on session-limit exception list."), mask);
+ }
+
+ if (Anope::ReadOnly)
+ source.Reply(_("Services are in read-only mode. Any changes made may not persist."));
+ }
+
+ void ProcessList(CommandSource &source, const std::vector<Anope::string> &params, ListFormatter &list)
+ {
+ const Anope::string &mask = params.size() > 1 ? params[1] : "";
+ std::vector<Exception *> exceptions = Serialize::GetObjects<Exception *>();
+
+ if (exceptions.empty())
+ {
+ source.Reply(_("The session exception list is empty."));
+ return;
+ }
+
+ if (!mask.empty() && mask.find_first_not_of("1234567890,-") == Anope::string::npos)
+ {
+ NumberList(mask, false,
+ [&](unsigned int number)
+ {
+ if (!number || number > exceptions.size())
+ return;
+
+ Exception *e = exceptions[number - 1];
+
+ ListFormatter::ListEntry entry;
+ entry["Number"] = stringify(number);
+ entry["Mask"] = e->GetMask();
+ entry["By"] = e->GetWho();
+ entry["Created"] = Anope::strftime(e->GetTime(), NULL, true);
+ entry["Expires"] = Anope::Expires(e->GetExpires(), source.GetAccount());
+ entry["Limit"] = stringify(e->GetLimit());
+ entry["Reason"] = e->GetReason();
+ list.AddEntry(entry);
+ },
+ []{});
+ }
+ else
+ {
+ unsigned int i = 0;
+ for (Exception *e : exceptions)
+ {
+ if (mask.empty() || Anope::Match(e->GetMask(), mask))
+ {
+ ListFormatter::ListEntry entry;
+ entry["Number"] = stringify(++i);
+ entry["Mask"] = e->GetMask();
+ entry["By"] = e->GetWho();
+ entry["Created"] = Anope::strftime(e->GetTime(), NULL, true);
+ entry["Expires"] = Anope::Expires(e->GetExpires(), source.GetAccount());
+ entry["Limit"] = stringify(e->GetLimit());
+ entry["Reason"] = e->GetReason();
+ list.AddEntry(entry);
+ }
+ }
+ }
+
+ if (list.IsEmpty())
+ {
+ source.Reply(_("No matching entries on session-limit exception list."));
+ return;
+ }
+
+ source.Reply(_("Session-limit exception list:"));
+
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+ for (const Anope::string &r : replies)
+ source.Reply(r);
+ }
+
+ void DoList(CommandSource &source, const std::vector<Anope::string> &params)
+ {
+ ListFormatter list(source.GetAccount());
+ list.AddColumn(_("Number")).AddColumn(_("Limit")).AddColumn(_("Mask"));
+
+ this->ProcessList(source, params, list);
+ }
+
+ void DoView(CommandSource &source, const std::vector<Anope::string> &params)
+ {
+ ListFormatter list(source.GetAccount());
+ list.AddColumn(_("Number")).AddColumn(_("Mask")).AddColumn(_("By")).AddColumn(_("Created")).AddColumn(_("Expires")).AddColumn(_("Limit")).AddColumn(_("Reason"));
+
+ this->ProcessList(source, params, list);
+ }
+
+ public:
+ CommandOSException(Module *creator) : Command(creator, "operserv/exception", 1, 5)
+ {
+ this->SetDesc(_("Modify the session-limit exception list"));
+ this->SetSyntax(_("ADD [\037+expiry\037] \037mask\037 \037limit\037 \037reason\037"));
+ this->SetSyntax(_("DEL {\037mask\037 | \037entry-num\037 | \037list\037}"));
+ this->SetSyntax(_("LIST [\037mask\037 | \037list\037]"));
+ this->SetSyntax(_("VIEW [\037mask\037 | \037list\037]"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &cmd = params[0];
+
+ if (!session_limit)
+ source.Reply(_("Session limiting is disabled."));
+ else if (cmd.equals_ci("ADD"))
+ return this->DoAdd(source, params);
+ else if (cmd.equals_ci("DEL"))
+ return this->DoDel(source, params);
+ else if (cmd.equals_ci("LIST"))
+ return this->DoList(source, params);
+ else if (cmd.equals_ci("VIEW"))
+ return this->DoView(source, params);
+ else
+ this->OnSyntaxError(source, "");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ if (subcommand.equals_ci("ADD"))
+ source.Reply(_("\002{0} ADD\002 adds the given hostmask to the exception list."
+ " Note that \002nick!user@host\002 and \002user@host\002 masks are invalid!\n"
+ " Only real host masks, such as \002box.host.dom\002 and \002*.host.dom\002, are allowed because sessions limiting does not take nickname or user names into account."
+ " \037limit\037 must be a number greater than or equal to zero."
+ " This determines how many sessions this host may carry at a time."
+ " A value of zero means the host has an unlimited session limit."
+ " If more than one entry matches a client, the first matching enty will be used."),
+ source.command);
+ else
+ source.Reply(_("Allows you to manipulate the list of hosts that have specific session limits - allowing certain machines, such as shell servers, to carry more than the default number of clients at a time."
+ " Once a host reaches its session limit, all clients attempting to connect from that host will be killed."
+ " Before the user is killed, they are notified, of a source of help regarding session limiting. The content of this notice is a config setting.\n"
+ "\n"
+ "\002{0} ADD\002 adds the given host mask to the exception list.\n"
+ "\002{msg}{service} {help} {command} ADD\002 for more information.\n"
+ "\n"
+ "\002{0} DEL\002 removes the given mask from the exception list.\n"
+ "\n"
+ "\002{0} LIST\002 and \002{0} VIEW\002 show all current sessions if the optional mask is given, the list is limited to those sessions matching the mask."
+ " The difference is that \002{0} VIEW\002 is more verbose, displaying the name of the person who added the exception, its session limit, reason, host mask and the expiry date and time.\n"));
+ return true;
+ }
+};
+
+class OSSession : public Module
+ , public EventHook<Event::UserConnect>
+ , public EventHook<Event::UserQuit>
+ , public EventHook<Event::ExpireTick>
+{
+ MySessionService ss;
+ CommandOSSession commandossession;
+ CommandOSException commandosexception;
+ ServiceReference<XLineManager> akills;
+ ExceptionType etype;
+
+ public:
+ OSSession(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , EventHook<Event::UserConnect>(this, EventHook<Event::UserConnect>::Priority::FIRST)
+ , EventHook<Event::UserQuit>(this, EventHook<Event::UserQuit>::Priority::FIRST)
+ , EventHook<Event::ExpireTick>(this, EventHook<Event::ExpireTick>::Priority::FIRST)
+ , ss(this)
+ , commandossession(this)
+ , commandosexception(this)
+ , akills("xlinemanager/sgline")
+ , etype(this)
+ {
+ this->SetPermanent(true);
+ }
+
+ void OnReload(Configuration::Conf *conf) override
+ {
+ Configuration::Block *block = Config->GetModule(this);
+
+ session_limit = block->Get<int>("defaultsessionlimit");
+ max_session_kill = block->Get<int>("maxsessionkill");
+ session_autokill_expiry = block->Get<time_t>("sessionautokillexpiry");
+ sle_reason = block->Get<Anope::string>("sessionlimitexceeded");
+ sle_detailsloc = block->Get<Anope::string>("sessionlimitdetailsloc");
+
+ max_exception_limit = block->Get<int>("maxsessionlimit");
+ exception_expiry = block->Get<time_t>("exceptionexpiry");
+
+ ipv4_cidr = block->Get<unsigned>("session_ipv4_cidr", "32");
+ ipv6_cidr = block->Get<unsigned>("session_ipv6_cidr", "128");
+
+ if (ipv4_cidr > 32 || ipv6_cidr > 128)
+ throw ConfigException(Module::name + ": session CIDR value out of range");
+ }
+
+ void OnUserConnect(User *u, bool &exempt) override
+ {
+ if (u->Quitting() || !session_limit || exempt || !u->server || u->server->IsULined())
+ return;
+
+ cidr u_ip(u->ip, u->ip.ipv6() ? ipv6_cidr : ipv4_cidr);
+ if (!u_ip.valid())
+ return;
+
+ Session* &session = this->ss.FindOrCreateSession(u_ip);
+
+ if (session)
+ {
+ bool kill = false;
+ if (session->count >= session_limit)
+ {
+ kill = true;
+ Exception *e = this->ss.FindException(u);
+ if (e)
+ {
+ kill = false;
+ if (e->GetLimit() && session->count >= e->GetLimit())
+ kill = true;
+ }
+ }
+
+ ++session->count;
+
+ if (kill && !exempt)
+ {
+ ServiceBot *OperServ = Config->GetClient("OperServ");
+ if (OperServ)
+ {
+ if (!sle_reason.empty())
+ {
+ Anope::string message = sle_reason.replace_all_cs("%IP%", u->ip.addr());
+ u->SendMessage(OperServ, message);
+ }
+ if (!sle_detailsloc.empty())
+ u->SendMessage(OperServ, sle_detailsloc);
+ }
+
+ ++session->hits;
+
+ const Anope::string &akillmask = "*@" + session->addr.mask();
+ if (max_session_kill && session->hits >= max_session_kill && akills && !akills->HasEntry(akillmask))
+ {
+ XLine *x = Serialize::New<XLine *>();
+ x->SetMask(akillmask);
+ x->SetBy(OperServ ? OperServ->nick : "");
+ x->SetExpires(Anope::CurTime + session_autokill_expiry);
+ x->SetReason("Session limit exceeded");
+ x->SetID(XLineManager::GenerateUID());
+
+ akills->AddXLine(x);
+ akills->Send(NULL, x);
+
+ Log(OperServ, "akill/session") << "Added a temporary AKILL for \002" << akillmask << "\002 due to excessive connections";
+ }
+ else
+ {
+ u->Kill(OperServ, "Session limit exceeded");
+ }
+ }
+ }
+ else
+ {
+ session = new Session(u->ip, u->ip.ipv6() ? ipv6_cidr : ipv4_cidr);
+ }
+ }
+
+ void OnUserQuit(User *u, const Anope::string &msg) override
+ {
+ if (!session_limit || !u->server || u->server->IsULined())
+ return;
+
+ SessionService::SessionMap &sessions = this->ss.GetSessions();
+ SessionService::SessionMap::iterator sit = this->ss.FindSessionIterator(u->ip);
+
+ if (sit == sessions.end())
+ return;
+
+ Session *session = sit->second;
+
+ if (session->count > 1)
+ {
+ --session->count;
+ return;
+ }
+
+ delete session;
+ sessions.erase(sit);
+ }
+
+ void OnExpireTick() override
+ {
+ if (Anope::NoExpire)
+ return;
+
+ for (Exception *e : Serialize::GetObjects<Exception *>())
+ {
+ if (!e->GetExpires() || e->GetExpires() > Anope::CurTime)
+ continue;
+
+ ServiceBot *OperServ = Config->GetClient("OperServ");
+ Log(OperServ, "expire/exception") << "Session exception for " << e->GetMask() << " has expired.";
+ e->Delete();
+ }
+ }
+};
+
+MODULE_INIT(OSSession)
diff --git a/modules/commands/os_set.cpp b/modules/operserv/set.cpp
index 5aca2f370..f12f43722 100644
--- a/modules/commands/os_set.cpp
+++ b/modules/operserv/set.cpp
@@ -1,12 +1,20 @@
-/* OperServ core functions
+/*
+ * Anope IRC Services
*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
*
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "module.h"
@@ -54,8 +62,6 @@ class CommandOSSet : public Command
}
else
source.Reply(_("Setting for READONLY must be \002ON\002 or \002OFF\002."));
-
- return;
}
void DoSetSuperAdmin(CommandSource &source, const std::vector<Anope::string> &params)
@@ -76,7 +82,7 @@ class CommandOSSet : public Command
*
* Rob
**/
- bool super_admin = Config->GetModule(this->owner)->Get<bool>("superadmin");
+ bool super_admin = Config->GetModule(this->GetOwner())->Get<bool>("superadmin");
if (!super_admin)
source.Reply(_("Super admin can not be set because it is not enabled in the configuration."));
else if (setting.equals_ci("ON"))
@@ -93,8 +99,6 @@ class CommandOSSet : public Command
}
else
source.Reply(_("Setting for super admin must be \002ON\002 or \002OFF\002."));
-
- return;
}
void DoSetDebug(CommandSource &source, const std::vector<Anope::string> &params)
@@ -129,7 +133,7 @@ class CommandOSSet : public Command
return;
}
catch (const ConvertException &) { }
-
+
source.Reply(_("Setting for DEBUG must be \002ON\002, \002OFF\002, or a positive number."));
}
@@ -160,8 +164,6 @@ class CommandOSSet : public Command
}
else
source.Reply(_("Setting for NOEXPIRE must be \002ON\002 or \002OFF\002."));
-
- return;
}
public:
CommandOSSet(Module *creator) : Command(creator, "operserv/set", 1, 2)
@@ -170,7 +172,7 @@ class CommandOSSet : public Command
this->SetSyntax(_("\037option\037 \037setting\037"));
}
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
{
const Anope::string &option = params[0];
@@ -186,18 +188,13 @@ class CommandOSSet : public Command
return this->DoSetSuperAdmin(source, params);
else
this->OnSyntaxError(source, "");
-
- return;
}
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
{
if (subcommand.empty())
{
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Sets various global Services options. Option names\n"
- "currently defined are:\n"
+ source.Reply(_("Sets various global Services options. Option names currently defined are:\n"
" READONLY Set read-only or read-write mode\n"
" DEBUG Activate or deactivate debug mode\n"
" NOEXPIRE Activate or deactivate no expire mode\n"
@@ -205,50 +202,37 @@ class CommandOSSet : public Command
" LIST List the options"));
}
else if (subcommand.equals_ci("LIST"))
- source.Reply(_("Syntax: \002LIST\002\n"
- " \n"
- "Display the various %s settings."), source.service->nick.c_str());
+ //source.Reply(_("Syntax: \002LIST\002\n"
+ // " \n"
+ source.Reply(("Display the various {0} settings."), source.service->nick);
else if (subcommand.equals_ci("READONLY"))
- source.Reply(_("Syntax: \002READONLY {ON | OFF}\002\n"
- " \n"
- "Sets read-only mode on or off. In read-only mode, normal\n"
- "users will not be allowed to modify any Services data,\n"
- "including channel and nickname access lists, etc. IRCops\n"
- "with sufficient Services privileges will be able to modify\n"
- "Services' AKILL, SQLINE, SNLINE and ignore lists, drop,\n"
- "suspend or forbid nicknames and channels, and manage news,\n"
- "oper info and DNS, but any such changes will not be saved\n"
- "unless read-only mode is deactivated before Services are\n"
- "terminated or restarted.\n"
- " \n"
- "This option is equivalent to the command-line option\n"
- "\002--readonly\002."));
+ //source.Reply(_("Syntax: \002READONLY {ON | OFF}\002\n"
+ // " \n"
+ source.Reply(_("Sets read-only mode on or off. In read-only mode, norma users will not be allowed to modify any Services data, including channel and nickname access lists, etc."
+ "Services Operators will still be able to do most tasks, but should understand any changes they do may not be permanent.\n"
+ "\n"
+ "This option is equivalent to the command-line option \002--readonly\002."));
else if (subcommand.equals_ci("DEBUG"))
- source.Reply(_("Syntax: \002DEBUG {ON | OFF}\002\n"
- " \n"
- "Sets debug mode on or off.\n"
- " \n"
- "This option is equivalent to the command-line option\n"
- "\002--debug\002."));
+ //source.Reply(_("Syntax: \002DEBUG {ON | OFF}\002\n"
+ // " \n"
+ source.Reply(_("Sets debug mode on or off.\n"
+ "\n"
+ "This option is equivalent to the command-line option \002--debug\002."));
else if (subcommand.equals_ci("NOEXPIRE"))
- source.Reply(_("Syntax: \002NOEXPIRE {ON | OFF}\002\n"
- " \n"
- "Sets no expire mode on or off. In no expire mode, nicks,\n"
- "channels, akills and exceptions won't expire until the\n"
- "option is unset.\n"
- " \n"
- "This option is equivalent to the command-line option\n"
- "\002--noexpire\002."));
+ //source.Reply(_("Syntax: \002NOEXPIRE {ON | OFF}\002\n"
+ // " \n"
+ source.Reply(_("Sets no expire mode on or off. In no expire mode, nicks, channels, akills and exceptions won't expire until the option is unset.\n"
+ "\n"
+ "This option is equivalent to the command-line option \002--noexpire\002."));
else if (subcommand.equals_ci("SUPERADMIN"))
- source.Reply(_("Syntax: \002SUPERADMIN {ON | OFF}\002\n"
- " \n"
- "Setting this will grant you extra privileges such as the\n"
- "ability to be \"founder\" on all channel's etc...\n"
- " \n"
- "This option is \002not\002 persistent, and should only be used when\n"
- "needed, and set back to OFF when no longer needed."));
+ //source.Reply(_("Syntax: \002SUPERADMIN {ON | OFF}\002\n"
+ // " \n"
+ source.Reply(_("Setting this will grant you extra privileges, such as the ability to be \"founder\" on all channels."
+ "\n"
+ "This option is \002not\002 persistent, and should only be used when needed, and set back to OFF when no longer needed."));
else
return false;
+
return true;
}
};
@@ -258,8 +242,8 @@ class OSSet : public Module
CommandOSSet commandosset;
public:
- OSSet(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
- commandosset(this)
+ OSSet(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandosset(this)
{
}
};
diff --git a/modules/commands/os_shutdown.cpp b/modules/operserv/shutdown.cpp
index 1142edbc0..c0adc182e 100644
--- a/modules/commands/os_shutdown.cpp
+++ b/modules/operserv/shutdown.cpp
@@ -1,12 +1,20 @@
-/* OperServ core functions
+/*
+ * Anope IRC Services
*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
*
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "module.h"
@@ -16,10 +24,10 @@ class CommandOSQuit : public Command
public:
CommandOSQuit(Module *creator) : Command(creator, "operserv/quit", 0, 0)
{
- this->SetDesc(_("Terminate Services WITHOUT saving"));
+ this->SetDesc(_("Terminate Services without saving"));
}
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
{
Log(LOG_ADMIN, source, this);
Anope::QuitReason = source.command + " command received from " + source.GetNick();
@@ -27,14 +35,11 @@ class CommandOSQuit : public Command
return;
}
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
{
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Causes Services to do an immediate shutdown; databases are\n"
- "\002not\002 saved. This command should not be used unless\n"
- "damage to the in-memory copies of the databases is feared\n"
- "and they should not be saved."));
+ source.Reply(_("Causes Services to do an immediate shutdown; databases may \002not\002 saved."
+ " If you are using a real time database such as SQL or Redis, this command is not useful."
+ " This command should not be used unless damage to the in-memory copies of the databases is feared and they should not be saved."));
return true;
}
};
@@ -47,7 +52,7 @@ class CommandOSRestart : public Command
this->SetDesc(_("Save databases and restart Services"));
}
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
{
Log(LOG_ADMIN, source, this);
Anope::QuitReason = source.command + " command received from " + source.GetNick();
@@ -56,11 +61,9 @@ class CommandOSRestart : public Command
return;
}
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
{
- this->SendSyntax(source);
- source.Reply(_("Causes Services to save all databases and then restart\n"
- "(i.e. exit and immediately re-run the executable)."));
+ source.Reply(_("Causes Services to restart."));
return true;
}
};
@@ -73,7 +76,7 @@ class CommandOSShutdown : public Command
this->SetDesc(_("Terminate services with save"));
}
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
{
Log(LOG_ADMIN, source, this);
Anope::QuitReason = source.command + " command received from " + source.GetNick();
@@ -82,11 +85,9 @@ class CommandOSShutdown : public Command
return;
}
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
{
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Causes Services to save all databases and then shut down."));
+ source.Reply(_("Causes Services to shut down"));
return true;
}
};
diff --git a/modules/operserv/stats.cpp b/modules/operserv/stats.cpp
new file mode 100644
index 000000000..8476e616e
--- /dev/null
+++ b/modules/operserv/stats.cpp
@@ -0,0 +1,327 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/operserv/session.h"
+#include "modules/operserv/stats.h"
+
+class StatsImpl : public Stats
+{
+ friend class StatsType;
+
+ unsigned int maxusercnt = 0;
+ time_t maxusertime = 0;
+
+ public:
+
+ using Stats::Stats;
+
+ unsigned int GetMaxUserCount() override;
+ void SetMaxUserCount(unsigned int i) override;
+
+ time_t GetMaxUserTime() override;
+ void SetMaxUserTime(time_t t) override;
+};
+
+class StatsType : public Serialize::Type<StatsImpl>
+{
+ public:
+ Serialize::Field<StatsImpl, unsigned int> maxusercount;
+ Serialize::Field<StatsImpl, time_t> maxusertime;
+
+ StatsType(Module *me) : Serialize::Type<StatsImpl>(me)
+ , maxusercount(this, "maxusercount", &StatsImpl::maxusercnt)
+ , maxusertime(this, "maxusertime", &StatsImpl::maxusertime)
+ {
+ }
+};
+
+unsigned int StatsImpl::GetMaxUserCount()
+{
+ return Get(&StatsType::maxusercount);
+}
+
+void StatsImpl::SetMaxUserCount(unsigned int i)
+{
+ Set(&StatsType::maxusercount, i);
+}
+
+time_t StatsImpl::GetMaxUserTime()
+{
+ return Get(&StatsType::maxusertime);
+}
+
+void StatsImpl::SetMaxUserTime(time_t t)
+{
+ Set(&StatsType::maxusertime, t);
+}
+
+/**
+ * Count servers connected to server s
+ * @param s The server to start counting from
+ * @return Amount of servers connected to server s
+ **/
+static int stats_count_servers(Server *s)
+{
+ if (!s)
+ return 0;
+
+ int count = 1;
+
+ if (!s->GetLinks().empty())
+ for (unsigned i = 0, j = s->GetLinks().size(); i < j; ++i)
+ count += stats_count_servers(s->GetLinks()[i]);
+
+ return count;
+}
+
+class CommandOSStats : public Command
+{
+ ServiceReference<XLineManager> akills, snlines, sqlines;
+ ServiceReference<SessionService> session_service;
+
+ private:
+ void DoStatsAkill(CommandSource &source)
+ {
+ int timeout;
+ if (akills)
+ {
+ /* AKILLs */
+ source.Reply(_("Current number of AKILLs: \002{0}\002"), akills->GetCount());
+ timeout = Config->GetModule("operserv")->Get<time_t>("autokillexpiry", "30d") + 59;
+ if (timeout >= 172800)
+ source.Reply(_("Default AKILL expiry time: \002{0} days\002"), timeout / 86400);
+ else if (timeout >= 86400)
+ source.Reply(_("Default AKILL expiry time: \0021 day\002"));
+ else if (timeout >= 7200)
+ source.Reply(_("Default AKILL expiry time: \002{0} hours\002"), timeout / 3600);
+ else if (timeout >= 3600)
+ source.Reply(_("Default AKILL expiry time: \0021 hour\002"));
+ else if (timeout >= 120)
+ source.Reply(_("Default AKILL expiry time: \002{0} minutes\002"), timeout / 60);
+ else if (timeout >= 60)
+ source.Reply(_("Default AKILL expiry time: \0021 minute\002"));
+ else
+ source.Reply(_("Default AKILL expiry time: \002No expiration\002"));
+ }
+ if (snlines)
+ {
+ /* SNLINEs */
+ source.Reply(_("Current number of SNLINEs: \002{0}\002"), snlines->GetCount());
+ timeout = Config->GetModule("operserv")->Get<time_t>("snlineexpiry", "30d") + 59;
+ if (timeout >= 172800)
+ source.Reply(_("Default SNLINE expiry time: \002{0} days\002"), timeout / 86400);
+ else if (timeout >= 86400)
+ source.Reply(_("Default SNLINE expiry time: \0021 day\002"));
+ else if (timeout >= 7200)
+ source.Reply(_("Default SNLINE expiry time: \002{0} hours\002"), timeout / 3600);
+ else if (timeout >= 3600)
+ source.Reply(_("Default SNLINE expiry time: \0021 hour\002"));
+ else if (timeout >= 120)
+ source.Reply(_("Default SNLINE expiry time: \002{0} minutes\002"), timeout / 60);
+ else if (timeout >= 60)
+ source.Reply(_("Default SNLINE expiry time: \0021 minute\002"));
+ else
+ source.Reply(_("Default SNLINE expiry time: \002No expiration\002"));
+ }
+ if (sqlines)
+ {
+ /* SQLINEs */
+ source.Reply(_("Current number of SQLINEs: \002{0}\002"), sqlines->GetCount());
+ timeout = Config->GetModule("operserv")->Get<time_t>("sglineexpiry", "30d") + 59;
+ if (timeout >= 172800)
+ source.Reply(_("Default SQLINE expiry time: \002{0} days\002"), timeout / 86400);
+ else if (timeout >= 86400)
+ source.Reply(_("Default SQLINE expiry time: \0021 day\002"));
+ else if (timeout >= 7200)
+ source.Reply(_("Default SQLINE expiry time: \002{0} hours\002"), timeout / 3600);
+ else if (timeout >= 3600)
+ source.Reply(_("Default SQLINE expiry time: \0021 hour\002"));
+ else if (timeout >= 120)
+ source.Reply(_("Default SQLINE expiry time: \002{0} minutes\002"), timeout / 60);
+ else if (timeout >= 60)
+ source.Reply(_("Default SQLINE expiry time: \0021 minute\002"));
+ else
+ source.Reply(_("Default SQLINE expiry time: \002No expiration\002"));
+ }
+ }
+
+ void DoStatsReset(CommandSource &source)
+ {
+ Stats *stats = Serialize::GetObject<Stats *>();
+ stats->SetMaxUserCount(UserListByNick.size());
+ source.Reply(_("Statistics reset."));
+ return;
+ }
+
+ void DoStatsUptime(CommandSource &source)
+ {
+ Stats *stats = Serialize::GetObject<Stats *>();
+ time_t uptime = Anope::CurTime - Anope::StartTime;
+
+ source.Reply(_("Current users: \002{0}\002 (\002{1}\002 ops)"), UserListByNick.size(), OperCount);
+ source.Reply(_("Maximum users: \002{0}\002 ({1})"), stats->GetMaxUserCount(), Anope::strftime(stats->GetMaxUserTime(), source.GetAccount()));
+ source.Reply(_("Services up \002{0}\002."), Anope::Duration(uptime, source.GetAccount()));
+ }
+
+ void DoStatsUplink(CommandSource &source)
+ {
+ Anope::string buf;
+ for (std::set<Anope::string>::iterator it = Servers::Capab.begin(); it != Servers::Capab.end(); ++it)
+ buf += " " + *it;
+ if (!buf.empty())
+ buf.erase(buf.begin());
+
+ source.Reply(_("Uplink server: \002{0}\002"), Me->GetLinks().front()->GetName());
+ source.Reply(_("Uplink capab: {0}"), buf);
+ source.Reply(_("Servers found: {0}"), stats_count_servers(Me->GetLinks().front()));
+ }
+
+ template<typename T> void GetHashStats(const T& map, size_t& entries, size_t& buckets, size_t& max_chain)
+ {
+ entries = map.size(), buckets = map.bucket_count(), max_chain = 0;
+ for (size_t i = 0; i < buckets; ++i)
+ if (map.bucket_size(i) > max_chain)
+ max_chain = map.bucket_size(i);
+ }
+
+ void DoStatsHash(CommandSource &source)
+ {
+ size_t entries, buckets, max_chain;
+
+ GetHashStats(UserListByNick, entries, buckets, max_chain);
+ source.Reply(_("Users (nick): {0} entries, {1} buckets, longest chain is {2}"), entries, buckets, max_chain);
+
+ if (!UserListByUID.empty())
+ {
+ GetHashStats(UserListByUID, entries, buckets, max_chain);
+ source.Reply(_("Users (uid): {0} entries, {1} buckets, longest chain is {2}"), entries, buckets, max_chain);
+ }
+
+ GetHashStats(ChannelList, entries, buckets, max_chain);
+ source.Reply(_("Channels: {0} entries, {1} buckets, longest chain is {2}"), entries, buckets, max_chain);
+
+ GetHashStats(ChanServ::service->GetChannels(), entries, buckets, max_chain);
+ source.Reply(_("Registered channels: {0} entries, {1} buckets, longest chain is {2}"), entries, buckets, max_chain);
+
+ GetHashStats(NickServ::service->GetNickMap(), entries, buckets, max_chain);
+ source.Reply(_("Registered nicknames: {0} entries, {1} buckets, longest chain is {2}"), entries, buckets, max_chain);
+
+ GetHashStats(NickServ::service->GetAccountMap(), entries, buckets, max_chain);
+ source.Reply(_("Registered nick groups: {0} entries, {1} buckets, longest chain is {2}"), entries, buckets, max_chain);
+
+ if (session_service)
+ {
+ GetHashStats(session_service->GetSessions(), entries, buckets, max_chain);
+ source.Reply(_("Sessions: {0} entries, {1} buckets, longest chain is {2}"), entries, buckets, max_chain);
+ }
+ }
+
+ public:
+ CommandOSStats(Module *creator) : Command(creator, "operserv/stats", 0, 1)
+ , akills("sgline")
+ , snlines("snline")
+ , sqlines("sqline")
+ {
+ this->SetDesc(_("Show status of Services and network"));
+ this->SetSyntax("[AKILL | HASH | UPLINK | UPTIME | ALL | RESET]");
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ Anope::string extra = !params.empty() ? params[0] : "";
+
+ Log(LOG_ADMIN, source, this) << extra;
+
+ if (extra.equals_ci("RESET"))
+ return this->DoStatsReset(source);
+
+ if (extra.equals_ci("ALL") || extra.equals_ci("AKILL"))
+ this->DoStatsAkill(source);
+
+ if (extra.equals_ci("ALL") || extra.equals_ci("HASH"))
+ this->DoStatsHash(source);
+
+ if (extra.equals_ci("ALL") || extra.equals_ci("UPLINK"))
+ this->DoStatsUplink(source);
+
+ if (extra.empty() || extra.equals_ci("ALL") || extra.equals_ci("UPTIME"))
+ this->DoStatsUptime(source);
+
+ if (!extra.empty() && !extra.equals_ci("ALL") && !extra.equals_ci("AKILL") && !extra.equals_ci("HASH") && !extra.equals_ci("UPLINK") && !extra.equals_ci("UPTIME"))
+ source.Reply(_("Unknown STATS option: \002{0}\002"), extra);
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Without any option, shows the current number of users online, and the highest number of users online since Services was started, and the length of time Services has been running.\n"
+ "\n"
+ "With the \002AKILL\002 option, displays the current size of the AKILL list and the current default expiry time.\n"
+ "\n"
+ "The \002RESET\002 option currently resets the maximum user count to the number of users currently present on the network.\n"
+ "\n"
+ "The \002UPLINK\002 option displays information about the current server Anope uses as an uplink to the network.\n"
+ "\n"
+ "The \002HASH\002 option displays information about the hash maps.\n"
+ "\n"
+ "The \002ALL\002 option displays all of the above statistics."));
+ return true;
+ }
+};
+
+class OSStats : public Module
+ , public EventHook<Event::UserConnect>
+{
+ CommandOSStats commandosstats;
+ StatsType stats_type;
+
+ Stats *GetStats()
+ {
+ Stats *stats = Serialize::GetObject<Stats *>();
+ if (stats)
+ return stats;
+ else
+ return Serialize::New<Stats *>();
+ }
+
+ public:
+ OSStats(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , EventHook<Event::UserConnect>(this)
+ , commandosstats(this)
+ , stats_type(this)
+ {
+ }
+
+ void OnUserConnect(User *u, bool &exempt) override
+ {
+ Stats *stats = GetStats();
+
+ if (stats && UserListByNick.size() > stats->GetMaxUserCount())
+ {
+ stats->SetMaxUserCount(UserListByNick.size());
+ stats->SetMaxUserTime(Anope::CurTime);
+
+ Server *sserver = u->server;
+ if (sserver && sserver->IsSynced())
+ Log(this, "maxusers") << "connected - new maximum user count: " << UserListByNick.size();
+ }
+ }
+};
+
+MODULE_INIT(OSStats)
diff --git a/modules/commands/os_svs.cpp b/modules/operserv/svs.cpp
index 769adb338..0a5acac2f 100644
--- a/modules/commands/os_svs.cpp
+++ b/modules/operserv/svs.cpp
@@ -1,12 +1,20 @@
-/* OperServ core functions
+/*
+ * Anope IRC Services
*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
*
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "module.h"
@@ -20,7 +28,7 @@ class CommandOSSVSNick : public Command
this->SetSyntax(_("\037nick\037 \037newnick\037"));
}
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
{
const Anope::string &nick = params[0];
Anope::string newnick = params[1];
@@ -36,35 +44,32 @@ class CommandOSSVSNick : public Command
unsigned nicklen = Config->GetBlock("networkinfo")->Get<unsigned>("nicklen");
if (newnick.length() > nicklen)
{
- source.Reply(_("Nick \002%s\002 was truncated to %d characters."), newnick.c_str(), nicklen, newnick.c_str());
+ source.Reply(_("Nick \002{0}\002 was truncated to \002{1}\002 characters."), newnick, nicklen);
newnick = params[1].substr(0, nicklen);
}
/* Check for valid characters */
if (!IRCD->IsNickValid(newnick))
{
- source.Reply(_("Nick \002%s\002 is an illegal nickname and cannot be used."), newnick.c_str());
+ source.Reply(_("Nick \002{0}\002 is an illegal nickname and cannot be used."), newnick);
return;
}
/* Check for a nick in use or a forbidden/suspended nick */
if (!(u2 = User::Find(nick, true)))
- source.Reply(NICK_X_NOT_IN_USE, nick.c_str());
+ source.Reply(_("\002{0}\002 isn't currently online."), nick);
else if (!nick.equals_ci(newnick) && User::Find(newnick))
- source.Reply(_("Nick \002%s\002 is currently in use."), newnick.c_str());
+ source.Reply(_("\002{0}\002 is currently in use."), newnick);
else
{
- source.Reply(_("The nick \002%s\002 is now being changed to \002%s\002."), nick.c_str(), newnick.c_str());
+ source.Reply(_("\002{0}\002 is now being changed to \002{1}\002."), nick, newnick);
Log(LOG_ADMIN, source, this) << "to change " << nick << " to " << newnick;
IRCD->SendForceNickChange(u2, newnick, Anope::CurTime);
}
- return;
}
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
{
- this->SendSyntax(source);
- source.Reply(" ");
source.Reply(_("Forcefully changes a user's nickname from \037nick\037 to \037newnick\037."));
return true;
}
@@ -76,10 +81,10 @@ class CommandOSSVSJoin : public Command
CommandOSSVSJoin(Module *creator) : Command(creator, "operserv/svsjoin", 2, 2)
{
this->SetDesc(_("Forcefully join a user to a channel"));
- this->SetSyntax(_("\037nick\037 \037channel\037"));
+ this->SetSyntax(_("\037user\037 \037channel\037"));
}
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
{
if (!IRCD->CanSVSJoin)
{
@@ -90,26 +95,24 @@ class CommandOSSVSJoin : public Command
User *target = User::Find(params[0], true);
Channel *c = Channel::Find(params[1]);
if (target == NULL)
- source.Reply(NICK_X_NOT_IN_USE, params[0].c_str());
+ source.Reply(_("\002{0}\002 isn't currently online."), params[0]);
else if (source.GetUser() != target && (target->IsProtected() || target->server == Me))
- source.Reply(ACCESS_DENIED);
- else if (!IRCD->IsChannelValid(params[1]))
- source.Reply(CHAN_X_INVALID, params[1].c_str());
+ source.Reply(_("Access denied."));
+ else if (!c && !IRCD->IsChannelValid(params[1]))
+ source.Reply(_("\002{0}\002 isn't a valid channel."), params[1]);
else if (c && c->FindUser(target))
- source.Reply(_("\002%s\002 is already in \002%s\002."), target->nick.c_str(), c->name.c_str());
+ source.Reply(_("\002{0}\002 is already in \002{1}\002."), target->nick, c->name);
else
{
IRCD->SendSVSJoin(*source.service, target, params[1], "");
Log(LOG_ADMIN, source, this) << "to force " << target->nick << " to join " << params[1];
- source.Reply(_("\002%s\002 has been joined to \002%s\002."), target->nick.c_str(), params[1].c_str());
+ source.Reply(_("\002{0}\002 has been joined to \002{1}\002."), target->nick, params[1]);
}
}
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
{
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Forcefully join a user to a channel."));
+ source.Reply(_("Forcefully join \037user\037 to \037channel\037."));
return true;
}
};
@@ -123,7 +126,7 @@ class CommandOSSVSPart : public Command
this->SetSyntax(_("\037nick\037 \037channel\037 [\037reason\037]"));
}
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
{
if (!IRCD->CanSVSJoin)
{
@@ -135,13 +138,13 @@ class CommandOSSVSPart : public Command
Channel *c = Channel::Find(params[1]);
const Anope::string &reason = params.size() > 2 ? params[2] : "";
if (target == NULL)
- source.Reply(NICK_X_NOT_IN_USE, params[0].c_str());
+ source.Reply(_("\002{0}\002 isn't currently online."), params[0]);
else if (source.GetUser() != target && (target->IsProtected() || target->server == Me))
- source.Reply(ACCESS_DENIED);
+ source.Reply(_("Access denied."));
else if (!c)
- source.Reply(CHAN_X_NOT_IN_USE, params[1].c_str());
+ source.Reply(_("Channel \002{0}\002 doesn't exist."), params[1]);
else if (!c->FindUser(target))
- source.Reply(_("\002%s\002 is not in \002%s\002."), target->nick.c_str(), c->name.c_str());
+ source.Reply(_("\002{0}\002 is not in \002{1}\002."), target->nick, c->name);
else
{
IRCD->SendSVSPart(*source.service, target, params[1], reason);
@@ -149,15 +152,13 @@ class CommandOSSVSPart : public Command
Log(LOG_ADMIN, source, this) << "to force " << target->nick << " to part " << c->name << " with reason " << reason;
else
Log(LOG_ADMIN, source, this) << "to force " << target->nick << " to part " << c->name;
- source.Reply(_("\002%s\002 has been parted from \002%s\002."), target->nick.c_str(), c->name.c_str());
+ source.Reply(_("\002{0}\002 has been parted from \002{1}\002."), target->nick, c->name);
}
}
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
{
- this->SendSyntax(source);
- source.Reply(" ");
- source.Reply(_("Forcefully part a user from a channel."));
+ source.Reply(_("Forcefully part \037user\037 from \037channel\037."));
return true;
}
};
diff --git a/modules/operserv/sxline.cpp b/modules/operserv/sxline.cpp
new file mode 100644
index 000000000..748418790
--- /dev/null
+++ b/modules/operserv/sxline.cpp
@@ -0,0 +1,701 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+
+class CommandOSSXLineBase : public Command
+{
+ private:
+ virtual XLineManager* xlm() anope_abstract;
+
+ virtual void OnAdd(CommandSource &source, const std::vector<Anope::string> &params) anope_abstract;
+
+ void OnDel(CommandSource &source, const std::vector<Anope::string> &params)
+ {
+ if (!this->xlm() || this->xlm()->GetXLines().empty())
+ {
+ source.Reply(_("{0} list is empty."), source.command);
+ return;
+ }
+
+ const Anope::string &mask = params.size() > 1 ? params[1] : "";
+
+ if (mask.empty())
+ {
+ this->OnSyntaxError(source, "DEL");
+ return;
+ }
+
+ if (isdigit(mask[0]) && mask.find_first_not_of("1234567890,-") == Anope::string::npos)
+ {
+ unsigned int deleted = 0;
+
+ NumberList(mask, true,
+ [&](unsigned int number)
+ {
+ XLine *x = this->xlm()->GetEntry(number - 1);
+
+ if (!x)
+ return;
+
+ Log(LOG_ADMIN, source, this) << "to remove " << x->GetMask() << " from the list";
+
+ ++deleted;
+ x->Delete();
+ },
+ [&]()
+ {
+ if (!deleted)
+ source.Reply(_("No matching entries on the {0} list."), source.command);
+ else if (deleted == 1)
+ source.Reply(_("Deleted \0021\002 entry from the {0} list."), source.command);
+ else
+ source.Reply(_("Deleted \002{0}\002 entries from the {1} list."), deleted, source.command);
+ });
+ }
+ else
+ {
+ XLine *x = this->xlm()->HasEntry(mask);
+
+ if (!x)
+ {
+ source.Reply(_("\002{0}\002 not found on the {1] list."), mask, source.command);
+ return;
+ }
+
+ EventManager::Get()->Dispatch(&Event::DelXLine::OnDelXLine, source, x, this->xlm());
+
+ x->Delete();
+ source.Reply(_("\002{0}\002 deleted from the {1} list."), mask, source.command);
+ Log(LOG_ADMIN, source, this) << "to remove " << mask << " from the list";
+ }
+
+ if (Anope::ReadOnly)
+ source.Reply(_("Services are in read-only mode. Any changes made may not persist."));
+ }
+
+ void ProcessList(CommandSource &source, const std::vector<Anope::string> &params, ListFormatter &list)
+ {
+ if (!this->xlm() || this->xlm()->GetXLines().empty())
+ {
+ source.Reply(_("{0} list is empty."), source.command);
+ return;
+ }
+
+ const Anope::string &mask = params.size() > 1 ? params[1] : "";
+
+ if (!mask.empty() && isdigit(mask[0]) && mask.find_first_not_of("1234567890,-") == Anope::string::npos)
+ {
+ NumberList(mask, false,
+ [&](unsigned int number)
+ {
+ XLine *x = this->xlm()->GetEntry(number - 1);
+
+ if (!x)
+ return;
+
+ ListFormatter::ListEntry entry;
+ entry["Number"] = stringify(number);
+ entry["Mask"] = x->GetMask();
+ entry["By"] = x->GetBy();
+ entry["Created"] = Anope::strftime(x->GetCreated(), NULL, true);
+ entry["Expires"] = Anope::Expires(x->GetExpires(), source.nc);
+ entry["ID"] = x->GetID();
+ entry["Reason"] = x->GetReason();
+ list.AddEntry(entry);
+ },
+ []{});
+ }
+ else
+ {
+ unsigned int i = 0;
+ for (XLine *x : this->xlm()->GetXLines())
+ {
+ if (mask.empty() || mask.equals_ci(x->GetMask()) || mask == x->GetID() || Anope::Match(x->GetMask(), mask, false, true))
+ {
+ ListFormatter::ListEntry entry;
+ entry["Number"] = stringify(i + 1);
+ entry["Mask"] = x->GetMask();
+ entry["By"] = x->GetBy();
+ entry["Created"] = Anope::strftime(x->GetCreated(), NULL, true);
+ entry["Expires"] = Anope::Expires(x->GetExpires(), source.nc);
+ entry["ID"] = x->GetID();
+ entry["Reason"] = x->GetReason();
+ list.AddEntry(entry);
+ }
+ }
+ }
+
+ if (list.IsEmpty())
+ source.Reply(_("No matching entries on the {0} list."), source.command);
+ else
+ {
+ source.Reply(_("{0} list:"), source.command);
+
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+ }
+ }
+
+ void OnList(CommandSource &source, const std::vector<Anope::string> &params)
+ {
+ ListFormatter list(source.GetAccount());
+ list.AddColumn(_("Number")).AddColumn(_("Mask")).AddColumn(_("Reason"));
+
+ this->ProcessList(source, params, list);
+ }
+
+ void OnView(CommandSource &source, const std::vector<Anope::string> &params)
+ {
+ ListFormatter list(source.GetAccount());
+ list.AddColumn(_("Number")).AddColumn(_("Mask")).AddColumn(_("By")).AddColumn(_("Created")).AddColumn(_("Expires"));
+ if (Config->GetModule("operserv")->Get<bool>("akillids"))
+ list.AddColumn(_("ID"));
+ list.AddColumn(_("Reason"));
+
+ this->ProcessList(source, params, list);
+ }
+
+ void OnClear(CommandSource &source)
+ {
+ EventManager::Get()->Dispatch(&Event::DelXLine::OnDelXLine, source, nullptr, this->xlm());
+
+ for (XLine *x : this->xlm()->GetXLines())
+ x->Delete();
+
+ Log(LOG_ADMIN, source, this) << "to CLEAR the list";
+ source.Reply(_("The {0} list has been cleared."), source.command);
+ if (Anope::ReadOnly)
+ source.Reply(_("Services are in read-only mode. Any changes made may not persist."));
+ }
+ public:
+ CommandOSSXLineBase(Module *creator, const Anope::string &cmd) : Command(creator, cmd, 1, 4)
+ {
+ }
+
+ const Anope::string GetDesc(CommandSource &source) const override
+ {
+ return Anope::printf(Language::Translate(source.GetAccount(), _("Manipulate the %s list")), source.command.upper().c_str());
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &cmd = params[0];
+
+ if (cmd.equals_ci("ADD"))
+ return this->OnAdd(source, params);
+ else if (cmd.equals_ci("DEL"))
+ return this->OnDel(source, params);
+ else if (cmd.equals_ci("LIST"))
+ return this->OnList(source, params);
+ else if (cmd.equals_ci("VIEW"))
+ return this->OnView(source, params);
+ else if (cmd.equals_ci("CLEAR"))
+ return this->OnClear(source);
+ else
+ this->OnSyntaxError(source, "");
+ }
+
+ virtual bool OnHelp(CommandSource &source, const Anope::string &subcommand) override = 0;
+};
+
+class CommandOSSNLine : public CommandOSSXLineBase
+{
+ XLineManager *xlm() override
+ {
+ return this->snlines;
+ }
+
+ void OnAdd(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ if (!this->xlm())
+ return;
+
+ unsigned last_param = 2;
+ Anope::string param, expiry;
+
+ param = params.size() > 1 ? params[1] : "";
+ if (!param.empty() && param[0] == '+')
+ {
+ expiry = param;
+ param = params.size() > 2 ? params[2] : "";
+ last_param = 3;
+ }
+
+ time_t expires = !expiry.empty() ? Anope::DoTime(expiry) : Config->GetModule("operserv")->Get<time_t>("snlineexpiry", "30d");
+ /* If the expiry given does not contain a final letter, it's in days,
+ * said the doc. Ah well.
+ */
+ if (!expiry.empty() && isdigit(expiry[expiry.length() - 1]))
+ expires *= 86400;
+ /* Do not allow less than a minute expiry time */
+ if (expires && expires < 60)
+ {
+ source.Reply(_("Invalid expiry time \002{0}\002."), expiry);
+ return;
+ }
+ else if (expires > 0)
+ expires += Anope::CurTime;
+
+ if (param.empty())
+ {
+ this->OnSyntaxError(source, "ADD");
+ return;
+ }
+
+ Anope::string rest = param;
+ if (params.size() > last_param)
+ rest += " " + params[last_param];
+
+ if (rest.find(':') == Anope::string::npos)
+ {
+ this->OnSyntaxError(source, "ADD");
+ return;
+ }
+
+ sepstream sep(rest, ':');
+ Anope::string mask;
+ sep.GetToken(mask);
+ Anope::string reason = sep.GetRemaining();
+
+ if (mask.empty() || reason.empty())
+ {
+ this->OnSyntaxError(source, "ADD");
+ return;
+ }
+
+ if (mask[0] == '/' && mask[mask.length() - 1] == '/')
+ {
+ if (!Config->regex_flags)
+ {
+ source.Reply(_("Regex is disabled."));
+ return;
+ }
+
+ Anope::string stripped_mask = mask.substr(1, mask.length() - 2);
+ try
+ {
+ std::regex(stripped_mask.str(), Config->regex_flags);
+ }
+ catch (const std::regex_error &ex)
+ {
+ source.Reply(ex.what());
+ return;
+ }
+ }
+
+ /* Clean up the last character of the mask if it is a space
+ * See bug #761
+ */
+ unsigned masklen = mask.length();
+ if (mask[masklen - 1] == ' ')
+ mask.erase(masklen - 1);
+
+ if (Config->GetModule("operserv")->Get<bool>("addakiller", "yes") && !source.GetNick().empty())
+ reason = "[" + source.GetNick() + "] " + reason;
+
+ if (mask.find_first_not_of("/.*?") == Anope::string::npos)
+ {
+ source.Reply(_("\002{0}\002 coverage is too wide; please use a more specific mask."), mask);
+ return;
+ }
+
+ if (!this->xlm()->CanAdd(source, mask, expires, reason))
+ return;
+
+ XLine *x = Serialize::New<XLine *>();
+ x->SetMask(mask);
+ x->SetBy(source.GetNick());
+ x->SetExpires(expires);
+ x->SetReason(reason);
+
+ if (Config->GetModule("operserv")->Get<bool>("akillids"))
+ x->SetID(XLineManager::GenerateUID());
+
+ unsigned int affected = 0;
+ for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it)
+ if (this->xlm()->Check(it->second, x))
+ ++affected;
+ float percent = static_cast<float>(affected) / static_cast<float>(UserListByNick.size()) * 100.0;
+
+ if (percent > 95)
+ {
+ source.Reply(_("\002{0}\002 coverage is too wide; please use a more specific mask."), mask);
+ Log(LOG_ADMIN, source, this) << "tried to " << source.command << " " << percent << "% of the network (" << affected << " users)";
+ delete x;
+ return;
+ }
+
+ EventReturn MOD_RESULT;
+ MOD_RESULT = EventManager::Get()->Dispatch(&Event::AddXLine::OnAddXLine, source, x, this->xlm());
+ if (MOD_RESULT == EVENT_STOP)
+ {
+ delete x;
+ return;
+ }
+
+ this->xlm()->AddXLine(x);
+
+ if (Config->GetModule("operserv")->Get<bool>("killonsnline", "yes"))
+ {
+ Anope::string rreason = "G-Lined: " + reason;
+
+ for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it)
+ {
+ User *user = it->second;
+
+ if (!user->HasMode("OPER") && user->server != Me && this->xlm()->Check(user, x))
+ user->Kill(Me, rreason);
+ }
+
+ this->xlm()->Send(NULL, x);
+ }
+
+ source.Reply(_("\002{0}\002 added to the {1} list."), mask, source.command);
+ Log(LOG_ADMIN, source, this) << "on " << mask << " (" << reason << "), expires in " << (expires ? Anope::Duration(expires - Anope::CurTime) : "never") << " [affects " << affected << " user(s) (" << percent << "%)]";
+ if (Anope::ReadOnly)
+ source.Reply(_("Services are in read-only mode. Any changes made may not persist."));
+ }
+
+ ServiceReference<XLineManager> snlines;
+ public:
+ CommandOSSNLine(Module *creator) : CommandOSSXLineBase(creator, "operserv/snline")
+ , snlines("xlinemanager/snline")
+ {
+ this->SetSyntax(_("ADD [+\037expiry\037] \037mask\037:\037reason\037"));
+ this->SetSyntax(_("DEL {\037mask\037 | \037entry-num\037 | \037list\037 | \037id\037}"));
+ this->SetSyntax(_("LIST [\037mask\037 | \037list\037 | \037id\037]"));
+ this->SetSyntax(_("VIEW [\037mask\037 | \037list\037 | \037id\037]"));
+ this->SetSyntax("CLEAR");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ if (subcommand.equals_ci("ADD"))
+ {
+ source.Reply(_("\002{0} ADD\002 adds the given \037mask\037 to the {0} list for the given \037reason\037."
+ " \037expiry\037 is specified as an integer followed by one of \037d\037 (days), \037h\037 (hours), or \037m\037 (minutes)."
+ " If a unit specifier is not included, the default is days, so \037+30\037 by itself means 30 days."
+ " To add a {0} which does not expire, use \037+0\037."
+ " The default {0} expiry time is \002{1}\002."
+ " Because the real name may contain spaces, the separator between it and the reason is a \002colon\002."),
+ source.command, Anope::Duration(Config->GetModule("operserv")->Get<time_t>("snlineexpiry", "30d"), source.GetAccount()));
+
+ const Anope::string &regexengine = Config->GetBlock("options")->Get<Anope::string>("regexengine");
+ if (!regexengine.empty())
+ {
+ source.Reply(" ");
+ source.Reply(_("Regex matches are also supported using the {0} engine. Enclose your mask in // if this is desired."), regexengine);
+ }
+ }
+ else if (subcommand.equals_ci("DEL"))
+ source.Reply(_("The \002{0} DEL\002 command removes the given \037mask\037 from the {0} list if it is present."
+ " If a list of entry numbers is given, those entries are deleted."));
+ else if (subcommand.equals_ci("LIST") || subcommand.equals_ci("VIEW"))
+ source.Reply(_("The \002{0} LIST\002 and \002{0} VIEW\002 commands displays the {0} list.\n"
+ "If a wildcard \037mask\037 is given, only those entries matching the \037mask\037 are displayed."
+ " If a list of entry numbers is given, only those entries are shown."
+ " \002VIEW\002 is similar to \002LIST\002 but also shows who created the {0}, when it was created, and when it expires.\n"
+ "\n"
+ "Example:\n"
+ "\n"
+ " {0} LIST 2-5,7-9\n"
+ " Lists {0} entries numbered 2 through 5 and 7 through 9.\n"),
+ source.command);
+ else if (subcommand.equals_ci("CLEAR"))
+ source.Reply(_("\002{0} CLEAR\002 removes all entries from the {0} list."),
+ source.command);
+ else
+ {
+ CommandInfo *help = source.service->FindCommand("generic/help");
+
+ source.Reply(_("Allows you to manipulate the {0} list."
+ " If a user attempts to use a realname that matches a {0} mask, services will kill the user."
+ "\n"
+ "The \002ADD\002 command adds \037mask\037 to the {0} list.\n"
+ "\002{msg}{service} {help} {command} ADD\002 for more information.\n"
+ "\n"
+ "The \002DEL\002 command removes \037mask\037 from the {0} list.\n"
+ "\002{msg}{service} {help} {command} DEL\002 for more information.\n"
+ "\n"
+ "The \002LIST\002 and \002VIEW\002 commands both show the {0} list, but \002VIEW\002 also shows who created the {0} entry, when it was created, and when it expires.\n"
+ "\002{msg}{service} {help} {command} [LIST | VIEW]\002 for more information.\n"
+ "\n"
+ "The \002CLEAR\002 command clears th auto kill list."
+ "\002{msg}{service} {help} {command} CLEAR\002 for more information.\n"),
+ "msg"_kw = Config->StrictPrivmsg, "service"_kw = source.service->nick, "help"_kw = help->cname, "command"_kw = source.command);
+ }
+ return true;
+ }
+};
+
+class CommandOSSQLine : public CommandOSSXLineBase
+{
+ XLineManager *xlm() override
+ {
+ return this->sqlines;
+ }
+
+ void OnAdd(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ if (!this->xlm())
+ return;
+
+ unsigned last_param = 2;
+ Anope::string expiry, mask;
+
+ mask = params.size() > 1 ? params[1] : "";
+ if (!mask.empty() && mask[0] == '+')
+ {
+ expiry = mask;
+ mask = params.size() > 2 ? params[2] : "";
+ last_param = 3;
+ }
+
+ time_t expires = !expiry.empty() ? Anope::DoTime(expiry) : Config->GetModule("operserv")->Get<time_t>("sqlineexpiry", "30d");
+ /* If the expiry given does not contain a final letter, it's in days,
+ * said the doc. Ah well.
+ */
+ if (!expiry.empty() && isdigit(expiry[expiry.length() - 1]))
+ expires *= 86400;
+ /* Do not allow less than a minute expiry time */
+ if (expires && expires < 60)
+ {
+ source.Reply(_("Invalid expiry time \002{0}\002."), expiry);
+ return;
+ }
+ else if (expires > 0)
+ expires += Anope::CurTime;
+
+ if (params.size() <= last_param)
+ {
+ this->OnSyntaxError(source, "ADD");
+ return;
+ }
+
+ Anope::string reason = params[last_param];
+ if (last_param == 2 && params.size() > 3)
+ reason += " " + params[3];
+
+ if (mask.empty() || reason.empty())
+ {
+ this->OnSyntaxError(source, "ADD");
+ return;
+ }
+
+ if (mask[0] == '/' && mask[mask.length() - 1] == '/')
+ {
+ if (!Config->regex_flags)
+ {
+ source.Reply(_("Regex is disabled."));
+ return;
+ }
+
+ Anope::string stripped_mask = mask.substr(1, mask.length() - 2);
+ try
+ {
+ std::regex(stripped_mask.str(), Config->regex_flags);
+ }
+ catch (const std::regex_error &ex)
+ {
+ source.Reply(ex.what());
+ return;
+ }
+ }
+
+ if (Config->GetModule("operserv")->Get<bool>("addakiller", "yes") && !source.GetNick().empty())
+ reason = "[" + source.GetNick() + "] " + reason;
+
+ if (mask.find_first_not_of("./?*") == Anope::string::npos)
+ {
+ source.Reply(_("\002{0}\002 coverage is too wide; please use a more specific mask."), mask);
+ return;
+ }
+
+ if (!this->sqlines->CanAdd(source, mask, expires, reason))
+ return;
+
+ XLine *x = Serialize::New<XLine *>();
+ x->SetMask(mask);
+ x->SetBy(source.GetNick());
+ x->SetExpires(expires);
+ x->SetReason(reason);
+
+ if (Config->GetModule("operserv")->Get<bool>("akillids"))
+ x->SetID(XLineManager::GenerateUID());
+
+ unsigned int affected = 0;
+ for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it)
+ if (this->xlm()->Check(it->second, x))
+ ++affected;
+ float percent = static_cast<float>(affected) / static_cast<float>(UserListByNick.size()) * 100.0;
+
+ if (percent > 95)
+ {
+ source.Reply(_("\002{0}\002 coverage is too wide; please use a more specific mask."), mask);
+ Log(LOG_ADMIN, source, this) << "tried to SQLine " << percent << "% of the network (" << affected << " users)";
+ delete x;
+ return;
+ }
+
+ EventReturn MOD_RESULT;
+ MOD_RESULT = EventManager::Get()->Dispatch(&Event::AddXLine::OnAddXLine, source, x, this->xlm());
+ if (MOD_RESULT == EVENT_STOP)
+ {
+ delete x;
+ return;
+ }
+
+ this->xlm()->AddXLine(x);
+
+ if (Config->GetModule("operserv")->Get<bool>("killonsqline", "yes"))
+ {
+ Anope::string rreason = "Q-Lined: " + reason;
+
+ if (mask[0] == '#')
+ {
+ for (channel_map::const_iterator cit = ChannelList.begin(), cit_end = ChannelList.end(); cit != cit_end; ++cit)
+ {
+ Channel *c = cit->second;
+
+ if (!Anope::Match(c->name, mask, false, true))
+ continue;
+
+ std::vector<User *> users;
+ for (Channel::ChanUserList::iterator it = c->users.begin(), it_end = c->users.end(); it != it_end; ++it)
+ {
+ ChanUserContainer *uc = it->second;
+ User *user = uc->user;
+
+ if (!user->HasMode("OPER") && user->server != Me)
+ users.push_back(user);
+ }
+
+ for (unsigned i = 0; i < users.size(); ++i)
+ c->Kick(NULL, users[i], "%s", reason.c_str());
+ }
+ }
+ else
+ {
+ for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it)
+ {
+ User *user = it->second;
+
+ if (!user->HasMode("OPER") && user->server != Me && this->xlm()->Check(user, x))
+ user->Kill(Me, rreason);
+ }
+ }
+
+ this->xlm()->Send(NULL, x);
+ }
+
+ source.Reply(_("\002{0}\002 added to the {1} list."), mask, source.command);
+ Log(LOG_ADMIN, source, this) << "on " << mask << " (" << reason << "), expires in " << (expires ? Anope::Duration(expires - Anope::CurTime) : "never") << " [affects " << affected << " user(s) (" << percent << "%)]";
+ if (Anope::ReadOnly)
+ source.Reply(_("Services are in read-only mode. Any changes made may not persist."));
+ }
+
+ ServiceReference<XLineManager> sqlines;
+ public:
+ CommandOSSQLine(Module *creator) : CommandOSSXLineBase(creator, "operserv/sqline")
+ , sqlines("xlinemanager/sqline")
+ {
+ this->SetSyntax(_("ADD [+\037expiry\037] \037mask\037 \037reason\037"));
+ this->SetSyntax(_("DEL {\037mask\037 | \037entry-num\037 | \037list\037 | \037id\037}"));
+ this->SetSyntax(_("LIST [\037mask\037 | \037list\037 | \037id\037]"));
+ this->SetSyntax(_("VIEW [\037mask\037 | \037list\037 | \037id\037]"));
+ this->SetSyntax("CLEAR");
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ if (subcommand.equals_ci("ADD"))
+ {
+ source.Reply(_("\002{0} ADD\002 adds the given \037mask\037 to the {0} list for the given \037reason\037."
+ " \037expiry\037 is specified as an integer followed by one of \037d\037 (days), \037h\037 (hours), or \037m\037 (minutes)."
+ " If a unit specifier is not included, the default is days, so \037+30\037 by itself means 30 days."
+ " To add a {0} which does not expire, use \037+0\037."
+ " The default {0} expiry time is \002{1}\002."),
+ source.command, Anope::Duration(Config->GetModule("operserv")->Get<time_t>("sqlineexpiry", "30d"), source.GetAccount()));
+
+ const Anope::string &regexengine = Config->GetBlock("options")->Get<Anope::string>("regexengine");
+ if (!regexengine.empty())
+ {
+ source.Reply(" ");
+ source.Reply(_("Regex matches are also supported using the {0} engine. Enclose your mask in // if this is desired."), regexengine);
+ }
+ }
+ else if (subcommand.equals_ci("DEL"))
+ source.Reply(_("The \002{0} DEL\002 command removes the given \037mask\037 from the {0} list if it is present."
+ " If a list of entry numbers is given, those entries are deleted."));
+ else if (subcommand.equals_ci("LIST") || subcommand.equals_ci("VIEW"))
+ source.Reply(_("The \002{0} LIST\002 and \002{0} VIEW\002 commands displays the {0} list.\n"
+ "If a wildcard \037mask\037 is given, only those entries matching the \037mask\037 are displayed."
+ " If a list of entry numbers is given, only those entries are shown."
+ " \002VIEW\002 is similar to \002LIST\002 but also shows who created the {0}, when it was created, and when it expires.\n"
+ "\n"
+ "Example:\n"
+ "\n"
+ " {0} LIST 2-5,7-9\n"
+ " Lists {0} entries numbered 2 through 5 and 7 through 9.\n"),
+ source.command);
+ else if (subcommand.equals_ci("CLEAR"))
+ source.Reply(_("\002{0} CLEAR\002 removes all entries from the {0} list."),
+ source.command);
+ else
+ {
+ CommandInfo *help = source.service->FindCommand("generic/help");
+
+ source.Reply(_("Allows you to manipulate the {0} list."
+ " If a user attempts to use a nickname that matches a {0} mask, services will force the user off of the nickname."
+ " If the first character of the mask is a #, services will prevent the use of matching channels."
+ "\n"
+ "The \002ADD\002 command adds \037mask\037 to the {0} list.\n"
+ "\002{msg}{service} {help} {command} ADD\002 for more information.\n"
+ "\n"
+ "The \002DEL\002 command removes \037mask\037 from the {0} list.\n"
+ "\002{msg}{service} {help} {command} DEL\002 for more information.\n"
+ "\n"
+ "The \002LIST\002 and \002VIEW\002 commands both show the {0} list, but \002VIEW\002 also shows who created the {0} entry, when it was created, and when it expires.\n"
+ "\002{msg}{service} {help} {command} [LIST | VIEW]\002 for more information.\n"
+ "\n"
+ "The \002CLEAR\002 command clears th auto kill list."
+ "\002{msg}{service} {help} {command} CLEAR\002 for more information.\n"),
+ "msg"_kw = Config->StrictPrivmsg, "service"_kw = source.service->nick, "help"_kw = help->cname, "command"_kw = source.command);
+ }
+ return true;
+ }
+};
+
+class OSSXLine : public Module
+{
+ CommandOSSNLine commandossnline;
+ CommandOSSQLine commandossqline;
+
+ public:
+ OSSXLine(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
+ commandossnline(this), commandossqline(this)
+ {
+ }
+};
+
+MODULE_INIT(OSSXLine)
diff --git a/modules/operserv/update.cpp b/modules/operserv/update.cpp
new file mode 100644
index 000000000..753644415
--- /dev/null
+++ b/modules/operserv/update.cpp
@@ -0,0 +1,57 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+
+class CommandOSUpdate : public Command
+{
+ public:
+ CommandOSUpdate(Module *creator) : Command(creator, "operserv/update", 0, 0)
+ {
+ this->SetDesc(_("Force the Services databases to be updated immediately"));
+ }
+
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
+ {
+ Log(LOG_ADMIN, source, this);
+ source.Reply(_("Updating databases."));
+ Anope::SaveDatabases();
+ return;
+ }
+
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
+ {
+ source.Reply(_("Causes Services to update all database files."));
+ return true;
+ }
+};
+
+class OSUpdate : public Module
+{
+ CommandOSUpdate commandosupdate;
+
+ public:
+ OSUpdate(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , commandosupdate(this)
+ {
+
+ }
+};
+
+MODULE_INIT(OSUpdate)
diff --git a/modules/protocol/CMakeLists.txt b/modules/protocol/CMakeLists.txt
new file mode 100644
index 000000000..9a236d6d0
--- /dev/null
+++ b/modules/protocol/CMakeLists.txt
@@ -0,0 +1,2 @@
+build_modules(${CMAKE_CURRENT_SOURCE_DIR})
+build_modules_dependencies(${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/modules/protocol/bahamut.cpp b/modules/protocol/bahamut.cpp
index 16d593fab..a32a7606d 100644
--- a/modules/protocol/bahamut.cpp
+++ b/modules/protocol/bahamut.cpp
@@ -1,12 +1,20 @@
-/* Bahamut functions
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
*
- * Please read COPYING and README for further details.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "module.h"
@@ -16,7 +24,7 @@ class ChannelModeFlood : public ChannelModeParam
public:
ChannelModeFlood(char modeChar, bool minusNoArg) : ChannelModeParam("FLOOD", modeChar, minusNoArg) { }
- bool IsValid(Anope::string &value) const anope_override
+ bool IsValid(Anope::string &value) const override
{
try
{
@@ -45,91 +53,97 @@ class BahamutIRCdProto : public IRCDProto
MaxModes = 60;
}
- void SendModeInternal(const MessageSource &source, const Channel *dest, const Anope::string &buf) anope_override
+ void SendModeInternal(const MessageSource &source, const Channel *dest, const Anope::string &buf) override
{
if (Servers::Capab.count("TSMODE") > 0)
{
- UplinkSocket::Message(source) << "MODE " << dest->name << " " << dest->creation_time << " " << buf;
+ IRCMessage message(source, "MODE", dest->name, dest->creation_time);
+ message.TokenizeAndPush(buf);
+ Uplink::SendMessage(message);
}
else
+ {
IRCDProto::SendModeInternal(source, dest, buf);
+ }
}
- void SendModeInternal(const MessageSource &source, User *u, const Anope::string &buf) anope_override
+ void SendModeInternal(const MessageSource &source, User *u, const Anope::string &buf) override
{
- UplinkSocket::Message(source) << "SVSMODE " << u->nick << " " << u->timestamp << " " << buf;
+ IRCMessage message(source, "SVSMODE", u->nick, u->timestamp);
+ message.TokenizeAndPush(buf);
+ Uplink::SendMessage(message);
}
- void SendGlobalNotice(BotInfo *bi, const Server *dest, const Anope::string &msg) anope_override
+ void SendGlobalNotice(ServiceBot *bi, const Server *dest, const Anope::string &msg) override
{
- UplinkSocket::Message(bi) << "NOTICE $" << dest->GetName() << " :" << msg;
+ Uplink::Send(bi, "NOTICE", "$" + dest->GetName(), msg);
}
- void SendGlobalPrivmsg(BotInfo *bi, const Server *dest, const Anope::string &msg) anope_override
+ void SendGlobalPrivmsg(ServiceBot *bi, const Server *dest, const Anope::string &msg) override
{
- UplinkSocket::Message(bi) << "PRIVMSG $" << dest->GetName() << " :" << msg;
+ Uplink::Send(bi, "PRIVMSG", "$" + dest->GetName(), msg);
}
/* SVSHOLD - set */
- void SendSVSHold(const Anope::string &nick, time_t time) anope_override
+ void SendSVSHold(const Anope::string &nick, time_t time) override
{
- UplinkSocket::Message(Me) << "SVSHOLD " << nick << " " << time << " :Being held for registered user";
+ Uplink::Send(Me, "SVSHOLD", nick, time, "Being held for registered user");
}
/* SVSHOLD - release */
- void SendSVSHoldDel(const Anope::string &nick) anope_override
+ void SendSVSHoldDel(const Anope::string &nick) override
{
- UplinkSocket::Message(Me) << "SVSHOLD " << nick << " 0";
+ Uplink::Send(Me, "SVSHOLD", nick, 0);
}
/* SQLINE */
- void SendSQLine(User *, const XLine *x) anope_override
+ void SendSQLine(User *, XLine *x) override
{
- UplinkSocket::Message() << "SQLINE " << x->mask << " :" << x->GetReason();
+ Uplink::Send(Me, "SQLINE", x->GetMask(), x->GetReason());
}
/* UNSLINE */
- void SendSGLineDel(const XLine *x) anope_override
+ void SendSGLineDel(XLine *x) override
{
- UplinkSocket::Message() << "UNSGLINE 0 :" << x->mask;
+ Uplink::Send("UNSGLINE", 0, x->GetMask());
}
/* UNSZLINE */
- void SendSZLineDel(const XLine *x) anope_override
+ void SendSZLineDel(XLine *x) override
{
/* this will likely fail so its only here for legacy */
- UplinkSocket::Message() << "UNSZLINE 0 " << x->GetHost();
+ Uplink::Send("UNSZLINE", 0, x->GetHost());
/* this is how we are supposed to deal with it */
- UplinkSocket::Message() << "RAKILL " << x->GetHost() << " *";
+ Uplink::Send("RAKILL", x->GetHost(), "*");
}
/* SZLINE */
- void SendSZLine(User *, const XLine *x) anope_override
+ void SendSZLine(User *, XLine *x) override
{
// Calculate the time left before this would expire, capping it at 2 days
- time_t timeleft = x->expires - Anope::CurTime;
- if (timeleft > 172800 || !x->expires)
+ time_t timeleft = x->GetExpires() - Anope::CurTime;
+ if (timeleft > 172800 || !x->GetExpires())
timeleft = 172800;
/* this will likely fail so its only here for legacy */
- UplinkSocket::Message() << "SZLINE " << x->GetHost() << " :" << x->GetReason();
+ Uplink::Send("SZLINE", x->GetHost(), x->GetReason());
/* this is how we are supposed to deal with it */
- UplinkSocket::Message() << "AKILL " << x->GetHost() << " * " << timeleft << " " << x->by << " " << Anope::CurTime << " :" << x->GetReason();
+ Uplink::Send("AKILL", x->GetHost(), "*", timeleft, x->GetBy(), Anope::CurTime, x->GetReason());
}
/* SVSNOOP */
- void SendSVSNOOP(const Server *server, bool set) anope_override
+ void SendSVSNOOP(const Server *server, bool set) override
{
- UplinkSocket::Message() << "SVSNOOP " << server->GetName() << " " << (set ? "+" : "-");
+ Uplink::Send("SVSNOOP", server->GetName(), set ? "+" : "-");
}
/* SGLINE */
- void SendSGLine(User *, const XLine *x) anope_override
+ void SendSGLine(User *, XLine *x) override
{
- UplinkSocket::Message() << "SGLINE " << x->mask.length() << " :" << x->mask << ":" << x->GetReason();
+ Uplink::Send("SGLINE", x->GetMask().length(), x->GetMask() + ":" + x->GetReason());
}
/* RAKILL */
- void SendAkillDel(const XLine *x) anope_override
+ void SendAkillDel(XLine *x) override
{
if (x->IsRegex() || x->HasNickOrReal())
return;
@@ -145,25 +159,26 @@ class BahamutIRCdProto : public IRCDProto
}
}
- UplinkSocket::Message() << "RAKILL " << x->GetHost() << " " << x->GetUser();
+ Uplink::Send("RAKKILL", x->GetHost(), x->GetUser());
}
/* TOPIC */
- void SendTopic(const MessageSource &source, Channel *c) anope_override
+ void SendTopic(const MessageSource &source, Channel *c) override
{
- UplinkSocket::Message(source) << "TOPIC " << c->name << " " << c->topic_setter << " " << c->topic_ts << " :" << c->topic;
+ Uplink::Send(source, "TOPIC", c->name, c->topic_setter, c->topic_ts, c->topic);
}
/* UNSQLINE */
- void SendSQLineDel(const XLine *x) anope_override
+ void SendSQLineDel(XLine *x) override
{
- UplinkSocket::Message() << "UNSQLINE " << x->mask;
+ Uplink::Send("UNSQLINE", x->GetMask());
}
/* JOIN - SJOIN */
- void SendJoin(User *user, Channel *c, const ChannelStatus *status) anope_override
+ void SendJoin(User *user, Channel *c, const ChannelStatus *status) override
{
- UplinkSocket::Message(user) << "SJOIN " << c->creation_time << " " << c->name;
+ Uplink::Send(user, "SJOIN", c->creation_time, c->name);
+
if (status)
{
/* First save the channel status incase uc->Status == status */
@@ -175,7 +190,7 @@ class BahamutIRCdProto : public IRCDProto
if (uc != NULL)
uc->status.Clear();
- BotInfo *setter = BotInfo::Find(user->GetUID());
+ ServiceBot *setter = ServiceBot::Find(user->GetUID());
for (size_t i = 0; i < cs.Modes().length(); ++i)
c->SetMode(setter, ModeManager::FindChannelModeByChar(cs.Modes()[i]), user->GetUID(), false);
@@ -184,7 +199,7 @@ class BahamutIRCdProto : public IRCDProto
}
}
- void SendAkill(User *u, XLine *x) anope_override
+ void SendAkill(User *u, XLine *x) override
{
if (x->IsRegex() || x->HasNickOrReal())
{
@@ -192,21 +207,26 @@ class BahamutIRCdProto : public IRCDProto
{
/* No user (this akill was just added), and contains nick and/or realname. Find users that match and ban them */
for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it)
- if (x->manager->Check(it->second, x))
+ if (x->GetManager()->Check(it->second, x))
this->SendAkill(it->second, x);
return;
}
- const XLine *old = x;
+ XLine *old = x;
- if (old->manager->HasEntry("*@" + u->host))
+ if (old->GetManager()->HasEntry("*@" + u->host))
return;
/* We can't akill x as it has a nick and/or realname included, so create a new akill for *@host */
- x = new XLine("*@" + u->host, old->by, old->expires, old->reason, old->id);
- old->manager->AddXLine(x);
-
- Log(Config->GetClient("OperServ"), "akill") << "AKILL: Added an akill for " << x->mask << " because " << u->GetMask() << "#" << u->realname << " matches " << old->mask;
+ x = Serialize::New<XLine *>();
+ x->SetMask("*@" + u->host);
+ x->SetBy(old->GetBy());
+ x->SetExpires(old->GetExpires());
+ x->SetReason(old->GetReason());
+ x->SetID(old->GetID());
+ old->GetManager()->AddXLine(x);
+
+ Log(Config->GetClient("OperServ"), "akill") << "AKILL: Added an akill for " << x->GetMask() << " because " << u->GetMask() << "#" << u->realname << " matches " << old->GetMask();
}
/* ZLine if we can instead */
@@ -221,46 +241,47 @@ class BahamutIRCdProto : public IRCDProto
}
// Calculate the time left before this would expire, capping it at 2 days
- time_t timeleft = x->expires - Anope::CurTime;
+ time_t timeleft = x->GetExpires() - Anope::CurTime;
if (timeleft > 172800)
timeleft = 172800;
- UplinkSocket::Message() << "AKILL " << x->GetHost() << " " << x->GetUser() << " " << timeleft << " " << x->by << " " << Anope::CurTime << " :" << x->GetReason();
+
+ Uplink::Send("AKILL", x->GetHost(), x->GetUser(), timeleft, x->GetBy(), Anope::CurTime, x->GetReason());
}
/*
Note: if the stamp is null 0, the below usage is correct of Bahamut
*/
- void SendSVSKillInternal(const MessageSource &source, User *user, const Anope::string &buf) anope_override
+ void SendSVSKillInternal(const MessageSource &source, User *user, const Anope::string &buf) override
{
- UplinkSocket::Message(source) << "SVSKILL " << user->nick << " :" << buf;
+ Uplink::Send(source, "SVSKILL", user->nick, buf);
}
- void SendBOB() anope_override
+ void SendBOB() override
{
- UplinkSocket::Message() << "BURST";
+ Uplink::Send("BURST");
}
- void SendEOB() anope_override
+ void SendEOB() override
{
- UplinkSocket::Message() << "BURST 0";
+ Uplink::Send("BURST", 0);
}
- void SendClientIntroduction(User *u) anope_override
+ void SendClientIntroduction(User *u) override
{
Anope::string modes = "+" + u->GetModes();
- UplinkSocket::Message() << "NICK " << u->nick << " 1 " << u->timestamp << " " << modes << " " << u->GetIdent() << " " << u->host << " " << u->server->GetName() << " 0 0 :" << u->realname;
+ Uplink::Send("NICK", u->nick, 1, u->timestamp, modes, u->GetIdent(), u->host, u->server->GetName(), 0, 0, u->realname);
}
/* SERVER */
- void SendServer(const Server *server) anope_override
+ void SendServer(const Server *server) override
{
- UplinkSocket::Message() << "SERVER " << server->GetName() << " " << server->GetHops() << " :" << server->GetDescription();
+ Uplink::Send("SERVER", server->GetName(), server->GetHops(), server->GetDescription());
}
- void SendConnect() anope_override
+ void SendConnect() override
{
- UplinkSocket::Message() << "PASS " << Config->Uplinks[Anope::CurrentUplink].password << " :TS";
- UplinkSocket::Message() << "CAPAB SSJOIN NOQUIT BURST UNCONNECT NICKIP TSMODE TS3";
+ Uplink::Send("PASS", Config->Uplinks[Anope::CurrentUplink].password, "TS");
+ Uplink::Send("CAPAB", "SSJOIN NOQUIT BURST UNCONNECT NICKIP TSMODE TS3");
SendServer(Me);
/*
* SVINFO
@@ -270,24 +291,24 @@ class BahamutIRCdProto : public IRCDProto
* parv[3] = server is standalone or connected to non-TS only
* parv[4] = server's idea of UTC time
*/
- UplinkSocket::Message() << "SVINFO 3 1 0 :" << Anope::CurTime;
+ Uplink::Send("SVINFO", 3, 1, 0, Anope::CurTime);
this->SendBOB();
}
- void SendChannel(Channel *c) anope_override
+ void SendChannel(Channel *c) override
{
Anope::string modes = c->GetModes(true, true);
if (modes.empty())
modes = "+";
- UplinkSocket::Message() << "SJOIN " << c->creation_time << " " << c->name << " " << modes << " :";
+ Uplink::Send("SJOIN", c->creation_time, c->name, modes, "");
}
- void SendLogin(User *u, NickAlias *) anope_override
+ void SendLogin(User *u, NickServ::Nick *) override
{
IRCD->SendMode(Config->GetClient("NickServ"), u, "+d %d", u->signon);
}
- void SendLogout(User *u) anope_override
+ void SendLogout(User *u) override
{
IRCD->SendMode(Config->GetClient("NickServ"), u, "+d 1");
}
@@ -297,7 +318,7 @@ struct IRCDMessageBurst : IRCDMessage
{
IRCDMessageBurst(Module *creator) : IRCDMessage(creator, "BURST", 0) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); SetFlag(IRCDMESSAGE_SOFT_LIMIT); }
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
{
/* If we found a server with the given source, that one just
* finished bursting. If there was no source, then our uplink
@@ -315,7 +336,7 @@ struct IRCDMessageMode : IRCDMessage
{
IRCDMessageMode(Module *creator, const Anope::string &sname) : IRCDMessage(creator, sname, 2) { SetFlag(IRCDMESSAGE_SOFT_LIMIT); }
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
{
if (params.size() > 2 && IRCD->IsChannelValid(params[0]))
{
@@ -366,7 +387,7 @@ struct IRCDMessageNick : IRCDMessage
{
IRCDMessageNick(Module *creator) : IRCDMessage(creator, "NICK", 2) { SetFlag(IRCDMESSAGE_SOFT_LIMIT); }
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
{
if (params.size() == 10)
{
@@ -377,13 +398,13 @@ struct IRCDMessageNick : IRCDMessage
return;
}
- NickAlias *na = NULL;
+ NickServ::Nick *na = NULL;
time_t signon = params[2].is_pos_number_only() ? convertTo<time_t>(params[2]) : 0,
stamp = params[7].is_pos_number_only() ? convertTo<time_t>(params[7]) : 0;
- if (signon && signon == stamp)
- na = NickAlias::Find(params[0]);
+ if (signon && signon == stamp && NickServ::service)
+ na = NickServ::service->FindNick(params[0]);
- User::OnIntroduce(params[0], params[4], params[5], "", params[8], s, params[9], signon, params[3], "", na ? *na->nc : NULL);
+ User::OnIntroduce(params[0], params[4], params[5], "", params[8], s, params[9], signon, params[3], "", na ? na->GetAccount() : NULL);
}
else
source.GetUser()->ChangeNick(params[0]);
@@ -394,7 +415,7 @@ struct IRCDMessageServer : IRCDMessage
{
IRCDMessageServer(Module *creator) : IRCDMessage(creator, "SERVER", 3) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
{
unsigned int hops = Anope::string(params[1]).is_pos_number_only() ? convertTo<unsigned>(params[1]) : 0;
new Server(source.GetServer() == NULL ? Me : source.GetServer(), params[0], hops, params[2]);
@@ -405,7 +426,7 @@ struct IRCDMessageSJoin : IRCDMessage
{
IRCDMessageSJoin(Module *creator) : IRCDMessage(creator, "SJOIN", 2) { SetFlag(IRCDMESSAGE_SOFT_LIMIT); }
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
{
Anope::string modes;
if (params.size() >= 4)
@@ -461,7 +482,7 @@ struct IRCDMessageTopic : IRCDMessage
{
IRCDMessageTopic(Module *creator) : IRCDMessage(creator, "TOPIC", 4) { }
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
{
Channel *c = Channel::Find(params[0]);
if (c)
@@ -470,6 +491,7 @@ struct IRCDMessageTopic : IRCDMessage
};
class ProtoBahamut : public Module
+ , public EventHook<Event::UserNickChange>
{
BahamutIRCdProto ircd_proto;
@@ -501,60 +523,40 @@ class ProtoBahamut : public Module
IRCDMessageSJoin message_sjoin;
IRCDMessageTopic message_topic;
- void AddModes()
- {
- /* Add user modes */
- ModeManager::AddUserMode(new UserModeOperOnly("SERV_ADMIN", 'A'));
- ModeManager::AddUserMode(new UserMode("REGPRIV", 'R'));
- ModeManager::AddUserMode(new UserModeOperOnly("ADMIN", 'a'));
- ModeManager::AddUserMode(new UserMode("INVIS", 'i'));
- ModeManager::AddUserMode(new UserModeOperOnly("OPER", 'o'));
- ModeManager::AddUserMode(new UserModeNoone("REGISTERED", 'r'));
- ModeManager::AddUserMode(new UserModeOperOnly("SNOMASK", 's'));
- ModeManager::AddUserMode(new UserModeOperOnly("WALLOPS", 'w'));
- ModeManager::AddUserMode(new UserMode("DEAF", 'd'));
-
- /* b/e/I */
- ModeManager::AddChannelMode(new ChannelModeList("BAN", 'b'));
-
- /* v/h/o/a/q */
- ModeManager::AddChannelMode(new ChannelModeStatus("VOICE", 'v', '+', 0));
- ModeManager::AddChannelMode(new ChannelModeStatus("OP", 'o', '@', 1));
-
- /* Add channel modes */
- ModeManager::AddChannelMode(new ChannelMode("BLOCKCOLOR", 'c'));
- ModeManager::AddChannelMode(new ChannelMode("INVITE", 'i'));
- ModeManager::AddChannelMode(new ChannelModeFlood('f', false));
- ModeManager::AddChannelMode(new ChannelModeKey('k'));
- ModeManager::AddChannelMode(new ChannelModeParam("LIMIT", 'l', true));
- ModeManager::AddChannelMode(new ChannelMode("MODERATED", 'm'));
- ModeManager::AddChannelMode(new ChannelMode("NOEXTERNAL", 'n'));
- ModeManager::AddChannelMode(new ChannelMode("PRIVATE", 'p'));
- ModeManager::AddChannelMode(new ChannelModeNoone("REGISTERED", 'r'));
- ModeManager::AddChannelMode(new ChannelMode("SECRET", 's'));
- ModeManager::AddChannelMode(new ChannelMode("TOPIC", 't'));
- ModeManager::AddChannelMode(new ChannelMode("REGMODERATED", 'M'));
- ModeManager::AddChannelMode(new ChannelModeOperOnly("OPERONLY", 'O'));
- ModeManager::AddChannelMode(new ChannelMode("REGISTEREDONLY", 'R'));
- }
-
public:
- ProtoBahamut(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, PROTOCOL | VENDOR),
- ircd_proto(this),
- message_away(this), message_capab(this), message_error(this), message_invite(this),
- message_join(this), message_kick(this), message_kill(this), message_motd(this), message_notice(this),
- message_part(this), message_ping(this), message_privmsg(this), message_quit(this),
- message_squit(this), message_stats(this), message_time(this), message_version(this), message_whois(this),
-
- message_burst(this), message_mode(this, "MODE"), message_svsmode(this, "SVSMODE"),
- message_nick(this), message_server(this), message_sjoin(this), message_topic(this)
- {
-
- this->AddModes();
-
- }
-
- void OnUserNickChange(User *u, const Anope::string &) anope_override
+ ProtoBahamut(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, PROTOCOL | VENDOR)
+ , EventHook<Event::UserNickChange>(this)
+ , ircd_proto(this)
+ , message_away(this)
+ , message_capab(this)
+ , message_error(this)
+ , message_invite(this)
+ , message_join(this)
+ , message_kick(this)
+ , message_kill(this)
+ , message_motd(this)
+ , message_notice(this)
+ , message_part(this)
+ , message_ping(this)
+ , message_privmsg(this)
+ , message_quit(this)
+ , message_squit(this)
+ , message_stats(this)
+ , message_time(this)
+ , message_version(this)
+ , message_whois(this)
+
+ , message_burst(this)
+ , message_mode(this, "MODE")
+ , message_svsmode(this, "SVSMODE")
+ , message_nick(this)
+ , message_server(this)
+ , message_sjoin(this)
+ , message_topic(this)
+ {
+ }
+
+ void OnUserNickChange(User *u, const Anope::string &) override
{
u->RemoveModeInternal(Me, ModeManager::FindUserModeByName("REGISTERED"));
IRCD->SendLogout(u);
diff --git a/modules/protocol/charybdis.cpp b/modules/protocol/charybdis.cpp
index c381ef124..c69fbaf5f 100644
--- a/modules/protocol/charybdis.cpp
+++ b/modules/protocol/charybdis.cpp
@@ -1,28 +1,39 @@
-/* Charybdis IRCD functions
+/*
+ * Anope IRC Services
*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2006-2016 Anope Team <team@anope.org>
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
*
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
+/* Dependencies: anope_protocol.ratbox */
+
#include "module.h"
-#include "modules/cs_mode.h"
#include "modules/sasl.h"
+#include "modules/protocol/hybrid.h"
+#include "modules/protocol/charybdis.h"
+#include "modules/protocol/ratbox.h"
+#include "modules/chanserv/mode.h"
static Anope::string UplinkSID;
-static ServiceReference<IRCDProto> ratbox("IRCDProto", "ratbox");
-
class ChannelModeLargeBan : public ChannelMode
{
public:
ChannelModeLargeBan(const Anope::string &mname, char modeChar) : ChannelMode(mname, modeChar) { }
- bool CanSet(User *u) const anope_override
+ bool CanSet(User *u) const override
{
return u && u->HasMode("OPER");
}
@@ -31,8 +42,10 @@ class ChannelModeLargeBan : public ChannelMode
class CharybdisProto : public IRCDProto
{
+ ServiceReference<IRCDProto> ratbox; // XXX
public:
CharybdisProto(Module *creator) : IRCDProto(creator, "Charybdis 3.4+")
+ , ratbox("ratbox")
{
DefaultPseudoclientModes = "+oiS";
CanCertFP = true;
@@ -46,43 +59,32 @@ class CharybdisProto : public IRCDProto
MaxModes = 4;
}
- void SendSVSKillInternal(const MessageSource &source, User *targ, const Anope::string &reason) anope_override { ratbox->SendSVSKillInternal(source, targ, reason); }
- void SendGlobalNotice(BotInfo *bi, const Server *dest, const Anope::string &msg) anope_override { ratbox->SendGlobalNotice(bi, dest, msg); }
- void SendGlobalPrivmsg(BotInfo *bi, const Server *dest, const Anope::string &msg) anope_override { ratbox->SendGlobalPrivmsg(bi, dest, msg); }
- void SendGlobopsInternal(const MessageSource &source, const Anope::string &buf) anope_override { ratbox->SendGlobopsInternal(source, buf); }
- void SendSGLine(User *u, const XLine *x) anope_override { ratbox->SendSGLine(u, x); }
- void SendSGLineDel(const XLine *x) anope_override { ratbox->SendSGLineDel(x); }
- void SendAkill(User *u, XLine *x) anope_override { ratbox->SendAkill(u, x); }
- void SendAkillDel(const XLine *x) anope_override { ratbox->SendAkillDel(x); }
- void SendSQLineDel(const XLine *x) anope_override { ratbox->SendSQLineDel(x); }
- void SendJoin(User *user, Channel *c, const ChannelStatus *status) anope_override { ratbox->SendJoin(user, c, status); }
- void SendServer(const Server *server) anope_override { ratbox->SendServer(server); }
- void SendChannel(Channel *c) anope_override { ratbox->SendChannel(c); }
- void SendTopic(const MessageSource &source, Channel *c) anope_override { ratbox->SendTopic(source, c); }
- bool IsIdentValid(const Anope::string &ident) anope_override { return ratbox->IsIdentValid(ident); }
- void SendLogin(User *u, NickAlias *na) anope_override { ratbox->SendLogin(u, na); }
- void SendLogout(User *u) anope_override { ratbox->SendLogout(u); }
-
- void SendSASLMechanisms(std::vector<Anope::string> &mechanisms) anope_override
+ void SendSVSKillInternal(const MessageSource &source, User *targ, const Anope::string &reason) override { ratbox->SendSVSKillInternal(source, targ, reason); }
+ void SendGlobalNotice(ServiceBot *bi, const Server *dest, const Anope::string &msg) override { ratbox->SendGlobalNotice(bi, dest, msg); }
+ void SendGlobalPrivmsg(ServiceBot *bi, const Server *dest, const Anope::string &msg) override { ratbox->SendGlobalPrivmsg(bi, dest, msg); }
+ void SendGlobopsInternal(const MessageSource &source, const Anope::string &buf) override { ratbox->SendGlobopsInternal(source, buf); }
+ void SendSGLine(User *u, XLine *x) override { ratbox->SendSGLine(u, x); }
+ void SendSGLineDel(XLine *x) override { ratbox->SendSGLineDel(x); }
+ void SendAkill(User *u, XLine *x) override { ratbox->SendAkill(u, x); }
+ void SendAkillDel(XLine *x) override { ratbox->SendAkillDel(x); }
+ void SendSQLineDel(XLine *x) override { ratbox->SendSQLineDel(x); }
+ void SendJoin(User *user, Channel *c, const ChannelStatus *status) override { ratbox->SendJoin(user, c, status); }
+ void SendServer(const Server *server) override { ratbox->SendServer(server); }
+ void SendChannel(Channel *c) override { ratbox->SendChannel(c); }
+ void SendTopic(const MessageSource &source, Channel *c) override { ratbox->SendTopic(source, c); }
+ bool IsIdentValid(const Anope::string &ident) override { return ratbox->IsIdentValid(ident); }
+ void SendLogin(User *u, NickServ::Nick *na) override { ratbox->SendLogin(u, na); }
+ void SendLogout(User *u) override { ratbox->SendLogout(u); }
+
+ void SendSQLine(User *, XLine *x) override
{
- Anope::string mechlist;
-
- for (unsigned i = 0; i < mechanisms.size(); ++i)
- {
- mechlist += "," + mechanisms[i];
- }
-
- UplinkSocket::Message(Me) << "ENCAP * MECHLIST :" << (mechanisms.empty() ? "" : mechlist.substr(1));
+ Uplink::Send(Me, "RESV", "*", x->GetMask(), x->GetReason());
}
- void SendSQLine(User *, const XLine *x) anope_override
+ void SendConnect() override
{
- UplinkSocket::Message(Me) << "RESV * " << x->mask << " :" << x->GetReason();
- }
+ Uplink::Send("PASS", Config->Uplinks[Anope::CurrentUplink].password, "TS", 6, Me->GetSID());
- void SendConnect() anope_override
- {
- UplinkSocket::Message() << "PASS " << Config->Uplinks[Anope::CurrentUplink].password << " TS 6 :" << Me->GetSID();
/*
* Received: CAPAB :BAN CHW CLUSTER ENCAP EOPMOD EUID EX IE KLN
* KNOCK MLOCK QS RSFNC SAVE SERVICES TB UNKLN
@@ -106,7 +108,7 @@ class CharybdisProto : public IRCDProto
* UNKLN - Can do UNKLINE (encap only)
* QS - Can handle quit storm removal
*/
- UplinkSocket::Message() << "CAPAB :BAN CHW CLUSTER ENCAP EOPMOD EUID EX IE KLN KNOCK MLOCK QS RSFNC SERVICES TB UNKLN";
+ Uplink::Send("CAPAB", "BAN CHW CLUSTER ENCAP EOPMOD EUID EX IE KLN KNOCK MLOCK QS RSFNC SERVICES TB UNKLN");
/* Make myself known to myself in the serverlist */
SendServer(Me);
@@ -118,160 +120,141 @@ class CharybdisProto : public IRCDProto
* arg[2] = '0'
* arg[3] = server's idea of UTC time
*/
- UplinkSocket::Message() << "SVINFO 6 6 0 :" << Anope::CurTime;
+ Uplink::Send("SVINFO", 6, 6, Anope::CurTime);
}
- void SendClientIntroduction(User *u) anope_override
+ void SendClientIntroduction(User *u) override
{
Anope::string modes = "+" + u->GetModes();
- UplinkSocket::Message(Me) << "EUID " << u->nick << " 1 " << u->timestamp << " " << modes << " " << u->GetIdent() << " " << u->host << " 0 " << u->GetUID() << " * * :" << u->realname;
+ Uplink::Send(Me, "EUID", u->nick, 1, u->timestamp, modes, u->GetIdent(), u->host, 0, u->GetUID(), "*", "*", u->realname);
}
- void SendForceNickChange(User *u, const Anope::string &newnick, time_t when) anope_override
+ void SendForceNickChange(User *u, const Anope::string &newnick, time_t when) override
{
- UplinkSocket::Message(Me) << "ENCAP " << u->server->GetName() << " RSFNC " << u->GetUID()
- << " " << newnick << " " << when << " " << u->timestamp;
+ Uplink::Send(Me, "ENCAP", u->server->GetName(), "RSFNC", u->GetUID(),
+ newnick, when, u->timestamp);
}
- void SendSVSHold(const Anope::string &nick, time_t delay) anope_override
+ void SendSVSHold(const Anope::string &nick, time_t delay) override
{
- UplinkSocket::Message(Me) << "ENCAP * NICKDELAY " << delay << " " << nick;
+ Uplink::Send(Me, "ENCAP", "*", "NICKDELAY", delay, nick);
}
- void SendSVSHoldDel(const Anope::string &nick) anope_override
+ void SendSVSHoldDel(const Anope::string &nick) override
{
- UplinkSocket::Message(Me) << "ENCAP * NICKDELAY 0 " << nick;
+ Uplink::Send(Me, "ENCAP", "*", "NICKDELAY", 0, nick);
}
- void SendVhost(User *u, const Anope::string &ident, const Anope::string &host) anope_override
+ void SendVhost(User *u, const Anope::string &ident, const Anope::string &host) override
{
- UplinkSocket::Message(Me) << "ENCAP * CHGHOST " << u->GetUID() << " :" << host;
+ Uplink::Send(Me, "ENCAP", "*", "CHGHOST", u->GetUID(), host);
}
- void SendVhostDel(User *u) anope_override
+ void SendVhostDel(User *u) override
{
this->SendVhost(u, "", u->host);
}
- void SendSASLMessage(const SASL::Message &message) anope_override
+ void SendSASLMechanisms(std::vector<Anope::string> &mechanisms) override
+ {
+ Anope::string mechlist;
+
+ for (unsigned i = 0; i < mechanisms.size(); ++i)
+ {
+ mechlist += "," + mechanisms[i];
+ }
+
+ Uplink::Send(Me, "ENCAP", "*", "MECHLIST", mechlist.empty() ? "" : mechlist.substr(1));
+ }
+
+ void SendSASLMessage(const SASL::Message &message) override
{
Server *s = Server::Find(message.target.substr(0, 3));
- UplinkSocket::Message(Me) << "ENCAP " << (s ? s->GetName() : message.target.substr(0, 3)) << " SASL " << message.source << " " << message.target << " " << message.type << " " << message.data << (message.ext.empty() ? "" : (" " + message.ext));
+ Uplink::Send(Me, "ENCAP", s ? s->GetName() : message.target.substr(0, 3), "SASL", message.source, message.target, message.type, message.data, message.ext.empty() ? "" : message.ext);
}
- void SendSVSLogin(const Anope::string &uid, const Anope::string &acc, const Anope::string &vident, const Anope::string &vhost) anope_override
+ void SendSVSLogin(const Anope::string &uid, const Anope::string &acc, const Anope::string &vident, const Anope::string &vhost) override
{
Server *s = Server::Find(uid.substr(0, 3));
- UplinkSocket::Message(Me) << "ENCAP " << (s ? s->GetName() : uid.substr(0, 3)) << " SVSLOGIN " << uid << " * " << (!vident.empty() ? vident : '*') << " " << (!vhost.empty() ? vhost : '*') << " " << acc;
+ Uplink::Send(Me, "ENCAP", s ? s->GetName() : uid.substr(0, 3), "SVSLOGIN", uid, "*", vident.empty() ? "*" : vident, vhost.empty() ? "*" : vhost, acc);
}
};
-struct IRCDMessageEncap : IRCDMessage
+void charybdis::Encap::Run(MessageSource &source, const std::vector<Anope::string> &params)
{
- IRCDMessageEncap(Module *creator) : IRCDMessage(creator, "ENCAP", 3) { SetFlag(IRCDMESSAGE_SOFT_LIMIT);}
+ User *u = source.GetUser();
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ // In a burst, states that the source user is logged in as the account.
+ if (params[1] == "LOGIN" || params[1] == "SU")
{
- User *u = source.GetUser();
-
- // In a burst, states that the source user is logged in as the account.
- if (params[1] == "LOGIN" || params[1] == "SU")
- {
- NickCore *nc = NickCore::Find(params[2]);
- if (!nc)
- return;
- u->Login(nc);
- }
- // Received: :42XAAAAAE ENCAP * CERTFP :3f122a9cc7811dbad3566bf2cec3009007c0868f
- if (params[1] == "CERTFP")
- {
- u->fingerprint = params[2];
- FOREACH_MOD(OnFingerprint, (u));
- }
- /*
- * Received: :42X ENCAP * SASL 42XAAAAAH * S PLAIN
- * Received: :42X ENCAP * SASL 42XAAAAAC * D A
- *
- * Part of a SASL authentication exchange. The mode is 'C' to send some data
- * (base64 encoded), or 'S' to end the exchange (data indicates type of
- * termination: 'A' for abort, 'F' for authentication failure, 'S' for
- * authentication success).
- *
- * Charybdis only accepts messages from SASL agents; these must have umode +S
- */
- if (params[1] == "SASL" && SASL::sasl && params.size() >= 6)
- {
- SASL::Message m;
- m.source = params[2];
- m.target = params[3];
- m.type = params[4];
- m.data = params[5];
- m.ext = params.size() > 6 ? params[6] : "";
-
- SASL::sasl->ProcessMessage(m);
- }
+ NickServ::Account *nc = NickServ::FindAccount(params[2]);
+ if (!nc)
+ return;
+ u->Login(nc);
+ }
+ // Received: :42XAAAAAE ENCAP * CERTFP :3f122a9cc7811dbad3566bf2cec3009007c0868f
+ if (params[1] == "CERTFP")
+ {
+ u->fingerprint = params[2];
+ EventManager::Get()->Dispatch(&Event::Fingerprint::OnFingerprint, u);
}
-};
-
-struct IRCDMessageEUID : IRCDMessage
-{
- IRCDMessageEUID(Module *creator) : IRCDMessage(creator, "EUID", 11) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
-
/*
- * :42X EUID DukePyrolator 1 1353240577 +Zi ~jens erft-5d80b00b.pool.mediaWays.net 93.128.176.11 42XAAAAAD * * :jens
- * :<SID> EUID <NICK> <HOPS> <TS> +<UMODE> <USERNAME> <VHOST> <IP> <UID> <REALHOST> <ACCOUNT> :<GECOS>
- * 0 1 2 3 4 5 6 7 8 9 10
+ * Received: :42X ENCAP * SASL 42XAAAAAH * S PLAIN
+ * Received: :42X ENCAP * SASL 42XAAAAAC * D A
*
- * Introduces a user. The hostname field is now always the visible host.
- * The realhost field is * if the real host is equal to the visible host.
- * The account field is * if the login is not set.
- * Note that even if both new fields are *, an EUID command still carries more
- * information than a UID command (namely that real host is visible host and the
- * user is not logged in with services). Hence a NICK or UID command received
- * from a remote server should not be sent in EUID form to other servers.
+ * Part of a SASL authentication exchange. The mode is 'C' to send some data
+ * (base64 encoded), or 'S' to end the exchange (data indicates type of
+ * termination: 'A' for abort, 'F' for authentication failure, 'S' for
+ * authentication success).
+ *
+ * Charybdis only accepts messages from SASL agents; these must have umode +S
*/
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ if (params[1] == "SASL" && sasl && params.size() >= 6)
{
- NickAlias *na = NULL;
- if (params[9] != "*")
- na = NickAlias::Find(params[9]);
-
- User::OnIntroduce(params[0], params[4], (params[8] != "*" ? params[8] : params[5]), params[5], params[6], source.GetServer(), params[10], params[2].is_pos_number_only() ? convertTo<time_t>(params[2]) : Anope::CurTime, params[3], params[7], na ? *na->nc : NULL);
+ SASL::Message m;
+ m.source = params[2];
+ m.target = params[3];
+ m.type = params[4];
+ m.data = params[5];
+ m.ext = params.size() > 6 ? params[6] : "";
+
+ sasl->ProcessMessage(m);
}
-};
+}
-// we can't use this function from ratbox because we set a local variable here
-struct IRCDMessageServer : IRCDMessage
+void charybdis::EUID::Run(MessageSource &source, const std::vector<Anope::string> &params)
{
- IRCDMessageServer(Module *creator) : IRCDMessage(creator, "SERVER", 3) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
+ NickServ::Nick *na = NULL;
+ if (params[9] != "*")
+ na = NickServ::FindNick(params[9]);
- // SERVER dev.anope.de 1 :charybdis test server
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- // Servers other then our immediate uplink are introduced via SID
- if (params[1] != "1")
- return;
- new Server(source.GetServer() == NULL ? Me : source.GetServer(), params[0], 1, params[2], UplinkSID);
- IRCD->SendPing(Me->GetName(), params[0]);
- }
-};
+ User::OnIntroduce(params[0], params[4], (params[8] != "*" ? params[8] : params[5]), params[5], params[6], source.GetServer(), params[10], params[2].is_pos_number_only() ? convertTo<time_t>(params[2]) : Anope::CurTime, params[3], params[7], na ? na->GetAccount() : NULL);
+}
// we can't use this function from ratbox because we set a local variable here
-struct IRCDMessagePass : IRCDMessage
+// SERVER dev.anope.de 1 :charybdis test server
+void charybdis::Server::Run(MessageSource &source, const std::vector<Anope::string> &params)
{
- IRCDMessagePass(Module *creator) : IRCDMessage(creator, "PASS", 4) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
+ // Servers other then our immediate uplink are introduced via SID
+ if (params[1] != "1")
+ return;
+ new ::Server(source.GetServer() == NULL ? Me : source.GetServer(), params[0], 1, params[2], UplinkSID);
+ IRCD->SendPing(Me->GetName(), params[0]);
+}
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- // UplinkSID is used in IRCDMessageServer
- UplinkSID = params[3];
- }
-};
+// we can't use this function from ratbox because we set a local variable here
+void charybdis::Pass::Run(MessageSource &source, const std::vector<Anope::string> &params)
+{
+ // UplinkSID is used in server handler
+ UplinkSID = params[3];
+}
class ProtoCharybdis : public Module
+ , public EventHook<Event::ChannelSync>
+ , public EventHook<Event::MLockEvents>
{
- Module *m_ratbox;
+ ServiceReference<ModeLocks> mlocks;
CharybdisProto ircd_proto;
@@ -296,125 +279,115 @@ class ProtoCharybdis : public Module
Message::Version message_version;
Message::Whois message_whois;
- /* Ratbox Message Handlers */
- ServiceAlias message_bmask, message_join, message_nick, message_pong, message_sid, message_sjoin,
- message_tb, message_tmode, message_uid;
-
/* Our message handlers */
- IRCDMessageEncap message_encap;
- IRCDMessageEUID message_euid;
- IRCDMessagePass message_pass;
- IRCDMessageServer message_server;
+ hybrid::BMask message_bmask;
+ charybdis::Encap message_encap;
+ charybdis::EUID message_euid;
+ ratbox::Join message_join;
+ hybrid::Nick message_nick;
+ charybdis::Pass message_pass;
+ hybrid::Pong message_pong;
+ charybdis::Server message_server;
+ hybrid::SID message_sid;
+ hybrid::SJoin message_sjoin;
+ ratbox::TB message_tb;
+ hybrid::TMode message_tmode;
+ ratbox::UID message_uid;
bool use_server_side_mlock;
- void AddModes()
- {
- /* Add user modes */
- ModeManager::AddUserMode(new UserMode("NOFORWARD", 'Q'));
- ModeManager::AddUserMode(new UserMode("REGPRIV", 'R'));
- ModeManager::AddUserMode(new UserModeOperOnly("OPERWALLS", 'z'));
- ModeManager::AddUserMode(new UserModeNoone("SSL", 'Z'));
-
- /* b/e/I */
- ModeManager::AddChannelMode(new ChannelModeList("QUIET", 'q'));
-
- /* Add channel modes */
- ModeManager::AddChannelMode(new ChannelMode("BLOCKCOLOR", 'c'));
- ModeManager::AddChannelMode(new ChannelMode("NOCTCP", 'C'));
- ModeManager::AddChannelMode(new ChannelModeParam("REDIRECT", 'f'));
- ModeManager::AddChannelMode(new ChannelMode("ALLOWFORWARD", 'F'));
- ModeManager::AddChannelMode(new ChannelMode("ALLINVITE", 'g'));
- ModeManager::AddChannelMode(new ChannelModeParam("JOINFLOOD", 'j'));
- ModeManager::AddChannelMode(new ChannelModeLargeBan("LBAN", 'L'));
- ModeManager::AddChannelMode(new ChannelMode("PERM", 'P'));
- ModeManager::AddChannelMode(new ChannelMode("NOFORWARD", 'Q'));
- ModeManager::AddChannelMode(new ChannelMode("OPMODERATED", 'z'));
- }
-
public:
- ProtoCharybdis(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, PROTOCOL | VENDOR),
- ircd_proto(this),
- message_away(this), message_capab(this), message_error(this), message_invite(this), message_kick(this),
- message_kill(this), message_mode(this), message_motd(this), message_notice(this), message_part(this),
- message_ping(this), message_privmsg(this), message_quit(this), message_squit(this), message_stats(this),
- message_time(this), message_topic(this), message_version(this), message_whois(this),
-
- message_bmask("IRCDMessage", "charybdis/bmask", "ratbox/bmask"),
- message_join("IRCDMessage", "charybdis/join", "ratbox/join"),
- message_nick("IRCDMessage", "charybdis/nick", "ratbox/nick"),
- message_pong("IRCDMessage", "charybdis/pong", "ratbox/pong"),
- message_sid("IRCDMessage", "charybdis/sid", "ratbox/sid"),
- message_sjoin("IRCDMessage", "charybdis/sjoin", "ratbox/sjoin"),
- message_tb("IRCDMessage", "charybdis/tb", "ratbox/tb"),
- message_tmode("IRCDMessage", "charybdis/tmode", "ratbox/tmode"),
- message_uid("IRCDMessage", "charybdis/uid", "ratbox/uid"),
-
- message_encap(this), message_euid(this), message_pass(this), message_server(this)
-
- {
-
-
- if (ModuleManager::LoadModule("ratbox", User::Find(creator)) != MOD_ERR_OK)
- throw ModuleException("Unable to load ratbox");
- m_ratbox = ModuleManager::FindModule("ratbox");
- if (!m_ratbox)
- throw ModuleException("Unable to find ratbox");
- if (!ratbox)
- throw ModuleException("No protocol interface for ratbox");
-
- this->AddModes();
- }
-
- ~ProtoCharybdis()
+ ProtoCharybdis(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, PROTOCOL | VENDOR)
+ , EventHook<Event::ChannelSync>(this)
+ , EventHook<Event::MLockEvents>(this)
+ , ircd_proto(this)
+ , message_away(this)
+ , message_capab(this)
+ , message_error(this)
+ , message_invite(this)
+ , message_kick(this)
+ , message_kill(this)
+ , message_mode(this)
+ , message_motd(this)
+ , message_notice(this)
+ , message_part(this)
+ , message_ping(this)
+ , message_privmsg(this)
+ , message_quit(this)
+ , message_squit(this)
+ , message_stats(this)
+ , message_time(this)
+ , message_topic(this)
+ , message_version(this)
+ , message_whois(this)
+
+ , message_bmask(this)
+ , message_encap(this)
+ , message_euid(this)
+ , message_join(this)
+ , message_nick(this)
+ , message_pass(this)
+ , message_pong(this)
+ , message_server(this)
+ , message_sid(this)
+ , message_sjoin(this)
+ , message_tb(this)
+ , message_tmode(this)
+ , message_uid(this)
{
- m_ratbox = ModuleManager::FindModule("ratbox");
- ModuleManager::UnloadModule(m_ratbox, NULL);
}
- void OnReload(Configuration::Conf *conf) anope_override
+ void OnReload(Configuration::Conf *conf) override
{
use_server_side_mlock = conf->GetModule(this)->Get<bool>("use_server_side_mlock");
}
- void OnChannelSync(Channel *c) anope_override
+ void OnChannelSync(Channel *c) override
{
- if (!c->ci)
+ if (!c->ci || !mlocks)
return;
- ModeLocks *modelocks = c->ci->GetExt<ModeLocks>("modelocks");
- if (use_server_side_mlock && modelocks && Servers::Capab.count("MLOCK") > 0)
+ if (use_server_side_mlock && Servers::Capab.count("MLOCK") > 0)
{
- Anope::string modes = modelocks->GetMLockAsString(false).replace_all_cs("+", "").replace_all_cs("-", "");
- UplinkSocket::Message(Me) << "MLOCK " << static_cast<long>(c->creation_time) << " " << c->ci->name << " " << modes;
+ Anope::string modes = mlocks->GetMLockAsString(c->ci, false).replace_all_cs("+", "").replace_all_cs("-", "");
+ Uplink::Send(Me, "MLOCK", c->creation_time, c->ci->GetName(), modes);
}
}
- EventReturn OnMLock(ChannelInfo *ci, ModeLock *lock) anope_override
+ EventReturn OnMLock(ChanServ::Channel *ci, ModeLock *lock) override
{
- ModeLocks *modelocks = ci->GetExt<ModeLocks>("modelocks");
- ChannelMode *cm = ModeManager::FindChannelModeByName(lock->name);
- if (use_server_side_mlock && cm && ci->c && modelocks && (cm->type == MODE_REGULAR || cm->type == MODE_PARAM) && Servers::Capab.count("MLOCK") > 0)
+ if (!mlocks)
+ return EVENT_CONTINUE;
+
+ ChannelMode *cm = ModeManager::FindChannelModeByName(lock->GetName());
+ if (use_server_side_mlock && cm && ci->c && (cm->type == MODE_REGULAR || cm->type == MODE_PARAM) && Servers::Capab.count("MLOCK") > 0)
{
- Anope::string modes = modelocks->GetMLockAsString(false).replace_all_cs("+", "").replace_all_cs("-", "") + cm->mchar;
- UplinkSocket::Message(Me) << "MLOCK " << static_cast<long>(ci->c->creation_time) << " " << ci->name << " " << modes;
+ Anope::string modes = mlocks->GetMLockAsString(ci, false).replace_all_cs("+", "").replace_all_cs("-", "") + cm->mchar;
+ Uplink::Send(Me, "MLOCK", ci->c->creation_time, ci->GetName(), modes);
}
return EVENT_CONTINUE;
}
- EventReturn OnUnMLock(ChannelInfo *ci, ModeLock *lock) anope_override
+ EventReturn OnUnMLock(ChanServ::Channel *ci, ModeLock *lock) override
{
- ModeLocks *modelocks = ci->GetExt<ModeLocks>("modelocks");
- ChannelMode *cm = ModeManager::FindChannelModeByName(lock->name);
- if (use_server_side_mlock && cm && modelocks && ci->c && (cm->type == MODE_REGULAR || cm->type == MODE_PARAM) && Servers::Capab.count("MLOCK") > 0)
+ if (!mlocks)
+ return EVENT_CONTINUE;
+
+ ChannelMode *cm = ModeManager::FindChannelModeByName(lock->GetName());
+ if (use_server_side_mlock && cm && ci->c && (cm->type == MODE_REGULAR || cm->type == MODE_PARAM) && Servers::Capab.count("MLOCK") > 0)
{
- Anope::string modes = modelocks->GetMLockAsString(false).replace_all_cs("+", "").replace_all_cs("-", "").replace_all_cs(cm->mchar, "");
- UplinkSocket::Message(Me) << "MLOCK " << static_cast<long>(ci->c->creation_time) << " " << ci->name << " " << modes;
+ Anope::string modes = mlocks->GetMLockAsString(ci, false).replace_all_cs("+", "").replace_all_cs("-", "").replace_all_cs(cm->mchar, "");
+ Uplink::Send(Me, "MLOCK", ci->c->creation_time, ci->GetName(), modes);
}
return EVENT_CONTINUE;
}
};
+template<> void ModuleInfo<ProtoCharybdis>(ModuleDef *def)
+{
+ def->Depends("ratbox");
+}
+
MODULE_INIT(ProtoCharybdis)
diff --git a/modules/protocol/hybrid.cpp b/modules/protocol/hybrid.cpp
index 2db72717e..c1d85bcf8 100644
--- a/modules/protocol/hybrid.cpp
+++ b/modules/protocol/hybrid.cpp
@@ -1,35 +1,51 @@
-/* ircd-hybrid-8 protocol module
+/*
+ * Anope IRC Services
*
- * (C) 2003-2016 Anope Team <team@anope.org>
- * (C) 2012-2016 ircd-hybrid development team
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
+ * Copyright (C) 2012-2016 ircd-hybrid development team
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
*
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "module.h"
+#include "modules/protocol/hybrid.h"
static Anope::string UplinkSID;
class HybridProto : public IRCDProto
{
- BotInfo *FindIntroduced()
+ ServiceBot *FindIntroduced()
{
- BotInfo *bi = Config->GetClient("OperServ");
-
+ ServiceBot *bi = Config->GetClient("OperServ");
if (bi && bi->introduced)
return bi;
- for (botinfo_map::iterator it = BotListByNick->begin(), it_end = BotListByNick->end(); it != it_end; ++it)
- if (it->second->introduced)
- return it->second;
+ for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it)
+ {
+ User *u = it->second;
+ if (u->type == UserType::BOT)
+ {
+ bi = anope_dynamic_static_cast<ServiceBot *>(u);
+ if (bi->introduced)
+ return bi;
+ }
+ }
return NULL;
}
- void SendSVSKillInternal(const MessageSource &source, User *u, const Anope::string &buf) anope_override
+ void SendSVSKillInternal(const MessageSource &source, User *u, const Anope::string &buf) override
{
IRCDProto::SendSVSKillInternal(source, u, buf);
u->KillInternal(source, buf);
@@ -52,73 +68,72 @@ class HybridProto : public IRCDProto
MaxModes = 6;
}
- void SendInvite(const MessageSource &source, const Channel *c, User *u) anope_override
+ void SendInvite(const MessageSource &source, const Channel *c, User *u) override
{
- UplinkSocket::Message(source) << "INVITE " << u->GetUID() << " " << c->name << " " << c->creation_time;
+ Uplink::Send(source, "INVITE", u->GetUID(), c->name, c->creation_time);
}
- void SendGlobalNotice(BotInfo *bi, const Server *dest, const Anope::string &msg) anope_override
+ void SendGlobalNotice(ServiceBot *bi, const Server *dest, const Anope::string &msg) override
{
- UplinkSocket::Message(bi) << "NOTICE $$" << dest->GetName() << " :" << msg;
+ Uplink::Send(bi, "NOTICE", "$$" + dest->GetName(), msg);
}
- void SendGlobalPrivmsg(BotInfo *bi, const Server *dest, const Anope::string &msg) anope_override
+ void SendGlobalPrivmsg(ServiceBot *bi, const Server *dest, const Anope::string &msg) override
{
- UplinkSocket::Message(bi) << "PRIVMSG $$" << dest->GetName() << " :" << msg;
+ Uplink::Send(bi, "PRIVMSG", "$$" + dest->GetName(), msg);
}
- void SendSQLine(User *, const XLine *x) anope_override
+ void SendSQLine(User *, XLine *x) override
{
- UplinkSocket::Message(FindIntroduced()) << "RESV * " << (x->expires ? x->expires - Anope::CurTime : 0) << " " << x->mask << " :" << x->reason;
+ Uplink::Send(FindIntroduced(), "RESV", "*", x->GetExpires() ? x->GetExpires() - Anope::CurTime : 0, x->GetMask(), x->GetReason());
}
- void SendSGLineDel(const XLine *x) anope_override
+ void SendSGLineDel(XLine *x) override
{
- UplinkSocket::Message(Config->GetClient("OperServ")) << "UNXLINE * " << x->mask;
+ Uplink::Send(Config->GetClient("OperServ"), "UNXLINE", "*", x->GetMask());
}
- void SendSGLine(User *, const XLine *x) anope_override
+ void SendSGLine(User *, XLine *x) override
{
- UplinkSocket::Message(Config->GetClient("OperServ")) << "XLINE * " << x->mask << " " << (x->expires ? x->expires - Anope::CurTime : 0) << " :" << x->GetReason();
+ Uplink::Send(Config->GetClient("OperServ"), "XLINE", "*", x->GetMask(), x->GetExpires() ? x->GetExpires() - Anope::CurTime : 0, x->GetReason());
}
- void SendSZLineDel(const XLine *x) anope_override
+ void SendSZLineDel(XLine *x) override
{
- UplinkSocket::Message(Config->GetClient("OperServ")) << "UNDLINE * " << x->GetHost();
+ Uplink::Send(Config->GetClient("OperServ"), "UNDLINE", "*", x->GetHost());
}
- void SendSZLine(User *, const XLine *x) anope_override
+ void SendSZLine(User *, XLine *x) override
{
/* Calculate the time left before this would expire, capping it at 2 days */
- time_t timeleft = x->expires - Anope::CurTime;
+ time_t timeleft = x->GetExpires() - Anope::CurTime;
- if (timeleft > 172800 || !x->expires)
+ if (timeleft > 172800 || !x->GetExpires())
timeleft = 172800;
- UplinkSocket::Message(Config->GetClient("OperServ")) << "DLINE * " << timeleft << " " << x->GetHost() << " :" << x->GetReason();
+ Uplink::Send(Config->GetClient("OperServ"), "DLINE", "*", timeleft, x->GetHost(), x->GetReason());
}
- void SendAkillDel(const XLine *x) anope_override
+ void SendAkillDel(XLine *x) override
{
if (x->IsRegex() || x->HasNickOrReal())
return;
- UplinkSocket::Message(Config->GetClient("OperServ")) << "UNKLINE * " << x->GetUser() << " " << x->GetHost();
+ Uplink::Send(Config->GetClient("OperServ"), "UNKLINE", "*", x->GetUser(), x->GetHost());
}
- void SendSQLineDel(const XLine *x) anope_override
+ void SendSQLineDel(XLine *x) override
{
- UplinkSocket::Message(Config->GetClient("OperServ")) << "UNRESV * " << x->mask;
+ Uplink::Send(Config->GetClient("OperServ"), "UNRESV", "*", x->GetMask());
}
- void SendJoin(User *u, Channel *c, const ChannelStatus *status) anope_override
+ void SendJoin(User *u, Channel *c, const ChannelStatus *status) override
{
/*
* Note that we must send our modes with the SJOIN and can not add them to the
* mode stacker because ircd-hybrid does not allow *any* client to op itself
*/
- UplinkSocket::Message() << "SJOIN " << c->creation_time << " " << c->name << " +" << c->GetModes(true, true) << " :"
- << (status != NULL ? status->BuildModePrefixList() : "") << u->GetUID();
+ Uplink::Send("SJOIN", c->creation_time, c->name, "+" + c->GetModes(true, true), (status != NULL ? status->BuildModePrefixList() : "") + u->GetUID());
/* And update our internal status for this user since this is not going through our mode handling system */
if (status)
@@ -130,7 +145,7 @@ class HybridProto : public IRCDProto
}
}
- void SendAkill(User *u, XLine *x) anope_override
+ void SendAkill(User *u, XLine *x) override
{
if (x->IsRegex() || x->HasNickOrReal())
{
@@ -141,50 +156,55 @@ class HybridProto : public IRCDProto
* Find users that match and ban them.
*/
for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it)
- if (x->manager->Check(it->second, x))
+ if (x->GetManager()->Check(it->second, x))
this->SendAkill(it->second, x);
return;
}
- const XLine *old = x;
+ XLine *old = x;
- if (old->manager->HasEntry("*@" + u->host))
+ if (old->GetManager()->HasEntry("*@" + u->host))
return;
/* We can't akill x as it has a nick and/or realname included, so create a new akill for *@host */
- XLine *xline = new XLine("*@" + u->host, old->by, old->expires, old->reason, old->id);
-
- old->manager->AddXLine(xline);
- x = xline;
-
- Log(Config->GetClient("OperServ"), "akill") << "AKILL: Added an akill for " << x->mask << " because " << u->GetMask() << "#"
- << u->realname << " matches " << old->mask;
+ XLine *xl = Serialize::New<XLine *>();
+ xl->SetMask("*@" + u->host);
+ xl->SetBy(old->GetBy());
+ xl->SetExpires(old->GetExpires());
+ xl->SetReason(old->GetReason());
+ xl->SetID(old->GetID());
+
+ old->GetManager()->AddXLine(xl);
+ x = xl;
+
+ Log(Config->GetClient("OperServ"), "akill") << "AKILL: Added an akill for " << x->GetMask() << " because " << u->GetMask() << "#"
+ << u->realname << " matches " << old->GetMask();
}
/* Calculate the time left before this would expire, capping it at 2 days */
- time_t timeleft = x->expires - Anope::CurTime;
+ time_t timeleft = x->GetExpires() - Anope::CurTime;
- if (timeleft > 172800 || !x->expires)
+ if (timeleft > 172800 || !x->GetExpires())
timeleft = 172800;
- UplinkSocket::Message(Config->GetClient("OperServ")) << "KLINE * " << timeleft << " " << x->GetUser() << " " << x->GetHost() << " :" << x->GetReason();
+ Uplink::Send(Config->GetClient("OperServ"), "KLINE", timeleft, x->GetUser(), x->GetHost(), x->GetReason());
}
- void SendServer(const Server *server) anope_override
+ void SendServer(const Server *server) override
{
if (server == Me)
- UplinkSocket::Message() << "SERVER " << server->GetName() << " " << server->GetHops() + 1 << " :" << server->GetDescription();
+ Uplink::Send("SERVER", server->GetName(), server->GetHops() + 1, server->GetDescription());
else
- UplinkSocket::Message(Me) << "SID " << server->GetName() << " " << server->GetHops() + 1 << " " << server->GetSID() << " :" << server->GetDescription();
+ Uplink::Send(Me, "SID", server->GetName(), server->GetHops() + 1, server->GetSID(), server->GetDescription());
}
- void SendConnect() anope_override
+ void SendConnect() override
{
- UplinkSocket::Message() << "PASS " << Config->Uplinks[Anope::CurrentUplink].password << " TS 6 :" << Me->GetSID();
+ Uplink::Send("PASS", Config->Uplinks[Anope::CurrentUplink].password, "TS", 6, Me->GetSID());
/*
- * As of January 13, 2016, ircd-hybrid-8 does support the following capabilities
+ * As of January 13, 2016, ircd-hybrid-8 supports the following capabilities
* which are required to work with IRC-services:
*
* QS - Can handle quit storm removal
@@ -197,97 +217,103 @@ class HybridProto : public IRCDProto
* SVS - Supports services
* EOB - Supports End Of Burst message
*/
- UplinkSocket::Message() << "CAPAB :QS EX CHW IE ENCAP TBURST SVS HOPS EOB";
+ Uplink::Send("CAPAB", "QS EX CHW IE ENCAP TBURST SVS HOPS EOB");
SendServer(Me);
- UplinkSocket::Message() << "SVINFO 6 6 0 :" << Anope::CurTime;
+ Uplink::Send("SVINFO", 6, 6, 0, Anope::CurTime);
}
- void SendClientIntroduction(User *u) anope_override
+ void SendClientIntroduction(User *u) override
{
Anope::string modes = "+" + u->GetModes();
- UplinkSocket::Message(Me) << "UID " << u->nick << " 1 " << u->timestamp << " " << modes << " "
- << u->GetIdent() << " " << u->host << " 0.0.0.0 " << u->GetUID() << " * :" << u->realname;
+ Uplink::Send(Me, "UID", u->nick, 1, u->timestamp, modes, u->GetIdent(), u->host, "0.0.0.0", u->GetUID(), "*", u->realname);
}
- void SendEOB() anope_override
+ void SendEOB() override
{
- UplinkSocket::Message(Me) << "EOB";
+ Uplink::Send(Me, "EOB");
}
- void SendModeInternal(const MessageSource &source, User *u, const Anope::string &buf) anope_override
+ void SendModeInternal(const MessageSource &source, User *u, const Anope::string &buf) override
{
- UplinkSocket::Message(source) << "SVSMODE " << u->GetUID() << " " << u->timestamp << " " << buf;
+ IRCMessage message(source, "SVSMODE", u->GetUID(), u->timestamp);
+ message.TokenizeAndPush(buf);
+ Uplink::SendMessage(message);
}
- void SendLogin(User *u, NickAlias *na) anope_override
+ void SendLogin(User *u, NickServ::Nick *na) override
{
- IRCD->SendMode(Config->GetClient("NickServ"), u, "+d %s", na->nc->display.c_str());
+ IRCD->SendMode(Config->GetClient("NickServ"), u, "+d %s", na->GetAccount()->GetDisplay().c_str());
}
- void SendLogout(User *u) anope_override
+ void SendLogout(User *u) override
{
IRCD->SendMode(Config->GetClient("NickServ"), u, "+d *");
}
- void SendChannel(Channel *c) anope_override
+ void SendChannel(Channel *c) override
{
Anope::string modes = c->GetModes(true, true);
if (modes.empty())
modes = "+";
- UplinkSocket::Message() << "SJOIN " << c->creation_time << " " << c->name << " " << modes << " :";
+ Uplink::Send("SJOIN", c->creation_time, c->name, modes, "");
}
- void SendTopic(const MessageSource &source, Channel *c) anope_override
+ void SendTopic(const MessageSource &source, Channel *c) override
{
- UplinkSocket::Message(source) << "TBURST " << c->creation_time << " " << c->name << " " << c->topic_ts << " " << c->topic_setter << " :" << c->topic;
+ Uplink::Send(source, "TBURST", c->creation_time, c->name, c->topic_ts, c->topic_setter, c->topic);
}
- void SendForceNickChange(User *u, const Anope::string &newnick, time_t when) anope_override
+ void SendForceNickChange(User *u, const Anope::string &newnick, time_t when) override
{
- UplinkSocket::Message(Me) << "SVSNICK " << u->GetUID() << " " << newnick << " " << when;
+ Uplink::Send(Me, "SVSNICK", u->GetUID(), newnick, when);
}
- void SendSVSJoin(const MessageSource &source, User *u, const Anope::string &chan, const Anope::string &) anope_override
+ void SendSVSJoin(const MessageSource &source, User *u, const Anope::string &chan, const Anope::string &) override
{
- UplinkSocket::Message(source) << "SVSJOIN " << u->GetUID() << " " << chan;
+ Uplink::Send(source, "SVSJOIN", u->GetUID(), chan);
}
- void SendSVSPart(const MessageSource &source, User *u, const Anope::string &chan, const Anope::string &param) anope_override
+ void SendSVSPart(const MessageSource &source, User *u, const Anope::string &chan, const Anope::string &param) override
{
if (!param.empty())
- UplinkSocket::Message(source) << "SVSPART " << u->GetUID() << " " << chan << " :" << param;
+ Uplink::Send(source, "SVSPART", u->GetUID(), chan, param);
else
- UplinkSocket::Message(source) << "SVSPART " << u->GetUID() << " " << chan;
+ Uplink::Send(source, "SVSPART", u->GetUID(), chan);
}
- void SendSVSHold(const Anope::string &nick, time_t t) anope_override
+ void SendSVSHold(const Anope::string &nick, time_t t) override
{
+#if 0
XLine x(nick, Me->GetName(), Anope::CurTime + t, "Being held for registered user");
this->SendSQLine(NULL, &x);
+#endif
}
+#warning "xline on stack"
- void SendSVSHoldDel(const Anope::string &nick) anope_override
+ void SendSVSHoldDel(const Anope::string &nick) override
{
+#if 0
XLine x(nick);
this->SendSQLineDel(&x);
+#endif
}
- void SendVhost(User *u, const Anope::string &ident, const Anope::string &host) anope_override
+ void SendVhost(User *u, const Anope::string &ident, const Anope::string &host) override
{
u->SetMode(Config->GetClient("HostServ"), "CLOAK", host);
}
- void SendVhostDel(User *u) anope_override
+ void SendVhostDel(User *u) override
{
u->RemoveMode(Config->GetClient("HostServ"), "CLOAK", u->host);
}
- bool IsIdentValid(const Anope::string &ident) anope_override
+ bool IsIdentValid(const Anope::string &ident) override
{
if (ident.empty() || ident.length() > Config->GetBlock("networkinfo")->Get<unsigned>("userlen"))
return false;
@@ -311,65 +337,44 @@ class HybridProto : public IRCDProto
}
};
-struct IRCDMessageBMask : IRCDMessage
+/* 0 1 2 3 */
+/* :0MC BMASK 1350157102 #channel b :*!*@*.test.com */
+void hybrid::BMask::Run(MessageSource &source, const std::vector<Anope::string> &params)
{
- IRCDMessageBMask(Module *creator) : IRCDMessage(creator, "BMASK", 4) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
+ Channel *c = Channel::Find(params[1]);
+ ChannelMode *mode = ModeManager::FindChannelModeByChar(params[2][0]);
- /* 0 1 2 3 */
- /* :0MC BMASK 1350157102 #channel b :*!*@*.test.com */
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ if (c && mode)
{
- Channel *c = Channel::Find(params[1]);
- ChannelMode *mode = ModeManager::FindChannelModeByChar(params[2][0]);
-
- if (c && mode)
- {
- spacesepstream bans(params[3]);
- Anope::string token;
-
- while (bans.GetToken(token))
- c->SetModeInternal(source, mode, token);
- }
+ spacesepstream bans(params[3]);
+ Anope::string token;
+ while (bans.GetToken(token))
+ c->SetModeInternal(source, mode, token);
}
-};
+}
-struct IRCDMessageEOB : IRCDMessage
+void hybrid::EOB::Run(MessageSource &source, const std::vector<Anope::string> &params)
{
- IRCDMessageEOB(Module *craetor) : IRCDMessage(craetor, "EOB", 0) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
-
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- source.GetServer()->Sync(true);
- }
-};
+ source.GetServer()->Sync(true);
+}
-struct IRCDMessageJoin : Message::Join
+void hybrid::Join::Run(MessageSource &source, const std::vector<Anope::string> &params)
{
- IRCDMessageJoin(Module *creator) : Message::Join(creator, "JOIN") { }
+ if (params.size() < 2)
+ return;
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (params.size() < 2)
- return;
+ std::vector<Anope::string> p = params;
+ p.erase(p.begin());
- std::vector<Anope::string> p = params;
- p.erase(p.begin());
-
- return Message::Join::Run(source, p);
- }
-};
+ return Message::Join::Run(source, p);
+}
-struct IRCDMessageNick : IRCDMessage
+/* 0 1 */
+/* :0MCAAAAAB NICK newnick 1350157102 */
+void hybrid::Nick::Run(MessageSource &source, const std::vector<Anope::string> &params)
{
- IRCDMessageNick(Module *creator) : IRCDMessage(creator, "NICK", 2) { SetFlag(IRCDMESSAGE_REQUIRE_USER); }
-
- /* 0 1 */
- /* :0MCAAAAAB NICK newnick 1350157102 */
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- source.GetUser()->ChangeNick(params[0], convertTo<time_t>(params[1]));
- }
-};
+ source.GetUser()->ChangeNick(params[0], convertTo<time_t>(params[1]));
+}
struct IRCDMessagePass : IRCDMessage
{
@@ -377,207 +382,158 @@ struct IRCDMessagePass : IRCDMessage
/* 0 1 2 3 */
/* PASS password TS 6 0MC */
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
{
UplinkSID = params[3];
}
};
-struct IRCDMessagePong : IRCDMessage
+void hybrid::Pong::Run(MessageSource &source, const std::vector<Anope::string> &params)
{
- IRCDMessagePong(Module *creator) : IRCDMessage(creator, "PONG", 0) { SetFlag(IRCDMESSAGE_SOFT_LIMIT); SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
+ source.GetServer()->Sync(false);
+}
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- source.GetServer()->Sync(false);
- }
-};
-
-struct IRCDMessageServer : IRCDMessage
+/* 0 1 2 */
+/* SERVER hades.arpa 1 :ircd-hybrid test server */
+void hybrid::Server::Run(MessageSource &source, const std::vector<Anope::string> &params)
{
- IRCDMessageServer(Module *creator) : IRCDMessage(creator, "SERVER", 3) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
+ /* Servers other than our immediate uplink are introduced via SID */
+ if (params[1] != "1")
+ return;
- /* 0 1 2 */
- /* SERVER hades.arpa 1 :ircd-hybrid test server */
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- /* Servers other than our immediate uplink are introduced via SID */
- if (params[1] != "1")
- return;
+ new ::Server(source.GetServer() == NULL ? Me : source.GetServer(), params[0], 1, params[2], UplinkSID);
- new Server(source.GetServer() == NULL ? Me : source.GetServer(), params[0], 1, params[2], UplinkSID);
+ IRCD->SendPing(Me->GetName(), params[0]);
+}
- IRCD->SendPing(Me->GetName(), params[0]);
- }
-};
-
-struct IRCDMessageSID : IRCDMessage
+void hybrid::SID::Run(MessageSource &source, const std::vector<Anope::string> &params)
{
- IRCDMessageSID(Module *creator) : IRCDMessage(creator, "SID", 4) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
-
- /* 0 1 2 3 */
- /* :0MC SID hades.arpa 2 4XY :ircd-hybrid test server */
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- unsigned int hops = params[1].is_pos_number_only() ? convertTo<unsigned>(params[1]) : 0;
- new Server(source.GetServer() == NULL ? Me : source.GetServer(), params[0], hops, params[3], params[2]);
+ unsigned int hops = params[1].is_pos_number_only() ? convertTo<unsigned>(params[1]) : 0;
+ new ::Server(source.GetServer() == NULL ? Me : source.GetServer(), params[0], hops, params[3], params[2]);
- IRCD->SendPing(Me->GetName(), params[0]);
- }
-};
+ IRCD->SendPing(Me->GetName(), params[0]);
+}
-struct IRCDMessageSJoin : IRCDMessage
+void hybrid::SJoin::Run(MessageSource &source, const std::vector<Anope::string> &params)
{
- IRCDMessageSJoin(Module *creator) : IRCDMessage(creator, "SJOIN", 2) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); SetFlag(IRCDMESSAGE_SOFT_LIMIT); }
-
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- Anope::string modes;
-
- if (params.size() >= 3)
- for (unsigned i = 2; i < params.size() - 1; ++i)
- modes += " " + params[i];
+ Anope::string modes;
+ if (params.size() >= 3)
+ for (unsigned i = 2; i < params.size() - 1; ++i)
+ modes += " " + params[i];
+ if (!modes.empty())
+ modes.erase(modes.begin());
- if (!modes.empty())
- modes.erase(modes.begin());
+ std::list<Message::Join::SJoinUser> users;
- std::list<Message::Join::SJoinUser> users;
+ spacesepstream sep(params[params.size() - 1]);
+ Anope::string buf;
- spacesepstream sep(params[params.size() - 1]);
- Anope::string buf;
+ while (sep.GetToken(buf))
+ {
+ Message::Join::SJoinUser sju;
- while (sep.GetToken(buf))
+ /* Get prefixes from the nick */
+ for (char ch; (ch = ModeManager::GetStatusChar(buf[0]));)
{
- Message::Join::SJoinUser sju;
-
- /* Get prefixes from the nick */
- for (char ch; (ch = ModeManager::GetStatusChar(buf[0]));)
- {
- buf.erase(buf.begin());
- sju.first.AddMode(ch);
- }
-
- sju.second = User::Find(buf);
- if (!sju.second)
- {
- Log(LOG_DEBUG) << "SJOIN for non-existent user " << buf << " on " << params[1];
- continue;
- }
+ buf.erase(buf.begin());
+ sju.first.AddMode(ch);
+ }
- users.push_back(sju);
+ sju.second = User::Find(buf);
+ if (!sju.second)
+ {
+ Log(LOG_DEBUG) << "SJOIN for non-existent user " << buf << " on " << params[1];
+ continue;
}
- time_t ts = Anope::string(params[0]).is_pos_number_only() ? convertTo<time_t>(params[0]) : Anope::CurTime;
- Message::Join::SJoin(source, params[1], ts, modes, users);
+ users.push_back(sju);
}
-};
-struct IRCDMessageSVSMode : IRCDMessage
-{
- IRCDMessageSVSMode(Module *creator) : IRCDMessage(creator, "SVSMODE", 3) { SetFlag(IRCDMESSAGE_SOFT_LIMIT); }
+ time_t ts = Anope::string(params[0]).is_pos_number_only() ? convertTo<time_t>(params[0]) : Anope::CurTime;
+ Message::Join::SJoin(source, params[1], ts, modes, users);
+}
- /*
- * parv[0] = nickname
- * parv[1] = TS
- * parv[2] = mode
- * parv[3] = optional argument (services id)
- */
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- User *u = User::Find(params[0]);
-
- if (!u)
- return;
+/*
+ * parv[0] = nickname
+ * parv[1] = TS
+ * parv[2] = mode
+ * parv[3] = optional argument (services id)
+ */
+void hybrid::SVSMode::Run(MessageSource &source, const std::vector<Anope::string> &params)
+{
+ User *u = User::Find(params[0]);
+ if (!u)
+ return;
- if (!params[1].is_pos_number_only() || convertTo<time_t>(params[1]) != u->timestamp)
- return;
+ if (!params[1].is_pos_number_only() || convertTo<time_t>(params[1]) != u->timestamp)
+ return;
- u->SetModesInternal(source, "%s", params[2].c_str());
- }
-};
+ u->SetModesInternal(source, "%s", params[2].c_str());
+}
-struct IRCDMessageTBurst : IRCDMessage
+void hybrid::TBurst::Run(MessageSource &source, const std::vector<Anope::string> &params)
{
- IRCDMessageTBurst(Module *creator) : IRCDMessage(creator, "TBURST", 5) { }
+ Anope::string setter;
+ sepstream(params[3], '!').GetToken(setter, 0);
+ time_t topic_time = Anope::string(params[2]).is_pos_number_only() ? convertTo<time_t>(params[2]) : Anope::CurTime;
+ Channel *c = Channel::Find(params[1]);
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- Anope::string setter;
- sepstream(params[3], '!').GetToken(setter, 0);
- time_t topic_time = Anope::string(params[2]).is_pos_number_only() ? convertTo<time_t>(params[2]) : Anope::CurTime;
- Channel *c = Channel::Find(params[1]);
+ if (c)
+ c->ChangeTopicInternal(NULL, setter, params[4], topic_time);
+}
- if (c)
- c->ChangeTopicInternal(NULL, setter, params[4], topic_time);
- }
-};
-
-struct IRCDMessageTMode : IRCDMessage
+void hybrid::TMode::Run(MessageSource &source, const std::vector<Anope::string> &params)
{
- IRCDMessageTMode(Module *creator) : IRCDMessage(creator, "TMODE", 3) { SetFlag(IRCDMESSAGE_SOFT_LIMIT); }
+ time_t ts = 0;
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ try
{
- time_t ts = 0;
-
- try
- {
- ts = convertTo<time_t>(params[0]);
- }
- catch (const ConvertException &) { }
+ ts = convertTo<time_t>(params[0]);
+ }
+ catch (const ConvertException &) { }
- Channel *c = Channel::Find(params[1]);
- Anope::string modes = params[2];
+ Channel *c = Channel::Find(params[1]);
+ Anope::string modes = params[2];
- for (unsigned i = 3; i < params.size(); ++i)
- modes += " " + params[i];
+ for (unsigned i = 3; i < params.size(); ++i)
+ modes += " " + params[i];
- if (c)
- c->SetModesInternal(source, modes, ts);
- }
-};
+ if (c)
+ c->SetModesInternal(source, modes, ts);
+}
-struct IRCDMessageUID : IRCDMessage
+/* 0 1 2 3 4 5 6 7 8 9 */
+/* :0MC UID Steve 1 1350157102 +oi ~steve resolved.host 10.0.0.1 0MCAAAAAB Steve :Mining all the time */
+void hybrid::UID::Run(MessageSource &source, const std::vector<Anope::string> &params)
{
- IRCDMessageUID(Module *creator) : IRCDMessage(creator, "UID", 10) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
-
- /* 0 1 2 3 4 5 6 7 8 9 */
- /* :0MC UID Steve 1 1350157102 +oi ~steve resolved.host 10.0.0.1 0MCAAAAAB Steve :Mining all the time */
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- Anope::string ip = params[6];
+ Anope::string ip = params[6];
- if (ip == "0") /* Can be 0 for spoofed clients */
- ip.clear();
+ if (ip == "0") /* Can be 0 for spoofed clients */
+ ip.clear();
- NickAlias *na = NULL;
- if (params[8] != "0" && params[8] != "*")
- na = NickAlias::Find(params[8]);
+ NickServ::Nick *na = NULL;
+ if (params[8] != "0" && params[8] != "*")
+ na = NickServ::FindNick(params[8]);
- /* Source is always the server */
- User::OnIntroduce(params[0], params[4], params[5], "",
- ip, source.GetServer(),
- params[9], params[2].is_pos_number_only() ? convertTo<time_t>(params[2]) : 0,
- params[3], params[7], na ? *na->nc : NULL);
- }
-};
+ /* Source is always the server */
+ User::OnIntroduce(params[0], params[4], params[5], "",
+ ip, source.GetServer(),
+ params[9], params[2].is_pos_number_only() ? convertTo<time_t>(params[2]) : 0,
+ params[3], params[7], na ? na->GetAccount() : NULL);
+}
-struct IRCDMessageCertFP: IRCDMessage
+/* 0 */
+/* :0MCAAAAAB CERTFP 4C62287BA6776A89CD4F8FF10A62FFB35E79319F51AF6C62C674984974FCCB1D */
+void hybrid::CertFP::Run(MessageSource &source, const std::vector<Anope::string> &params)
{
- IRCDMessageCertFP(Module *creator) : IRCDMessage(creator, "CERTFP", 1) { SetFlag(IRCDMESSAGE_REQUIRE_USER); }
+ User *u = source.GetUser();
- /* 0 */
- /* :0MCAAAAAB CERTFP 4C62287BA6776A89CD4F8FF10A62FFB35E79319F51AF6C62C674984974FCCB1D */
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- User *u = source.GetUser();
-
- u->fingerprint = params[0];
- FOREACH_MOD(OnFingerprint, (u));
- }
-};
+ u->fingerprint = params[0];
+ EventManager::Get()->Dispatch(&Event::Fingerprint::OnFingerprint, u);
+}
class ProtoHybrid : public Module
+ , public EventHook<Event::UserNickChange>
{
HybridProto ircd_proto;
@@ -603,90 +559,62 @@ class ProtoHybrid : public Module
Message::Whois message_whois;
/* Our message handlers */
- IRCDMessageBMask message_bmask;
- IRCDMessageEOB message_eob;
- IRCDMessageJoin message_join;
- IRCDMessageNick message_nick;
+ hybrid::CertFP message_certfp;
+ hybrid::BMask message_bmask;
+ hybrid::EOB message_eob;
+ hybrid::Join message_join;
+ hybrid::Nick message_nick;
IRCDMessagePass message_pass;
- IRCDMessagePong message_pong;
- IRCDMessageServer message_server;
- IRCDMessageSID message_sid;
- IRCDMessageSJoin message_sjoin;
- IRCDMessageSVSMode message_svsmode;
- IRCDMessageTBurst message_tburst;
- IRCDMessageTMode message_tmode;
- IRCDMessageUID message_uid;
- IRCDMessageCertFP message_certfp;
-
- void AddModes()
- {
- /* Add user modes */
- ModeManager::AddUserMode(new UserModeOperOnly("ADMIN", 'a'));
- ModeManager::AddUserMode(new UserModeOperOnly("CALLERID", 'g'));
- ModeManager::AddUserMode(new UserMode("INVIS", 'i'));
- ModeManager::AddUserMode(new UserModeOperOnly("LOCOPS", 'l'));
- ModeManager::AddUserMode(new UserModeOperOnly("OPER", 'o'));
- ModeManager::AddUserMode(new UserMode("HIDECHANS", 'p'));
- ModeManager::AddUserMode(new UserMode("HIDEIDLE", 'q'));
- ModeManager::AddUserMode(new UserModeNoone("REGISTERED", 'r'));
- ModeManager::AddUserMode(new UserModeOperOnly("SNOMASK", 's'));
- ModeManager::AddUserMode(new UserMode("WALLOPS", 'w'));
- ModeManager::AddUserMode(new UserMode("CLOAK", 'x'));
- ModeManager::AddUserMode(new UserMode("DEAF", 'D'));
- ModeManager::AddUserMode(new UserMode("SOFTCALLERID", 'G'));
- ModeManager::AddUserMode(new UserModeOperOnly("HIDEOPER", 'H'));
- ModeManager::AddUserMode(new UserMode("REGPRIV", 'R'));
- ModeManager::AddUserMode(new UserModeNoone("SSL", 'S'));
- ModeManager::AddUserMode(new UserModeNoone("WEBIRC", 'W'));
-
- /* b/e/I */
- ModeManager::AddChannelMode(new ChannelModeList("BAN", 'b'));
- ModeManager::AddChannelMode(new ChannelModeList("EXCEPT", 'e'));
- ModeManager::AddChannelMode(new ChannelModeList("INVITEOVERRIDE", 'I'));
-
- /* v/h/o */
- ModeManager::AddChannelMode(new ChannelModeStatus("VOICE", 'v', '+', 0));
- ModeManager::AddChannelMode(new ChannelModeStatus("HALFOP", 'h', '%', 1));
- ModeManager::AddChannelMode(new ChannelModeStatus("OP", 'o', '@', 2));
-
- /* l/k */
- ModeManager::AddChannelMode(new ChannelModeParam("LIMIT", 'l', true));
- ModeManager::AddChannelMode(new ChannelModeKey('k'));
-
- /* Add channel modes */
- ModeManager::AddChannelMode(new ChannelMode("BLOCKCOLOR", 'c'));
- ModeManager::AddChannelMode(new ChannelMode("INVITE", 'i'));
- ModeManager::AddChannelMode(new ChannelMode("MODERATED", 'm'));
- ModeManager::AddChannelMode(new ChannelMode("NOEXTERNAL", 'n'));
- ModeManager::AddChannelMode(new ChannelMode("PRIVATE", 'p'));
- ModeManager::AddChannelMode(new ChannelModeNoone("REGISTERED", 'r'));
- ModeManager::AddChannelMode(new ChannelMode("SECRET", 's'));
- ModeManager::AddChannelMode(new ChannelMode("TOPIC", 't'));
- ModeManager::AddChannelMode(new ChannelMode("NOCTCP", 'C'));
- ModeManager::AddChannelMode(new ChannelMode("REGMODERATED", 'M'));
- ModeManager::AddChannelMode(new ChannelModeOperOnly("OPERONLY", 'O'));
- ModeManager::AddChannelMode(new ChannelMode("REGISTEREDONLY", 'R'));
- ModeManager::AddChannelMode(new ChannelMode("SSL", 'S'));
- ModeManager::AddChannelMode(new ChannelMode("NONOTICE", 'T'));
- }
+ hybrid::Pong message_pong;
+ hybrid::Server message_server;
+ hybrid::SID message_sid;
+ hybrid::SJoin message_sjoin;
+ hybrid::SVSMode message_svsmode;
+ hybrid::TBurst message_tburst;
+ hybrid::TMode message_tmode;
+ hybrid::UID message_uid;
public:
- ProtoHybrid(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, PROTOCOL | VENDOR),
- ircd_proto(this),
- message_away(this), message_capab(this), message_error(this), message_invite(this), message_kick(this),
- message_kill(this), message_mode(this), message_motd(this), message_notice(this), message_part(this),
- message_ping(this), message_privmsg(this), message_quit(this), message_squit(this), message_stats(this),
- message_time(this), message_topic(this), message_version(this), message_whois(this),
- message_bmask(this), message_eob(this), message_join(this),
- message_nick(this), message_pass(this), message_pong(this), message_server(this), message_sid(this),
- message_sjoin(this), message_svsmode(this), message_tburst(this), message_tmode(this), message_uid(this),
- message_certfp(this)
- {
- if (Config->GetModule(this))
- this->AddModes();
- }
-
- void OnUserNickChange(User *u, const Anope::string &) anope_override
+ ProtoHybrid(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, PROTOCOL | VENDOR)
+ , EventHook<Event::UserNickChange>(this)
+ , ircd_proto(this)
+ , message_away(this)
+ , message_capab(this)
+ , message_error(this)
+ , message_invite(this)
+ , message_kick(this)
+ , message_kill(this)
+ , message_mode(this)
+ , message_motd(this)
+ , message_notice(this)
+ , message_part(this)
+ , message_ping(this)
+ , message_privmsg(this)
+ , message_quit(this)
+ , message_squit(this)
+ , message_stats(this)
+ , message_time(this)
+ , message_topic(this)
+ , message_version(this)
+ , message_whois(this)
+ , message_bmask(this)
+ , message_eob(this)
+ , message_join(this)
+ , message_nick(this)
+ , message_pass(this)
+ , message_pong(this)
+ , message_server(this)
+ , message_sid(this)
+ , message_sjoin(this)
+ , message_svsmode(this)
+ , message_tburst(this)
+ , message_tmode(this)
+ , message_uid(this)
+ , message_certfp(this)
+ {
+ }
+
+ void OnUserNickChange(User *u, const Anope::string &) override
{
u->RemoveModeInternal(Me, ModeManager::FindUserModeByName("REGISTERED"));
}
diff --git a/modules/protocol/inspircd12.cpp b/modules/protocol/inspircd12.cpp
deleted file mode 100644
index b8d846d5f..000000000
--- a/modules/protocol/inspircd12.cpp
+++ /dev/null
@@ -1,1383 +0,0 @@
-/* inspircd 1.2 functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-#include "modules/sasl.h"
-
-struct SASLUser
-{
- Anope::string uid;
- Anope::string acc;
- time_t created;
-};
-
-static std::list<SASLUser> saslusers;
-
-static Anope::string rsquit_server, rsquit_id;
-
-class ChannelModeFlood : public ChannelModeParam
-{
- public:
- ChannelModeFlood(char modeChar, bool minusNoArg) : ChannelModeParam("FLOOD", modeChar, minusNoArg) { }
-
- bool IsValid(Anope::string &value) const anope_override
- {
- try
- {
- Anope::string rest;
- if (!value.empty() && value[0] != ':' && convertTo<int>(value[0] == '*' ? value.substr(1) : value, rest, false) > 0 && rest[0] == ':' && rest.length() > 1 && convertTo<int>(rest.substr(1), rest, false) > 0 && rest.empty())
- return true;
- }
- catch (const ConvertException &) { }
-
- return false;
- }
-};
-
-class InspIRCd12Proto : public IRCDProto
-{
- private:
- void SendSVSKillInternal(const MessageSource &source, User *user, const Anope::string &buf) anope_override
- {
- IRCDProto::SendSVSKillInternal(source, user, buf);
- user->KillInternal(source, buf);
- }
-
- void SendChgIdentInternal(const Anope::string &nick, const Anope::string &vIdent)
- {
- if (!Servers::Capab.count("CHGIDENT"))
- Log() << "CHGIDENT not loaded!";
- else
- UplinkSocket::Message(Me) << "CHGIDENT " << nick << " " << vIdent;
- }
-
- void SendChgHostInternal(const Anope::string &nick, const Anope::string &vhost)
- {
- if (!Servers::Capab.count("CHGHOST"))
- Log() << "CHGHOST not loaded!";
- else
- UplinkSocket::Message(Me) << "CHGHOST " << nick << " " << vhost;
- }
-
- void SendAddLine(const Anope::string &xtype, const Anope::string &mask, time_t duration, const Anope::string &addedby, const Anope::string &reason)
- {
- UplinkSocket::Message(Me) << "ADDLINE " << xtype << " " << mask << " " << addedby << " " << Anope::CurTime << " " << duration << " :" << reason;
- }
-
- void SendDelLine(const Anope::string &xtype, const Anope::string &mask)
- {
- UplinkSocket::Message(Me) << "DELLINE " << xtype << " " << mask;
- }
-
- public:
- InspIRCd12Proto(Module *creator) : IRCDProto(creator, "InspIRCd 1.2")
- {
- DefaultPseudoclientModes = "+I";
- CanSVSNick = true;
- CanSVSJoin = true;
- CanSetVHost = true;
- CanSetVIdent = true;
- CanSQLine = true;
- CanSZLine = true;
- CanSVSHold = true;
- CanCertFP = true;
- RequiresID = true;
- MaxModes = 20;
- }
-
- void SendGlobalNotice(BotInfo *bi, const Server *dest, const Anope::string &msg) anope_override
- {
- UplinkSocket::Message(bi) << "NOTICE $" << dest->GetName() << " :" << msg;
- }
-
- void SendGlobalPrivmsg(BotInfo *bi, const Server *dest, const Anope::string &msg) anope_override
- {
- UplinkSocket::Message(bi) << "PRIVMSG $" << dest->GetName() << " :" << msg;
- }
-
- void SendAkillDel(const XLine *x) anope_override
- {
- /* InspIRCd may support regex bans */
- if (x->IsRegex() && Servers::Capab.count("RLINE"))
- {
- Anope::string mask = x->mask;
- size_t h = x->mask.find('#');
- if (h != Anope::string::npos)
- mask = mask.replace(h, 1, ' ');
- SendDelLine("R", mask);
- return;
- }
- else if (x->IsRegex() || x->HasNickOrReal())
- return;
-
- /* ZLine if we can instead */
- if (x->GetUser() == "*")
- {
- cidr addr(x->GetHost());
- if (addr.valid())
- {
- IRCD->SendSZLineDel(x);
- return;
- }
- }
-
- SendDelLine("G", x->GetUser() + "@" + x->GetHost());
- }
-
- void SendTopic(const MessageSource &source, Channel *c) anope_override
- {
- if (Servers::Capab.count("SVSTOPIC"))
- {
- UplinkSocket::Message(c->ci->WhoSends()) << "SVSTOPIC " << c->name << " " << c->topic_ts << " " << c->topic_setter << " :" << c->topic;
- }
- else
- {
- /* If the last time a topic was set is after the TS we want for this topic we must bump this topic's timestamp to now */
- time_t ts = c->topic_ts;
- if (c->topic_time > ts)
- ts = Anope::CurTime;
- /* But don't modify c->topic_ts, it should remain set to the real TS we want as ci->last_topic_time pulls from it */
- UplinkSocket::Message(source) << "FTOPIC " << c->name << " " << ts << " " << c->topic_setter << " :" << c->topic;
- }
- }
-
- void SendVhostDel(User *u) anope_override
- {
- if (u->HasMode("CLOAK"))
- this->SendChgHostInternal(u->nick, u->chost);
- else
- this->SendChgHostInternal(u->nick, u->host);
-
- if (Servers::Capab.count("CHGIDENT") && u->GetIdent() != u->GetVIdent())
- this->SendChgIdentInternal(u->nick, u->GetIdent());
- }
-
- void SendAkill(User *u, XLine *x) anope_override
- {
- // Calculate the time left before this would expire, capping it at 2 days
- time_t timeleft = x->expires - Anope::CurTime;
- if (timeleft > 172800 || !x->expires)
- timeleft = 172800;
-
- /* InspIRCd may support regex bans, if they do we can send this and forget about it */
- if (x->IsRegex() && Servers::Capab.count("RLINE"))
- {
- Anope::string mask = x->mask;
- size_t h = x->mask.find('#');
- if (h != Anope::string::npos)
- mask = mask.replace(h, 1, ' ');
- SendAddLine("R", mask, timeleft, x->by, x->GetReason());
- return;
- }
- else if (x->IsRegex() || x->HasNickOrReal())
- {
- if (!u)
- {
- /* No user (this akill was just added), and contains nick and/or realname. Find users that match and ban them */
- for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it)
- if (x->manager->Check(it->second, x))
- this->SendAkill(it->second, x);
- return;
- }
-
- const XLine *old = x;
-
- if (old->manager->HasEntry("*@" + u->host))
- return;
-
- /* We can't akill x as it has a nick and/or realname included, so create a new akill for *@host */
- x = new XLine("*@" + u->host, old->by, old->expires, old->reason, old->id);
- old->manager->AddXLine(x);
-
- Log(Config->GetClient("OperServ"), "akill") << "AKILL: Added an akill for " << x->mask << " because " << u->GetMask() << "#" << u->realname << " matches " << old->mask;
- }
-
- /* ZLine if we can instead */
- if (x->GetUser() == "*")
- {
- cidr addr(x->GetHost());
- if (addr.valid())
- {
- IRCD->SendSZLine(u, x);
- return;
- }
- }
-
- SendAddLine("G", x->GetUser() + "@" + x->GetHost(), timeleft, x->by, x->GetReason());
- }
-
- void SendNumericInternal(int numeric, const Anope::string &dest, const Anope::string &buf) anope_override
- {
- User *u = User::Find(dest);
- UplinkSocket::Message() << "PUSH " << dest << " ::" << Me->GetName() << " " << numeric << " " << (u ? u->nick : dest) << " " << buf;
- }
-
- void SendModeInternal(const MessageSource &source, const Channel *dest, const Anope::string &buf) anope_override
- {
- UplinkSocket::Message(source) << "FMODE " << dest->name << " " << dest->creation_time << " " << buf;
- }
-
- void SendClientIntroduction(User *u) anope_override
- {
- Anope::string modes = "+" + u->GetModes();
- UplinkSocket::Message(Me) << "UID " << u->GetUID() << " " << u->timestamp << " " << u->nick << " " << u->host << " " << u->host << " " << u->GetIdent() << " 0.0.0.0 " << u->timestamp << " " << modes << " :" << u->realname;
- if (modes.find('o') != Anope::string::npos)
- UplinkSocket::Message(u) << "OPERTYPE :services";
- }
-
- /* SERVER services-dev.chatspike.net password 0 :Description here */
- void SendServer(const Server *server) anope_override
- {
- /* if rsquit is set then we are waiting on a squit */
- if (rsquit_id.empty() && rsquit_server.empty())
- UplinkSocket::Message() << "SERVER " << server->GetName() << " " << Config->Uplinks[Anope::CurrentUplink].password << " " << server->GetHops() << " " << server->GetSID() << " :" << server->GetDescription();
- }
-
- void SendSquit(Server *s, const Anope::string &message) anope_override
- {
- if (s != Me)
- {
- rsquit_id = s->GetSID();
- rsquit_server = s->GetName();
- UplinkSocket::Message() << "RSQUIT " << s->GetName() << " :" << message;
- }
- else
- UplinkSocket::Message() << "SQUIT " << s->GetName() << " :" << message;
- }
-
- /* JOIN */
- void SendJoin(User *user, Channel *c, const ChannelStatus *status) anope_override
- {
- UplinkSocket::Message(Me) << "FJOIN " << c->name << " " << c->creation_time << " +" << c->GetModes(true, true) << " :," << user->GetUID();
- /* Note that we can send this with the FJOIN but choose not to
- * because the mode stacker will handle this and probably will
- * merge these modes with +nrt and other mlocked modes
- */
- if (status)
- {
- /* First save the channel status incase uc->Status == status */
- ChannelStatus cs = *status;
- /* If the user is internally on the channel with flags, kill them so that
- * the stacker will allow this.
- */
- ChanUserContainer *uc = c->FindUser(user);
- if (uc != NULL)
- uc->status.Clear();
-
- BotInfo *setter = BotInfo::Find(user->GetUID());
- for (size_t i = 0; i < cs.Modes().length(); ++i)
- c->SetMode(setter, ModeManager::FindChannelModeByChar(cs.Modes()[i]), user->GetUID(), false);
-
- if (uc != NULL)
- uc->status = cs;
- }
- }
-
- /* UNSQLINE */
- void SendSQLineDel(const XLine *x) anope_override
- {
- SendDelLine("Q", x->mask);
- }
-
- /* SQLINE */
- void SendSQLine(User *, const XLine *x) anope_override
- {
- // Calculate the time left before this would expire, capping it at 2 days
- time_t timeleft = x->expires - Anope::CurTime;
- if (timeleft > 172800 || !x->expires)
- timeleft = 172800;
- SendAddLine("Q", x->mask, timeleft, x->by, x->GetReason());
- }
-
- void SendVhost(User *u, const Anope::string &vIdent, const Anope::string &vhost) anope_override
- {
- if (!vIdent.empty())
- this->SendChgIdentInternal(u->nick, vIdent);
- if (!vhost.empty())
- this->SendChgHostInternal(u->nick, vhost);
- }
-
- void SendConnect() anope_override
- {
- SendServer(Me);
- }
-
- /* SVSHOLD - set */
- void SendSVSHold(const Anope::string &nick, time_t t) anope_override
- {
- UplinkSocket::Message(Config->GetClient("NickServ")) << "SVSHOLD " << nick << " " << t << " :Being held for registered user";
- }
-
- /* SVSHOLD - release */
- void SendSVSHoldDel(const Anope::string &nick) anope_override
- {
- UplinkSocket::Message(Config->GetClient("NickServ")) << "SVSHOLD " << nick;
- }
-
- /* UNSZLINE */
- void SendSZLineDel(const XLine *x) anope_override
- {
- SendDelLine("Z", x->GetHost());
- }
-
- /* SZLINE */
- void SendSZLine(User *, const XLine *x) anope_override
- {
- // Calculate the time left before this would expire, capping it at 2 days
- time_t timeleft = x->expires - Anope::CurTime;
- if (timeleft > 172800 || !x->expires)
- timeleft = 172800;
- SendAddLine("Z", x->GetHost(), timeleft, x->by, x->GetReason());
- }
-
- void SendSVSJoin(const MessageSource &source, User *u, const Anope::string &chan, const Anope::string &) anope_override
- {
- UplinkSocket::Message(source) << "SVSJOIN " << u->GetUID() << " " << chan;
- }
-
- void SendSVSPart(const MessageSource &source, User *u, const Anope::string &chan, const Anope::string &param) anope_override
- {
- if (!param.empty())
- UplinkSocket::Message(source) << "SVSPART " << u->GetUID() << " " << chan << " :" << param;
- else
- UplinkSocket::Message(source) << "SVSPART " << u->GetUID() << " " << chan;
- }
-
- void SendSWhois(const MessageSource &, const Anope::string &who, const Anope::string &mask) anope_override
- {
- User *u = User::Find(who);
-
- UplinkSocket::Message(Me) << "METADATA " << u->GetUID() << " swhois :" << mask;
- }
-
- void SendBOB() anope_override
- {
- UplinkSocket::Message(Me) << "BURST " << Anope::CurTime;
- Module *enc = ModuleManager::FindFirstOf(ENCRYPTION);
- UplinkSocket::Message(Me) << "VERSION :Anope-" << Anope::Version() << " " << Me->GetName() << " :" << IRCD->GetProtocolName() << " - (" << (enc ? enc->name : "none") << ") -- " << Anope::VersionBuildString();
- }
-
- void SendEOB() anope_override
- {
- UplinkSocket::Message(Me) << "ENDBURST";
- }
-
- void SendGlobopsInternal(const MessageSource &source, const Anope::string &buf) anope_override
- {
- if (Servers::Capab.count("GLOBOPS"))
- UplinkSocket::Message(source) << "SNONOTICE g :" << buf;
- else
- UplinkSocket::Message(source) << "SNONOTICE A :" << buf;
- }
-
- void SendLogin(User *u, NickAlias *na) anope_override
- {
- /* InspIRCd uses an account to bypass chmode +R, not umode +r, so we can't send this here */
- if (na->nc->HasExt("UNCONFIRMED"))
- return;
-
- UplinkSocket::Message(Me) << "METADATA " << u->GetUID() << " accountname :" << na->nc->display;
- }
-
- void SendLogout(User *u) anope_override
- {
- UplinkSocket::Message(Me) << "METADATA " << u->GetUID() << " accountname :";
- }
-
- void SendChannel(Channel *c) anope_override
- {
- UplinkSocket::Message(Me) << "FJOIN " << c->name << " " << c->creation_time << " +" << c->GetModes(true, true) << " :";
- }
-
- void SendOper(User *u) anope_override
- {
- }
-
- void SendSASLMessage(const SASL::Message &message) anope_override
- {
- UplinkSocket::Message(Me) << "ENCAP " << message.target.substr(0, 3) << " SASL " << message.source << " " << message.target << " " << message.type << " " << message.data << (message.ext.empty() ? "" : (" " + message.ext));
- }
-
- void SendSVSLogin(const Anope::string &uid, const Anope::string &acc, const Anope::string &vident, const Anope::string &vhost) anope_override
- {
- UplinkSocket::Message(Me) << "METADATA " << uid << " accountname :" << acc;
-
- SASLUser su;
- su.uid = uid;
- su.acc = acc;
- su.created = Anope::CurTime;
-
- for (std::list<SASLUser>::iterator it = saslusers.begin(); it != saslusers.end();)
- {
- SASLUser &u = *it;
-
- if (u.created + 30 < Anope::CurTime || u.uid == uid)
- it = saslusers.erase(it);
- else
- ++it;
- }
-
- saslusers.push_back(su);
- }
-
- bool IsExtbanValid(const Anope::string &mask) anope_override
- {
- return mask.length() >= 3 && mask[1] == ':';
- }
-
- bool IsIdentValid(const Anope::string &ident) anope_override
- {
- if (ident.empty() || ident.length() > Config->GetBlock("networkinfo")->Get<unsigned>("userlen"))
- return false;
-
- for (unsigned i = 0; i < ident.length(); ++i)
- {
- const char &c = ident[i];
-
- if (c >= 'A' && c <= '}')
- continue;
-
- if ((c >= '0' && c <= '9') || c == '-' || c == '.')
- continue;
-
- return false;
- }
-
- return true;
- }
-};
-
-class InspIRCdExtBan : public ChannelModeList
-{
- public:
- InspIRCdExtBan(const Anope::string &mname, char modeChar) : ChannelModeList(mname, modeChar) { }
-
- bool Matches(User *u, const Entry *e) anope_override
- {
- const Anope::string &mask = e->GetMask();
-
- if (mask.find("m:") == 0 || mask.find("N:") == 0)
- {
- Anope::string real_mask = mask.substr(2);
-
- Entry en(this->name, real_mask);
- if (en.Matches(u))
- return true;
- }
- else if (mask.find("j:") == 0)
- {
- Anope::string real_mask = mask.substr(2);
-
- Channel *c = Channel::Find(real_mask);
- if (c != NULL && c->FindUser(u) != NULL)
- return true;
- }
- else if (mask.find("M:") == 0 || mask.find("R:") == 0)
- {
- Anope::string real_mask = mask.substr(2);
-
- if (u->IsIdentified() && real_mask.equals_ci(u->Account()->display))
- return true;
- }
- else if (mask.find("r:") == 0)
- {
- Anope::string real_mask = mask.substr(2);
-
- if (Anope::Match(u->realname, real_mask))
- return true;
- }
- else if (mask.find("s:") == 0)
- {
- Anope::string real_mask = mask.substr(2);
-
- if (Anope::Match(u->server->GetName(), real_mask))
- return true;
- }
-
- return false;
- }
-};
-
-struct IRCDMessageCapab : Message::Capab
-{
- IRCDMessageCapab(Module *creator) : Message::Capab(creator, "CAPAB") { SetFlag(IRCDMESSAGE_SOFT_LIMIT); }
-
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (params[0].equals_cs("START"))
- {
- /* reset CAPAB */
- Servers::Capab.clear();
- Servers::Capab.insert("NOQUIT");
- IRCD->CanSVSHold = false;
- }
- else if (params[0].equals_cs("MODULES") && params.size() > 1)
- {
- if (params[1].find("m_globops.so") != Anope::string::npos)
- Servers::Capab.insert("GLOBOPS");
- if (params[1].find("m_services_account.so") != Anope::string::npos)
- Servers::Capab.insert("SERVICES");
- if (params[1].find("m_svshold.so") != Anope::string::npos)
- IRCD->CanSVSHold = true;
- if (params[1].find("m_chghost.so") != Anope::string::npos)
- Servers::Capab.insert("CHGHOST");
- if (params[1].find("m_chgident.so") != Anope::string::npos)
- Servers::Capab.insert("CHGIDENT");
- if (params[1].find("m_hidechans.so") != Anope::string::npos)
- Servers::Capab.insert("HIDECHANS");
- if (params[1].find("m_servprotect.so") != Anope::string::npos)
- IRCD->DefaultPseudoclientModes = "+Ik";
- if (params[1].find("m_rline.so") != Anope::string::npos)
- Servers::Capab.insert("RLINE");
- }
- else if (params[0].equals_cs("CAPABILITIES") && params.size() > 1)
- {
- spacesepstream ssep(params[1]);
- Anope::string capab;
- while (ssep.GetToken(capab))
- {
- if (capab.find("CHANMODES") != Anope::string::npos)
- {
- Anope::string modes(capab.begin() + 10, capab.end());
- commasepstream sep(modes);
- Anope::string modebuf;
-
- sep.GetToken(modebuf);
- for (size_t t = 0, end = modebuf.length(); t < end; ++t)
- {
- switch (modebuf[t])
- {
- case 'b':
- ModeManager::AddChannelMode(new InspIRCdExtBan("BAN", 'b'));
- continue;
- case 'e':
- ModeManager::AddChannelMode(new InspIRCdExtBan("EXCEPT", 'e'));
- continue;
- case 'I':
- ModeManager::AddChannelMode(new InspIRCdExtBan("INVITEOVERRIDE", 'I'));
- continue;
- /* InspIRCd sends q and a here if they have no prefixes */
- case 'q':
- ModeManager::AddChannelMode(new ChannelModeStatus("OWNER", 'q', '@', 4));
- continue;
- case 'a':
- ModeManager::AddChannelMode(new ChannelModeStatus("PROTECT" , 'a', '@', 3));
- continue;
- default:
- ModeManager::AddChannelMode(new ChannelModeList("", modebuf[t]));
- }
- }
-
- sep.GetToken(modebuf);
- for (size_t t = 0, end = modebuf.length(); t < end; ++t)
- {
- switch (modebuf[t])
- {
- case 'k':
- ModeManager::AddChannelMode(new ChannelModeKey('k'));
- continue;
- default:
- ModeManager::AddChannelMode(new ChannelModeParam("", modebuf[t]));
- }
- }
-
- sep.GetToken(modebuf);
- for (size_t t = 0, end = modebuf.length(); t < end; ++t)
- {
- switch (modebuf[t])
- {
- case 'F':
- ModeManager::AddChannelMode(new ChannelModeParam("NICKFLOOD", 'F', true));
- continue;
- case 'J':
- ModeManager::AddChannelMode(new ChannelModeParam("NOREJOIN", 'J', true));
- continue;
- case 'L':
- ModeManager::AddChannelMode(new ChannelModeParam("REDIRECT", 'L', true));
- continue;
- case 'f':
- ModeManager::AddChannelMode(new ChannelModeFlood('f', true));
- continue;
- case 'j':
- ModeManager::AddChannelMode(new ChannelModeParam("JOINFLOOD", 'j', true));
- continue;
- case 'l':
- ModeManager::AddChannelMode(new ChannelModeParam("LIMIT", 'l', true));
- continue;
- default:
- ModeManager::AddChannelMode(new ChannelModeParam("", modebuf[t], true));
- }
- }
-
- sep.GetToken(modebuf);
- for (size_t t = 0, end = modebuf.length(); t < end; ++t)
- {
- switch (modebuf[t])
- {
- case 'A':
- ModeManager::AddChannelMode(new ChannelMode("ALLINVITE", 'A'));
- continue;
- case 'B':
- ModeManager::AddChannelMode(new ChannelMode("BLOCKCAPS", 'B'));
- continue;
- case 'C':
- ModeManager::AddChannelMode(new ChannelMode("NOCTCP", 'C'));
- continue;
- case 'D':
- ModeManager::AddChannelMode(new ChannelMode("DELAYEDJOIN", 'D'));
- continue;
- case 'G':
- ModeManager::AddChannelMode(new ChannelMode("CENSOR", 'G'));
- continue;
- case 'K':
- ModeManager::AddChannelMode(new ChannelMode("NOKNOCK", 'K'));
- continue;
- case 'M':
- ModeManager::AddChannelMode(new ChannelMode("REGMODERATED", 'M'));
- continue;
- case 'N':
- ModeManager::AddChannelMode(new ChannelMode("NONICK", 'N'));
- continue;
- case 'O':
- ModeManager::AddChannelMode(new ChannelModeOperOnly("OPERONLY", 'O'));
- continue;
- case 'P':
- ModeManager::AddChannelMode(new ChannelMode("PERM", 'P'));
- continue;
- case 'Q':
- ModeManager::AddChannelMode(new ChannelMode("NOKICK", 'Q'));
- continue;
- case 'R':
- ModeManager::AddChannelMode(new ChannelMode("REGISTEREDONLY", 'R'));
- continue;
- case 'S':
- ModeManager::AddChannelMode(new ChannelMode("STRIPCOLOR", 'S'));
- continue;
- case 'T':
- ModeManager::AddChannelMode(new ChannelMode("NONOTICE", 'T'));
- continue;
- case 'c':
- ModeManager::AddChannelMode(new ChannelMode("BLOCKCOLOR", 'c'));
- continue;
- case 'i':
- ModeManager::AddChannelMode(new ChannelMode("INVITE", 'i'));
- continue;
- case 'm':
- ModeManager::AddChannelMode(new ChannelMode("MODERATED", 'm'));
- continue;
- case 'n':
- ModeManager::AddChannelMode(new ChannelMode("NOEXTERNAL", 'n'));
- continue;
- case 'p':
- ModeManager::AddChannelMode(new ChannelMode("PRIVATE", 'p'));
- continue;
- case 'r':
- ModeManager::AddChannelMode(new ChannelModeNoone("REGISTERED", 'r'));
- continue;
- case 's':
- ModeManager::AddChannelMode(new ChannelMode("SECRET", 's'));
- continue;
- case 't':
- ModeManager::AddChannelMode(new ChannelMode("TOPIC", 't'));
- continue;
- case 'u':
- ModeManager::AddChannelMode(new ChannelMode("AUDITORIUM", 'u'));
- continue;
- case 'z':
- ModeManager::AddChannelMode(new ChannelMode("SSL", 'z'));
- continue;
- default:
- ModeManager::AddChannelMode(new ChannelMode("", modebuf[t]));
- }
- }
- }
- else if (capab.find("USERMODES") != Anope::string::npos)
- {
- Anope::string modes(capab.begin() + 10, capab.end());
- commasepstream sep(modes);
- Anope::string modebuf;
-
- while (sep.GetToken(modebuf))
- {
- for (size_t t = 0, end = modebuf.length(); t < end; ++t)
- {
- switch (modebuf[t])
- {
- case 'h':
- ModeManager::AddUserMode(new UserModeOperOnly("HELPOP", 'h'));
- continue;
- case 'B':
- ModeManager::AddUserMode(new UserMode("BOT", 'B'));
- continue;
- case 'G':
- ModeManager::AddUserMode(new UserMode("CENSOR", 'G'));
- continue;
- case 'H':
- ModeManager::AddUserMode(new UserModeOperOnly("HIDEOPER", 'H'));
- continue;
- case 'I':
- ModeManager::AddUserMode(new UserMode("PRIV", 'I'));
- continue;
- case 'Q':
- ModeManager::AddUserMode(new UserModeOperOnly("HIDDEN", 'Q'));
- continue;
- case 'R':
- ModeManager::AddUserMode(new UserMode("REGPRIV", 'R'));
- continue;
- case 'S':
- ModeManager::AddUserMode(new UserMode("STRIPCOLOR", 'S'));
- continue;
- case 'W':
- ModeManager::AddUserMode(new UserMode("WHOIS", 'W'));
- continue;
- case 'c':
- ModeManager::AddUserMode(new UserMode("COMMONCHANS", 'c'));
- continue;
- case 'g':
- ModeManager::AddUserMode(new UserMode("CALLERID", 'g'));
- continue;
- case 'i':
- ModeManager::AddUserMode(new UserMode("INVIS", 'i'));
- continue;
- case 'k':
- ModeManager::AddUserMode(new UserModeNoone("PROTECTED", 'k'));
- continue;
- case 'o':
- ModeManager::AddUserMode(new UserModeOperOnly("OPER", 'o'));
- continue;
- case 'r':
- ModeManager::AddUserMode(new UserModeNoone("REGISTERED", 'r'));
- continue;
- case 'w':
- ModeManager::AddUserMode(new UserMode("WALLOPS", 'w'));
- continue;
- case 'x':
- ModeManager::AddUserMode(new UserMode("CLOAK", 'x'));
- continue;
- case 'd':
- ModeManager::AddUserMode(new UserMode("DEAF", 'd'));
- continue;
- default:
- ModeManager::AddUserMode(new UserMode("", modebuf[t]));
- }
- }
- }
- }
- else if (capab.find("PREFIX=(") != Anope::string::npos)
- {
- Anope::string modes(capab.begin() + 8, capab.begin() + capab.find(')'));
- Anope::string chars(capab.begin() + capab.find(')') + 1, capab.end());
- unsigned short level = modes.length() - 1;
-
- for (size_t t = 0, end = modes.length(); t < end; ++t)
- {
- switch (modes[t])
- {
- case 'q':
- ModeManager::AddChannelMode(new ChannelModeStatus("OWNER", 'q', chars[t], level--));
- continue;
- case 'a':
- ModeManager::AddChannelMode(new ChannelModeStatus("PROTECT", 'a', chars[t], level--));
- continue;
- case 'o':
- ModeManager::AddChannelMode(new ChannelModeStatus("OP", 'o', chars[t], level--));
- continue;
- case 'h':
- ModeManager::AddChannelMode(new ChannelModeStatus("HALFOP", 'h', chars[t], level--));
- continue;
- case 'v':
- ModeManager::AddChannelMode(new ChannelModeStatus("VOICE", 'v', chars[t], level--));
- continue;
- default:
- ModeManager::AddChannelMode(new ChannelModeStatus("", modes[t], chars[t], level--));
- }
- }
-
- ModeManager::RebuildStatusModes();
- }
- else if (capab.find("MAXMODES=") != Anope::string::npos)
- {
- Anope::string maxmodes(capab.begin() + 9, capab.end());
- IRCD->MaxModes = maxmodes.is_pos_number_only() ? convertTo<unsigned>(maxmodes) : 3;
- }
- }
- }
- else if (params[0].equals_cs("END"))
- {
- if (!Servers::Capab.count("GLOBOPS"))
- {
- UplinkSocket::Message() << "ERROR :m_globops is not loaded. This is required by Anope";
- Anope::QuitReason = "Remote server does not have the m_globops module loaded, and this is required.";
- Anope::Quitting = true;
- return;
- }
- if (!Servers::Capab.count("SERVICES"))
- {
- UplinkSocket::Message() << "ERROR :m_services_account.so is not loaded. This is required by Anope";
- Anope::QuitReason = "ERROR: Remote server does not have the m_services_account module loaded, and this is required.";
- Anope::Quitting = true;
- return;
- }
- if (!Servers::Capab.count("HIDECHANS"))
- {
- UplinkSocket::Message() << "ERROR :m_hidechans.so is not loaded. This is required by Anope";
- Anope::QuitReason = "ERROR: Remote server does not have the m_hidechans module loaded, and this is required.";
- Anope::Quitting = true;
- return;
- }
- if (!IRCD->CanSVSHold)
- Log() << "SVSHOLD missing, Usage disabled until module is loaded.";
- if (!Servers::Capab.count("CHGHOST"))
- Log() << "CHGHOST missing, Usage disabled until module is loaded.";
- if (!Servers::Capab.count("CHGIDENT"))
- Log() << "CHGIDENT missing, Usage disabled until module is loaded.";
- }
-
- Message::Capab::Run(source, params);
- }
-};
-
-struct IRCDMessageChgIdent : IRCDMessage
-{
- IRCDMessageChgIdent(Module *creator) : IRCDMessage(creator, "CHGIDENT", 2) { }
-
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- User *u = User::Find(params[0]);
- if (u)
- u->SetIdent(params[1]);
- }
-};
-
-struct IRCDMessageChgName : IRCDMessage
-{
- IRCDMessageChgName(Module *creator, const Anope::string &n) : IRCDMessage(creator, n, 1) { SetFlag(IRCDMESSAGE_REQUIRE_USER); }
-
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- source.GetUser()->SetRealname(params[0]);
- }
-};
-
-struct IRCDMessageEncap : IRCDMessage
-{
- IRCDMessageEncap(Module *creator) : IRCDMessage(creator, "ENCAP", 4) { SetFlag(IRCDMESSAGE_SOFT_LIMIT); }
-
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (Anope::Match(Me->GetSID(), params[0]) == false)
- return;
-
- if (SASL::sasl && params[1] == "SASL" && params.size() >= 6)
- {
- SASL::Message m;
- m.source = params[2];
- m.target = params[3];
- m.type = params[4];
- m.data = params[5];
- m.ext = params.size() > 6 ? params[6] : "";
-
- SASL::sasl->ProcessMessage(m);
- }
- }
-};
-
-struct IRCDMessageEndburst : IRCDMessage
-{
- IRCDMessageEndburst(Module *creator) : IRCDMessage(creator, "ENDBURST", 0) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
-
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- Server *s = source.GetServer();
-
- Log(LOG_DEBUG) << "Processed ENDBURST for " << s->GetName();
-
- s->Sync(true);
- }
-};
-
-struct IRCDMessageFHost : IRCDMessage
-{
- IRCDMessageFHost(Module *creator, const Anope::string &n) : IRCDMessage(creator, n, 1) { SetFlag(IRCDMESSAGE_REQUIRE_USER); }
-
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- source.GetUser()->SetDisplayedHost(params[0]);
- }
-};
-
-struct IRCDMessageFJoin : IRCDMessage
-{
- IRCDMessageFJoin(Module *creator) : IRCDMessage(creator, "FJOIN", 2) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); SetFlag(IRCDMESSAGE_SOFT_LIMIT); }
-
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- Anope::string modes;
- if (params.size() >= 3)
- {
- for (unsigned i = 2; i < params.size() - 1; ++i)
- modes += " " + params[i];
- if (!modes.empty())
- modes.erase(modes.begin());
- }
-
- std::list<Message::Join::SJoinUser> users;
-
- spacesepstream sep(params[params.size() - 1]);
- Anope::string buf;
- while (sep.GetToken(buf))
- {
- Message::Join::SJoinUser sju;
-
- /* Loop through prefixes and find modes for them */
- for (char c; (c = buf[0]) != ',' && c;)
- {
- buf.erase(buf.begin());
- sju.first.AddMode(c);
- }
- /* Erase the , */
- if (!buf.empty())
- buf.erase(buf.begin());
-
- sju.second = User::Find(buf);
- if (!sju.second)
- {
- Log(LOG_DEBUG) << "FJOIN for non-existent user " << buf << " on " << params[0];
- continue;
- }
-
- users.push_back(sju);
- }
-
- time_t ts = Anope::string(params[1]).is_pos_number_only() ? convertTo<time_t>(params[1]) : Anope::CurTime;
- Message::Join::SJoin(source, params[0], ts, modes, users);
- }
-};
-
-struct IRCDMessageFMode : IRCDMessage
-{
- IRCDMessageFMode(Module *creator) : IRCDMessage(creator, "FMODE", 3) { SetFlag(IRCDMESSAGE_SOFT_LIMIT); }
-
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- /* :source FMODE #test 12345678 +nto foo */
-
- Anope::string modes = params[2];
- for (unsigned n = 3; n < params.size(); ++n)
- modes += " " + params[n];
-
- Channel *c = Channel::Find(params[0]);
- time_t ts;
-
- try
- {
- ts = convertTo<time_t>(params[1]);
- }
- catch (const ConvertException &)
- {
- ts = 0;
- }
-
- if (c)
- c->SetModesInternal(source, modes, ts);
- }
-};
-
-struct IRCDMessageFTopic : IRCDMessage
-{
- IRCDMessageFTopic(Module *creator) : IRCDMessage(creator, "FTOPIC", 4) { }
-
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- /* :source FTOPIC channel topicts setby :topic */
-
- Channel *c = Channel::Find(params[0]);
- if (c)
- c->ChangeTopicInternal(NULL, params[2], params[3], Anope::string(params[1]).is_pos_number_only() ? convertTo<time_t>(params[1]) : Anope::CurTime);
- }
-};
-
-struct IRCDMessageIdle : IRCDMessage
-{
- IRCDMessageIdle(Module *creator) : IRCDMessage(creator, "IDLE", 1) { }
-
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- BotInfo *bi = BotInfo::Find(params[0]);
- if (bi)
- UplinkSocket::Message(bi) << "IDLE " << source.GetSource() << " " << Anope::StartTime << " " << (Anope::CurTime - bi->lastmsg);
- else
- {
- User *u = User::Find(params[0]);
- if (u && u->server == Me)
- UplinkSocket::Message(u) << "IDLE " << source.GetSource() << " " << Anope::StartTime << " 0";
- }
- }
-};
-
-/*
- * source = numeric of the sending server
- * params[0] = uuid
- * params[1] = metadata name
- * params[2] = data
- */
-struct IRCDMessageMetadata : IRCDMessage
-{
- IRCDMessageMetadata(Module *creator) : IRCDMessage(creator, "METADATA", 3) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
-
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (isdigit(params[0][0]))
- {
- if (params[1].equals_cs("accountname"))
- {
- User *u = User::Find(params[0]);
- NickCore *nc = NickCore::Find(params[2]);
- if (u && nc)
- u->Login(nc);
- }
-
- /*
- * possible incoming ssl_cert messages:
- * Received: :409 METADATA 409AAAAAA ssl_cert :vTrSe c38070ce96e41cc144ed6590a68d45a6 <...> <...>
- * Received: :409 METADATA 409AAAAAC ssl_cert :vTrSE Could not get peer certificate: error:00000000:lib(0):func(0):reason(0)
- */
- else if (params[1].equals_cs("ssl_cert"))
- {
- User *u = User::Find(params[0]);
- if (!u)
- return;
- u->Extend<bool>("ssl");
- Anope::string data = params[2].c_str();
- size_t pos1 = data.find(' ') + 1;
- size_t pos2 = data.find(' ', pos1);
- if ((pos2 - pos1) >= 32) // inspircd supports md5 and sha1 fingerprint hashes -> size 32 or 40 bytes.
- {
- u->fingerprint = data.substr(pos1, pos2 - pos1);
- }
- FOREACH_MOD(OnFingerprint, (u));
- }
- }
- else if (params[0][0] == '#')
- {
- }
- else if (params[0] == "*")
- {
- // Wed Oct 3 15:40:27 2012: S[14] O :20D METADATA * modules :-m_svstopic.so
-
- if (params[1].equals_cs("modules") && !params[2].empty())
- {
- // only interested when it comes from our uplink
- Server* server = source.GetServer();
- if (!server || server->GetUplink() != Me)
- return;
-
- bool plus = (params[2][0] == '+');
- if (!plus && params[2][0] != '-')
- return;
-
- bool required = false;
- Anope::string capab, module = params[2].substr(1);
-
- if (module.equals_cs("m_services_account.so"))
- required = true;
- else if (module.equals_cs("m_hidechans.so"))
- required = true;
- else if (module.equals_cs("m_chghost.so"))
- capab = "CHGHOST";
- else if (module.equals_cs("m_chgident.so"))
- capab = "CHGIDENT";
- else if (module.equals_cs("m_svshold.so"))
- capab = "SVSHOLD";
- else if (module.equals_cs("m_rline.so"))
- capab = "RLINE";
- else if (module.equals_cs("m_topiclock.so"))
- capab = "TOPICLOCK";
- else
- return;
-
- if (required)
- {
- if (!plus)
- Log() << "Warning: InspIRCd unloaded module " << module << ", Anope won't function correctly without it";
- }
- else
- {
- if (plus)
- Servers::Capab.insert(capab);
- else
- Servers::Capab.erase(capab);
-
- Log() << "InspIRCd " << (plus ? "loaded" : "unloaded") << " module " << module << ", adjusted functionality";
- }
-
- }
- }
- }
-};
-
-struct IRCDMessageMode : IRCDMessage
-{
- IRCDMessageMode(Module *creator) : IRCDMessage(creator, "MODE", 2) { SetFlag(IRCDMESSAGE_SOFT_LIMIT); }
-
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (IRCD->IsChannelValid(params[0]))
- {
- Channel *c = Channel::Find(params[0]);
-
- Anope::string modes = params[1];
- for (unsigned n = 2; n < params.size(); ++n)
- modes += " " + params[n];
-
- if (c)
- c->SetModesInternal(source, modes);
- }
- else
- {
- /* InspIRCd lets opers change another
- users modes, we have to kludge this
- as it slightly breaks RFC1459
- */
- User *u = source.GetUser();
- // This can happen with server-origin modes.
- if (!u)
- u = User::Find(params[0]);
- // if it's still null, drop it like fire.
- // most likely situation was that server introduced a nick which we subsequently akilled
- if (u)
- u->SetModesInternal(source, "%s", params[1].c_str());
- }
- }
-};
-
-struct IRCDMessageNick : IRCDMessage
-{
- IRCDMessageNick(Module *creator) : IRCDMessage(creator, "NICK", 2) { SetFlag(IRCDMESSAGE_REQUIRE_USER); }
-
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- source.GetUser()->ChangeNick(params[0]);
- }
-};
-
-struct IRCDMessageOperType : IRCDMessage
-{
- IRCDMessageOperType(Module *creator) : IRCDMessage(creator, "OPERTYPE", 0) { SetFlag(IRCDMESSAGE_SOFT_LIMIT); SetFlag(IRCDMESSAGE_REQUIRE_USER); }
-
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- /* opertype is equivalent to mode +o because servers
- don't do this directly */
- User *u = source.GetUser();
- if (!u->HasMode("OPER"))
- u->SetModesInternal(source, "+o");
- }
-};
-
-struct IRCDMessageRSQuit : IRCDMessage
-{
- IRCDMessageRSQuit(Module *creator) : IRCDMessage(creator, "RSQUIT", 1) { SetFlag(IRCDMESSAGE_SOFT_LIMIT); }
-
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- Server *s = Server::Find(params[0]);
- const Anope::string &reason = params.size() > 1 ? params[1] : "";
- if (!s)
- return;
-
- UplinkSocket::Message(Me) << "SQUIT " << s->GetSID() << " :" << reason;
- s->Delete(s->GetName() + " " + s->GetUplink()->GetName());
- }
-};
-
-struct IRCDMessageSetIdent : IRCDMessage
-{
- IRCDMessageSetIdent(Module *creator) : IRCDMessage(creator, "SETIDENT", 0) { SetFlag(IRCDMESSAGE_REQUIRE_USER); }
-
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- source.GetUser()->SetIdent(params[0]);
- }
-};
-
-struct IRCDMessageServer : IRCDMessage
-{
- IRCDMessageServer(Module *creator) : IRCDMessage(creator, "SERVER", 5) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
-
- /*
- * [Nov 04 00:08:46.308435 2009] debug: Received: SERVER irc.inspircd.com pass 0 964 :Testnet Central!
- * 0: name
- * 1: pass
- * 2: hops
- * 3: numeric
- * 4: desc
- */
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- unsigned int hops = Anope::string(params[2]).is_pos_number_only() ? convertTo<unsigned>(params[2]) : 0;
- new Server(source.GetServer() == NULL ? Me : source.GetServer(), params[0], hops, params[4], params[3]);
- }
-};
-
-struct IRCDMessageSQuit : Message::SQuit
-{
- IRCDMessageSQuit(Module *creator) : Message::SQuit(creator) { }
-
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (params[0] == rsquit_id || params[0] == rsquit_server)
- {
- /* squit for a recently squit server, introduce the juped server now */
- Server *s = Server::Find(rsquit_server);
-
- rsquit_id.clear();
- rsquit_server.clear();
-
- if (s && s->IsJuped())
- IRCD->SendServer(s);
- }
- else
- Message::SQuit::Run(source, params);
- }
-};
-
-struct IRCDMessageTime : IRCDMessage
-{
- IRCDMessageTime(Module *creator) : IRCDMessage(creator, "TIME", 2) { }
-
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- UplinkSocket::Message(Me) << "TIME " << source.GetSource() << " " << params[1] << " " << Anope::CurTime;
- }
-};
-
-struct IRCDMessageUID : IRCDMessage
-{
- IRCDMessageUID(Module *creator) : IRCDMessage(creator, "UID", 8) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); SetFlag(IRCDMESSAGE_SOFT_LIMIT); }
-
- /*
- * [Nov 03 22:09:58.176252 2009] debug: Received: :964 UID 964AAAAAC 1225746297 w00t2 localhost testnet.user w00t 127.0.0.1 1225746302 +iosw +ACGJKLNOQcdfgjklnoqtx :Robin Burchell <w00t@inspircd.org>
- * 0: uid
- * 1: ts
- * 2: nick
- * 3: host
- * 4: dhost
- * 5: ident
- * 6: ip
- * 7: signon
- * 8+: modes and params -- IMPORTANT, some modes (e.g. +s) may have parameters. So don't assume a fixed position of realname!
- * last: realname
- */
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- time_t ts = convertTo<time_t>(params[1]);
-
- Anope::string modes = params[8];
- for (unsigned i = 9; i < params.size() - 1; ++i)
- modes += " " + params[i];
-
- NickAlias *na = NULL;
- if (SASL::sasl)
- for (std::list<SASLUser>::iterator it = saslusers.begin(); it != saslusers.end();)
- {
- SASLUser &u = *it;
-
- if (u.created + 30 < Anope::CurTime)
- it = saslusers.erase(it);
- else if (u.uid == params[0])
- {
- na = NickAlias::Find(u.acc);
- it = saslusers.erase(it);
- }
- else
- ++it;
- }
-
- User *u = User::OnIntroduce(params[2], params[5], params[3], params[4], params[6], source.GetServer(), params[params.size() - 1], ts, modes, params[0], na ? *na->nc : NULL);
- if (u)
- u->signon = convertTo<time_t>(params[7]);
- }
-};
-
-class ProtoInspIRCd12 : public Module
-{
- InspIRCd12Proto ircd_proto;
- ExtensibleItem<bool> ssl;
-
- /* Core message handlers */
- Message::Away message_away;
- Message::Error message_error;
- Message::Invite message_invite;
- Message::Join message_join;
- Message::Kick message_kick;
- Message::Kill message_kill;
- Message::MOTD message_motd;
- Message::Notice message_notice;
- Message::Part message_part;
- Message::Ping message_ping;
- Message::Privmsg message_privmsg;
- Message::Quit message_quit;
- Message::Stats message_stats;
- Message::Topic message_topic;
-
- /* Our message handlers */
- IRCDMessageChgIdent message_chgident;
- IRCDMessageChgName message_setname, message_chgname;
- IRCDMessageCapab message_capab;
- IRCDMessageEncap message_encap;
- IRCDMessageEndburst message_endburst;
- IRCDMessageFHost message_fhost, message_sethost;
- IRCDMessageFJoin message_fjoin;
- IRCDMessageFMode message_fmode;
- IRCDMessageFTopic message_ftopic;
- IRCDMessageIdle message_idle;
- IRCDMessageMetadata message_metadata;
- IRCDMessageMode message_mode;
- IRCDMessageNick message_nick;
- IRCDMessageOperType message_opertype;
- IRCDMessageRSQuit message_rsquit;
- IRCDMessageSetIdent message_setident;
- IRCDMessageServer message_server;
- IRCDMessageSQuit message_squit;
- IRCDMessageTime message_time;
- IRCDMessageUID message_uid;
-
- public:
- ProtoInspIRCd12(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, PROTOCOL | VENDOR),
- ircd_proto(this), ssl(this, "ssl"),
- message_away(this), message_error(this), message_invite(this), message_join(this), message_kick(this), message_kill(this),
- message_motd(this), message_notice(this), message_part(this), message_ping(this), message_privmsg(this), message_quit(this),
- message_stats(this), message_topic(this),
-
- message_chgident(this), message_setname(this, "SETNAME"), message_chgname(this, "FNAME"), message_capab(this), message_encap(this),
- message_endburst(this),
- message_fhost(this, "FHOST"), message_sethost(this, "SETHOST"), message_fjoin(this), message_fmode(this), message_ftopic(this),
- message_idle(this), message_metadata(this), message_mode(this), message_nick(this), message_opertype(this), message_rsquit(this),
- message_setident(this), message_server(this), message_squit(this), message_time(this), message_uid(this)
- {
- Servers::Capab.insert("NOQUIT");
- }
-
- void OnUserNickChange(User *u, const Anope::string &) anope_override
- {
- /* InspIRCd 1.2 doesn't set -r on nick change, remove -r here. Note that if we have to set +r later
- * this will cancel out this -r, resulting in no mode changes.
- *
- * Do not set -r if we don't have a NickServ loaded - DP
- */
- BotInfo *NickServ = Config->GetClient("NickServ");
- if (NickServ)
- u->RemoveMode(NickServ, "REGISTERED");
- }
-};
-
-MODULE_INIT(ProtoInspIRCd12)
diff --git a/modules/protocol/inspircd20.cpp b/modules/protocol/inspircd20.cpp
index 859bc667f..f8bf9b30e 100644
--- a/modules/protocol/inspircd20.cpp
+++ b/modules/protocol/inspircd20.cpp
@@ -1,23 +1,73 @@
-/* Inspircd 2.0 functions
+/*
+ * Anope IRC Services
*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2005-2016 Anope Team <team@anope.org>
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
*
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "module.h"
-#include "modules/cs_mode.h"
+#include "modules/sasl.h"
+#include "modules/chanserv/mode.h"
+#include "modules/chanserv/set.h"
-static unsigned int spanningtree_proto_ver = 0;
+struct SASLUser
+{
+ Anope::string uid;
+ Anope::string acc;
+ time_t created;
+};
-static ServiceReference<IRCDProto> insp12("IRCDProto", "inspircd12");
+static std::list<SASLUser> saslusers;
+static Anope::string rsquit_server, rsquit_id;
+static unsigned int spanningtree_proto_ver = 0;
class InspIRCd20Proto : public IRCDProto
{
+ private:
+ void SendSVSKillInternal(const MessageSource &source, User *user, const Anope::string &buf) override
+ {
+ IRCDProto::SendSVSKillInternal(source, user, buf);
+ user->KillInternal(source, buf);
+ }
+
+ void SendChgIdentInternal(const Anope::string &nick, const Anope::string &vIdent)
+ {
+ if (!Servers::Capab.count("CHGIDENT"))
+ Log() << "CHGIDENT not loaded!";
+ else
+ Uplink::Send(Me, "CHGIDENT", nick, vIdent);
+ }
+
+ void SendChgHostInternal(const Anope::string &nick, const Anope::string &vhost)
+ {
+ if (!Servers::Capab.count("CHGHOST"))
+ Log() << "CHGHOST not loaded!";
+ else
+ Uplink::Send(Me, "CHGHOST", nick, vhost);
+ }
+
+ void SendAddLine(const Anope::string &xtype, const Anope::string &mask, time_t duration, const Anope::string &addedby, const Anope::string &reason)
+ {
+ Uplink::Send(Me, "ADDLINE", xtype, mask, addedby, Anope::CurTime, duration, reason);
+ }
+
+ void SendDelLine(const Anope::string &xtype, const Anope::string &mask)
+ {
+ Uplink::Send(Me, "DELLINE", xtype, mask);
+ }
+
public:
InspIRCd20Proto(Module *creator) : IRCDProto(creator, "InspIRCd 2.0")
{
@@ -34,70 +84,390 @@ class InspIRCd20Proto : public IRCDProto
MaxModes = 20;
}
- void SendConnect() anope_override
+ void SendConnect() override
+ {
+ Uplink::Send("CAPAB START 1202");
+ Uplink::Send("CAPAB CAPABILITIES :PROTOCOL=1202");
+ Uplink::Send("CAPAB END");
+ SendServer(Me);
+ }
+
+ void SendGlobalNotice(ServiceBot *bi, const Server *dest, const Anope::string &msg) override
+ {
+ Uplink::Send(bi, "NOTICE", "$" + dest->GetName(), msg);
+ }
+
+ void SendGlobalPrivmsg(ServiceBot *bi, const Server *dest, const Anope::string &msg) override
{
- UplinkSocket::Message() << "CAPAB START 1202";
- UplinkSocket::Message() << "CAPAB CAPABILITIES :PROTOCOL=1202";
- UplinkSocket::Message() << "CAPAB END";
- insp12->SendConnect();
+ Uplink::Send(bi, "PRIVMSG", "$" + dest->GetName(), msg);
}
- void SendSASLMechanisms(std::vector<Anope::string> &mechanisms) anope_override
+ void SendAkillDel(XLine *x) override
+ {
+ /* InspIRCd may support regex bans */
+ if (x->IsRegex() && Servers::Capab.count("RLINE"))
+ {
+ Anope::string mask = x->GetMask();
+ size_t h = mask.find('#');
+ if (h != Anope::string::npos)
+ mask = mask.replace(h, 1, ' ');
+ SendDelLine("R", mask);
+ return;
+ }
+ else if (x->IsRegex() || x->HasNickOrReal())
+ return;
+
+ /* ZLine if we can instead */
+ if (x->GetUser() == "*")
+ {
+ cidr addr(x->GetHost());
+ if (addr.valid())
+ {
+ IRCD->SendSZLineDel(x);
+ return;
+ }
+ }
+
+ SendDelLine("G", x->GetUser() + "@" + x->GetHost());
+ }
+
+ void SendTopic(const MessageSource &source, Channel *c) override
+ {
+ if (Servers::Capab.count("SVSTOPIC"))
+ {
+ Uplink::Send(c->ci->WhoSends(), "SVSTOPIC", c->name, c->topic_ts, c->topic_setter, c->topic);
+ }
+ else
+ {
+ /* If the last time a topic was set is after the TS we want for this topic we must bump this topic's timestamp to now */
+ time_t ts = c->topic_ts;
+ if (c->topic_time > ts)
+ ts = Anope::CurTime;
+ /* But don't modify c->topic_ts, it should remain set to the real TS we want as ci->last_topic_time pulls from it */
+ Uplink::Send(source, "FTOPIC", c->name, ts, c->topic_setter, c->topic);
+ }
+ }
+
+ void SendVhostDel(User *u) override
+ {
+ if (u->HasMode("CLOAK"))
+ this->SendChgHostInternal(u->nick, u->chost);
+ else
+ this->SendChgHostInternal(u->nick, u->host);
+
+ if (Servers::Capab.count("CHGIDENT") && u->GetIdent() != u->GetVIdent())
+ this->SendChgIdentInternal(u->nick, u->GetIdent());
+ }
+
+ void SendAkill(User *u, XLine *x) override
+ {
+ // Calculate the time left before this would expire, capping it at 2 days
+ time_t timeleft = x->GetExpires() - Anope::CurTime;
+ if (timeleft > 172800 || !x->GetExpires())
+ timeleft = 172800;
+
+ /* InspIRCd may support regex bans, if they do we can send this and forget about it */
+ if (x->IsRegex() && Servers::Capab.count("RLINE"))
+ {
+ Anope::string mask = x->GetMask();
+ size_t h = mask.find('#');
+ if (h != Anope::string::npos)
+ mask = mask.replace(h, 1, ' ');
+ SendAddLine("R", mask, timeleft, x->GetBy(), x->GetReason());
+ return;
+ }
+ else if (x->IsRegex() || x->HasNickOrReal())
+ {
+ if (!u)
+ {
+ /* No user (this akill was just added), and contains nick and/or realname. Find users that match and ban them */
+ for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it)
+ if (x->GetManager()->Check(it->second, x))
+ this->SendAkill(it->second, x);
+ return;
+ }
+
+ XLine *old = x;
+
+ if (old->GetManager()->HasEntry("*@" + u->host))
+ return;
+
+ /* We can't akill x as it has a nick and/or realname included, so create a new akill for *@host */
+ x = Serialize::New<XLine *>();
+ x->SetMask("*@" + u->host);
+ x->SetBy(old->GetBy());
+ x->SetExpires(old->GetExpires());
+ x->SetReason(old->GetReason());
+ old->GetManager()->AddXLine(x);
+
+ Log(Config->GetClient("OperServ"), "akill") << "AKILL: Added an akill for " << x->GetMask() << " because " << u->GetMask() << "#" << u->realname << " matches " << old->GetMask();
+ }
+
+ /* ZLine if we can instead */
+ if (x->GetUser() == "*")
+ {
+ cidr addr(x->GetHost());
+ if (addr.valid())
+ {
+ IRCD->SendSZLine(u, x);
+ return;
+ }
+ }
+
+ SendAddLine("G", x->GetUser() + "@" + x->GetHost(), timeleft, x->GetBy(), x->GetReason());
+ }
+
+ void SendNumericInternal(int numeric, const Anope::string &dest, const Anope::string &buf) override
+ {
+ User *u = User::Find(dest);
+ Uplink::Send("PUSH", dest, ":" + Me->GetName() + " " + numeric + " " + (u ? u->nick : dest) + " " + buf);
+ }
+
+ void SendModeInternal(const MessageSource &source, const Channel *dest, const Anope::string &buf) override
+ {
+ IRCMessage message(source, "FMODE", dest->name, dest->creation_time);
+ message.TokenizeAndPush(buf);
+ Uplink::SendMessage(message);
+ }
+
+ void SendClientIntroduction(User *u) override
+ {
+ Anope::string modes = "+" + u->GetModes();
+ Uplink::Send(Me, "UID", u->GetUID(), u->timestamp, u->nick, u->host, u->host, u->GetIdent(), "0.0.0.0", u->timestamp, modes, u->realname);
+ if (modes.find('o') != Anope::string::npos)
+ Uplink::Send(u, "OPERTYPE", "services");
+ }
+
+ /* SERVER services-dev.chatspike.net password 0 :Description here */
+ void SendServer(const Server *server) override
+ {
+ /* if rsquit is set then we are waiting on a squit */
+ if (rsquit_id.empty() && rsquit_server.empty())
+ Uplink::Send("SERVER", server->GetName(), Config->Uplinks[Anope::CurrentUplink].password, server->GetHops(), server->GetSID(), server->GetDescription());
+ }
+
+ void SendSquit(Server *s, const Anope::string &message) override
+ {
+ if (s != Me)
+ {
+ rsquit_id = s->GetSID();
+ rsquit_server = s->GetName();
+
+ Uplink::Send("RSQUIT", s->GetName(), message);
+ }
+ else
+ {
+ Uplink::Send("SQUIT", s->GetName(), message);
+ }
+ }
+
+ /* JOIN */
+ void SendJoin(User *user, Channel *c, const ChannelStatus *status) override
+ {
+ Uplink::Send(Me, "FJOIN", c->name, c->creation_time, "+" + c->GetModes(true, true), "," + user->GetUID());
+
+ /* Note that we can send this with the FJOIN but choose not to
+ * because the mode stacker will handle this and probably will
+ * merge these modes with +nrt and other mlocked modes
+ */
+ if (status)
+ {
+ /* First save the channel status incase uc->Status == status */
+ ChannelStatus cs = *status;
+ /* If the user is internally on the channel with flags, kill them so that
+ * the stacker will allow this.
+ */
+ ChanUserContainer *uc = c->FindUser(user);
+ if (uc != NULL)
+ uc->status.Clear();
+
+ ServiceBot *setter = ServiceBot::Find(user->nick);
+ for (size_t i = 0; i < cs.Modes().length(); ++i)
+ c->SetMode(setter, ModeManager::FindChannelModeByChar(cs.Modes()[i]), user->GetUID(), false);
+
+ if (uc != NULL)
+ uc->status = cs;
+ }
+ }
+
+ /* UNSQLINE */
+ void SendSQLineDel(XLine *x) override
+ {
+ SendDelLine("Q", x->GetMask());
+ }
+
+ /* SQLINE */
+ void SendSQLine(User *, XLine *x) override
+ {
+ // Calculate the time left before this would expire, capping it at 2 days
+ time_t timeleft = x->GetExpires() - Anope::CurTime;
+ if (timeleft > 172800 || !x->GetExpires())
+ timeleft = 172800;
+ SendAddLine("Q", x->GetMask(), timeleft, x->GetBy(), x->GetReason());
+ }
+
+ void SendVhost(User *u, const Anope::string &vIdent, const Anope::string &vhost) override
+ {
+ if (!vIdent.empty())
+ this->SendChgIdentInternal(u->nick, vIdent);
+ if (!vhost.empty())
+ this->SendChgHostInternal(u->nick, vhost);
+ }
+
+ /* SVSHOLD - set */
+ void SendSVSHold(const Anope::string &nick, time_t t) override
+ {
+ Uplink::Send(Config->GetClient("NickServ"), "SVSHOLD", nick, t, "Being held for registered user");
+ }
+
+ /* SVSHOLD - release */
+ void SendSVSHoldDel(const Anope::string &nick) override
+ {
+ Uplink::Send(Config->GetClient("NickServ"), "SVSHOLD", nick);
+ }
+
+ /* UNSZLINE */
+ void SendSZLineDel(XLine *x) override
+ {
+ SendDelLine("Z", x->GetHost());
+ }
+
+ /* SZLINE */
+ void SendSZLine(User *, XLine *x) override
+ {
+ // Calculate the time left before this would expire, capping it at 2 days
+ time_t timeleft = x->GetExpires() - Anope::CurTime;
+ if (timeleft > 172800 || !x->GetExpires())
+ timeleft = 172800;
+ SendAddLine("Z", x->GetHost(), timeleft, x->GetBy(), x->GetReason());
+ }
+
+ void SendSVSJoin(const MessageSource &source, User *u, const Anope::string &chan, const Anope::string &) override
+ {
+ Uplink::Send(source, "SVSJOIN", u->GetUID(), chan);
+ }
+
+ void SendSVSPart(const MessageSource &source, User *u, const Anope::string &chan, const Anope::string &param) override
+ {
+ if (!param.empty())
+ Uplink::Send(source, "SVSPART", u->GetUID(), chan, param);
+ else
+ Uplink::Send(source, "SVSPART", u->GetUID(), chan);
+ }
+
+ void SendSWhois(const MessageSource &, const Anope::string &who, const Anope::string &mask) override
+ {
+ User *u = User::Find(who);
+
+ Uplink::Send(Me, "METADATA", u->GetUID(), "swhois", mask);
+ }
+
+ void SendBOB() override
+ {
+ Uplink::Send(Me, "BURST", Anope::CurTime);
+ Module *enc = ModuleManager::FindFirstOf(ENCRYPTION);
+ Uplink::Send(Me, "VERSION", "Anope-" + Anope::Version() + " " + Me->GetName() + " :" + IRCD->GetProtocolName() + " - (" + (enc ? enc->name : "none") + ") -- " + Anope::VersionBuildString());
+ }
+
+ void SendEOB() override
+ {
+ Uplink::Send(Me, "ENDBURST");
+ }
+
+ void SendGlobopsInternal(const MessageSource &source, const Anope::string &buf) override
+ {
+ if (Servers::Capab.count("GLOBOPS"))
+ Uplink::Send(source, "SNONOTICE", "g", buf);
+ else
+ Uplink::Send(source, "SNONOTICE", "A", buf);
+ }
+
+ void SendLogin(User *u, NickServ::Nick *na) override
+ {
+ /* InspIRCd uses an account to bypass chmode +R, not umode +r, so we can't send this here */
+ if (na->GetAccount()->HasFieldS("UNCONFIRMED"))
+ return;
+
+ Uplink::Send(Me, "METADATA", u->GetUID(), "accountname", na->GetAccount()->GetDisplay());
+ }
+
+ void SendLogout(User *u) override
+ {
+ Uplink::Send(Me, "METADATA", u->GetUID(), "accountname", "");
+ }
+
+ void SendChannel(Channel *c) override
+ {
+ Uplink::Send(Me, "FJOIN", c->name, c->creation_time, "+" + c->GetModes(true, true), "");
+ }
+
+ void SendSASLMechanisms(std::vector<Anope::string> &mechanisms) override
{
Anope::string mechlist;
for (unsigned i = 0; i < mechanisms.size(); ++i)
mechlist += "," + mechanisms[i];
- UplinkSocket::Message(Me) << "METADATA * saslmechlist :" << (mechanisms.empty() ? "" : mechlist.substr(1));
- }
-
- void SendSVSKillInternal(const MessageSource &source, User *user, const Anope::string &buf) anope_override { insp12->SendSVSKillInternal(source, user, buf); }
- void SendGlobalNotice(BotInfo *bi, const Server *dest, const Anope::string &msg) anope_override { insp12->SendGlobalNotice(bi, dest, msg); }
- void SendGlobalPrivmsg(BotInfo *bi, const Server *dest, const Anope::string &msg) anope_override { insp12->SendGlobalPrivmsg(bi, dest, msg); }
- void SendAkillDel(const XLine *x) anope_override { insp12->SendAkillDel(x); }
- void SendTopic(const MessageSource &whosets, Channel *c) anope_override { insp12->SendTopic(whosets, c); };
- void SendVhostDel(User *u) anope_override { insp12->SendVhostDel(u); }
- void SendAkill(User *u, XLine *x) anope_override { insp12->SendAkill(u, x); }
- void SendNumericInternal(int numeric, const Anope::string &dest, const Anope::string &buf) anope_override { insp12->SendNumericInternal(numeric, dest, buf); }
- void SendModeInternal(const MessageSource &source, const Channel *dest, const Anope::string &buf) anope_override { insp12->SendModeInternal(source, dest, buf); }
- void SendClientIntroduction(User *u) anope_override { insp12->SendClientIntroduction(u); }
- void SendServer(const Server *server) anope_override { insp12->SendServer(server); }
- void SendSquit(Server *s, const Anope::string &message) anope_override { insp12->SendSquit(s, message); }
- void SendJoin(User *user, Channel *c, const ChannelStatus *status) anope_override { insp12->SendJoin(user, c, status); }
- void SendSQLineDel(const XLine *x) anope_override { insp12->SendSQLineDel(x); }
- void SendSQLine(User *u, const XLine *x) anope_override { insp12->SendSQLine(u, x); }
- void SendVhost(User *u, const Anope::string &vident, const Anope::string &vhost) anope_override { insp12->SendVhost(u, vident, vhost); }
- void SendSVSHold(const Anope::string &nick, time_t t) anope_override { insp12->SendSVSHold(nick, t); }
- void SendSVSHoldDel(const Anope::string &nick) anope_override { insp12->SendSVSHoldDel(nick); }
- void SendSZLineDel(const XLine *x) anope_override { insp12->SendSZLineDel(x); }
- void SendSZLine(User *u, const XLine *x) anope_override { insp12->SendSZLine(u, x); }
- void SendSVSJoin(const MessageSource &source, User *u, const Anope::string &chan, const Anope::string &other) anope_override { insp12->SendSVSJoin(source, u, chan, other); }
- void SendSVSPart(const MessageSource &source, User *u, const Anope::string &chan, const Anope::string &param) anope_override { insp12->SendSVSPart(source, u, chan, param); }
- void SendSWhois(const MessageSource &bi, const Anope::string &who, const Anope::string &mask) anope_override { insp12->SendSWhois(bi, who, mask); }
- void SendBOB() anope_override { insp12->SendBOB(); }
- void SendEOB() anope_override { insp12->SendEOB(); }
- void SendGlobopsInternal(const MessageSource &source, const Anope::string &buf) { insp12->SendGlobopsInternal(source, buf); }
- void SendLogin(User *u, NickAlias *na) anope_override { insp12->SendLogin(u, na); }
- void SendLogout(User *u) anope_override { insp12->SendLogout(u); }
- void SendChannel(Channel *c) anope_override { insp12->SendChannel(c); }
- void SendSASLMessage(const SASL::Message &message) anope_override { insp12->SendSASLMessage(message); }
- void SendSVSLogin(const Anope::string &uid, const Anope::string &acc, const Anope::string &vident, const Anope::string &vhost) anope_override { insp12->SendSVSLogin(uid, acc, vident, vhost); }
- bool IsExtbanValid(const Anope::string &mask) anope_override { return insp12->IsExtbanValid(mask); }
- bool IsIdentValid(const Anope::string &ident) anope_override { return insp12->IsIdentValid(ident); }
-};
+ Uplink::Send(Me, "METADATA", "*", "saslmechlist", mechlist.empty() ? "" : mechlist.substr(1));
+ }
-class InspIRCdAutoOpMode : public ChannelModeList
-{
- public:
- InspIRCdAutoOpMode(char mode) : ChannelModeList("AUTOOP", mode)
+ void SendSASLMessage(const SASL::Message &message) override
{
+ if (!message.ext.empty())
+ Uplink::Send(Me, "ENCAP", message.target.substr(0, 3), "SASL",
+ message.source, message.target,
+ message.type, message.data, message.ext);
+ else
+ Uplink::Send(Me, "ENCAP", message.target.substr(0, 3), "SASL",
+ message.source, message.target,
+ message.type, message.data);
}
- bool IsValid(Anope::string &mask) const anope_override
+ void SendSVSLogin(const Anope::string &uid, const Anope::string &acc, const Anope::string &vident, const Anope::string &vhost) override
{
- // We can not validate this because we don't know about the
- // privileges of the setter so just reject attempts to set it.
- return false;
+ Uplink::Send(Me, "METADATA", uid, "accountname", acc);
+
+ SASLUser su;
+ su.uid = uid;
+ su.acc = acc;
+ su.created = Anope::CurTime;
+
+ for (std::list<SASLUser>::iterator it = saslusers.begin(); it != saslusers.end();)
+ {
+ SASLUser &u = *it;
+
+ if (u.created + 30 < Anope::CurTime || u.uid == uid)
+ it = saslusers.erase(it);
+ else
+ ++it;
+ }
+
+ saslusers.push_back(su);
+ }
+
+ bool IsExtbanValid(const Anope::string &mask) override
+ {
+ return mask.length() >= 3 && mask[1] == ':';
+ }
+
+ bool IsIdentValid(const Anope::string &ident) override
+ {
+ if (ident.empty() || ident.length() > Config->GetBlock("networkinfo")->Get<unsigned>("userlen"))
+ return false;
+
+ for (unsigned i = 0; i < ident.length(); ++i)
+ {
+ const char &c = ident[i];
+
+ if (c >= 'A' && c <= '}')
+ continue;
+
+ if ((c >= '0' && c <= '9') || c == '-' || c == '.')
+ continue;
+
+ return false;
+ }
+
+ return true;
}
};
@@ -111,13 +481,13 @@ class InspIRCdExtBan : public ChannelModeVirtual<ChannelModeList>
{
}
- ChannelMode *Wrap(Anope::string &param) anope_override
+ ChannelMode *Wrap(Anope::string &param) override
{
param = Anope::string(ext) + ":" + param;
return ChannelModeVirtual<ChannelModeList>::Wrap(param);
}
- ChannelMode *Unwrap(ChannelMode *cm, Anope::string &param) anope_override
+ ChannelMode *Unwrap(ChannelMode *cm, Anope::string &param) override
{
if (cm->type != MODE_LIST || param.length() < 3 || param[0] != ext || param[1] != ':')
return cm;
@@ -136,7 +506,7 @@ namespace InspIRCdExtban
{
}
- bool Matches(User *u, const Entry *e) anope_override
+ bool Matches(User *u, const Entry *e) override
{
const Anope::string &mask = e->GetMask();
Anope::string real_mask = mask.substr(3);
@@ -152,7 +522,7 @@ namespace InspIRCdExtban
{
}
- bool Matches(User *u, const Entry *e) anope_override
+ bool Matches(User *u, const Entry *e) override
{
const Anope::string &mask = e->GetMask();
@@ -188,12 +558,12 @@ namespace InspIRCdExtban
{
}
- bool Matches(User *u, const Entry *e) anope_override
+ bool Matches(User *u, const Entry *e) override
{
const Anope::string &mask = e->GetMask();
Anope::string real_mask = mask.substr(2);
- return u->IsIdentified() && real_mask.equals_ci(u->Account()->display);
+ return u->IsIdentified() && real_mask.equals_ci(u->Account()->GetDisplay());
}
};
@@ -204,7 +574,7 @@ namespace InspIRCdExtban
{
}
- bool Matches(User *u, const Entry *e) anope_override
+ bool Matches(User *u, const Entry *e) override
{
const Anope::string &mask = e->GetMask();
Anope::string real_mask = mask.substr(2);
@@ -219,7 +589,7 @@ namespace InspIRCdExtban
{
}
- bool Matches(User *u, const Entry *e) anope_override
+ bool Matches(User *u, const Entry *e) override
{
const Anope::string &mask = e->GetMask();
Anope::string real_mask = mask.substr(2);
@@ -227,14 +597,14 @@ namespace InspIRCdExtban
}
};
- class FinerprintMatcher : public InspIRCdExtBan
+ class FingerprintMatcher : public InspIRCdExtBan
{
public:
- FinerprintMatcher(const Anope::string &mname, const Anope::string &mbase, char c) : InspIRCdExtBan(mname, mbase, c)
+ FingerprintMatcher(const Anope::string &mname, const Anope::string &mbase, char c) : InspIRCdExtBan(mname, mbase, c)
{
}
- bool Matches(User *u, const Entry *e) anope_override
+ bool Matches(User *u, const Entry *e) override
{
const Anope::string &mask = e->GetMask();
Anope::string real_mask = mask.substr(2);
@@ -249,7 +619,7 @@ namespace InspIRCdExtban
{
}
- bool Matches(User *u, const Entry *e) anope_override
+ bool Matches(User *u, const Entry *e) override
{
const Anope::string &mask = e->GetMask();
Anope::string real_mask = mask.substr(2);
@@ -258,124 +628,11 @@ namespace InspIRCdExtban
};
}
-class ColonDelimitedParamMode : public ChannelModeParam
-{
- public:
- ColonDelimitedParamMode(const Anope::string &modename, char modeChar) : ChannelModeParam(modename, modeChar, true) { }
-
- bool IsValid(Anope::string &value) const anope_override
- {
- return IsValid(value, false);
- }
-
- bool IsValid(const Anope::string &value, bool historymode) const
- {
- if (value.empty())
- return false; // empty param is never valid
-
- Anope::string::size_type pos = value.find(':');
- if ((pos == Anope::string::npos) || (pos == 0))
- return false; // no ':' or it's the first char, both are invalid
-
- Anope::string rest;
- try
- {
- if (convertTo<int>(value, rest, false) <= 0)
- return false; // negative numbers and zero are invalid
-
- rest = rest.substr(1);
- int n;
- if (historymode)
- {
- // For the history mode, the part after the ':' is a duration and it
- // can be in the user friendly "1d3h20m" format, make sure we accept that
- n = Anope::DoTime(rest);
- }
- else
- n = convertTo<int>(rest);
-
- if (n <= 0)
- return false;
- }
- catch (const ConvertException &e)
- {
- // conversion error, invalid
- return false;
- }
-
- return true;
- }
-};
-
-class SimpleNumberParamMode : public ChannelModeParam
-{
- public:
- SimpleNumberParamMode(const Anope::string &modename, char modeChar) : ChannelModeParam(modename, modeChar, true) { }
-
- bool IsValid(Anope::string &value) const anope_override
- {
- if (value.empty())
- return false; // empty param is never valid
-
- try
- {
- if (convertTo<int>(value) <= 0)
- return false;
- }
- catch (const ConvertException &e)
- {
- // conversion error, invalid
- return false;
- }
-
- return true;
- }
-};
-
-class ChannelModeFlood : public ColonDelimitedParamMode
-{
- public:
- ChannelModeFlood(char modeChar) : ColonDelimitedParamMode("FLOOD", modeChar) { }
-
- bool IsValid(Anope::string &value) const anope_override
- {
- // The parameter of this mode is a bit different, it may begin with a '*',
- // ignore it if that's the case
- Anope::string v = value[0] == '*' ? value.substr(1) : value;
- return ((!value.empty()) && (ColonDelimitedParamMode::IsValid(v)));
- }
-};
-
-class ChannelModeHistory : public ColonDelimitedParamMode
-{
- public:
- ChannelModeHistory(char modeChar) : ColonDelimitedParamMode("HISTORY", modeChar) { }
-
- bool IsValid(Anope::string &value) const anope_override
- {
- return (ColonDelimitedParamMode::IsValid(value, true));
- }
-};
-
-class ChannelModeRedirect : public ChannelModeParam
-{
- public:
- ChannelModeRedirect(char modeChar) : ChannelModeParam("REDIRECT", modeChar, true) { }
-
- bool IsValid(Anope::string &value) const anope_override
- {
- // The parameter of this mode is a channel, and channel names start with '#'
- return ((!value.empty()) && (value[0] == '#'));
- }
-};
-
struct IRCDMessageCapab : Message::Capab
{
- std::map<char, Anope::string> chmodes, umodes;
-
IRCDMessageCapab(Module *creator) : Message::Capab(creator, "CAPAB") { SetFlag(IRCDMESSAGE_SOFT_LIMIT); }
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
{
if (params[0].equals_cs("START"))
{
@@ -384,15 +641,13 @@ struct IRCDMessageCapab : Message::Capab
if (spanningtree_proto_ver < 1202)
{
- UplinkSocket::Message() << "ERROR :Protocol mismatch, no or invalid protocol version given in CAPAB START";
+ Uplink::Send("ERROR", "Protocol mismatch, no or invalid protocol version given in CAPAB START");
Anope::QuitReason = "Protocol mismatch, no or invalid protocol version given in CAPAB START";
Anope::Quitting = true;
return;
}
/* reset CAPAB */
- chmodes.clear();
- umodes.clear();
Servers::Capab.insert("SERVERS");
Servers::Capab.insert("CHGHOST");
Servers::Capab.insert("CHGIDENT");
@@ -406,132 +661,35 @@ struct IRCDMessageCapab : Message::Capab
while (ssep.GetToken(capab))
{
+ if (capab.find('=') == Anope::string::npos)
+ continue;
+
Anope::string modename = capab.substr(0, capab.find('='));
Anope::string modechar = capab.substr(capab.find('=') + 1);
- ChannelMode *cm = NULL;
+ char symbol = 0;
- if (modename.equals_cs("admin"))
- cm = new ChannelModeStatus("PROTECT", modechar.length() > 1 ? modechar[1] : modechar[0], modechar.length() > 1 ? modechar[0] : 0, 3);
- else if (modename.equals_cs("allowinvite"))
- {
- cm = new ChannelMode("ALLINVITE", modechar[0]);
- ModeManager::AddChannelMode(new InspIRCdExtban::EntryMatcher("INVITEBAN", "BAN", 'A'));
- }
- else if (modename.equals_cs("auditorium"))
- cm = new ChannelMode("AUDITORIUM", modechar[0]);
- else if (modename.equals_cs("autoop"))
- cm = new InspIRCdAutoOpMode(modechar[0]);
- else if (modename.equals_cs("ban"))
- cm = new ChannelModeList("BAN", modechar[0]);
- else if (modename.equals_cs("banexception"))
- cm = new ChannelModeList("EXCEPT", modechar[0]);
- else if (modename.equals_cs("blockcaps"))
- {
- cm = new ChannelMode("BLOCKCAPS", modechar[0]);
- ModeManager::AddChannelMode(new InspIRCdExtban::EntryMatcher("BLOCKCAPSBAN", "BAN", 'B'));
- }
- else if (modename.equals_cs("blockcolor"))
- {
- cm = new ChannelMode("BLOCKCOLOR", modechar[0]);
- ModeManager::AddChannelMode(new InspIRCdExtban::EntryMatcher("BLOCKCOLORBAN", "BAN", 'c'));
- }
- else if (modename.equals_cs("c_registered"))
- cm = new ChannelModeNoone("REGISTERED", modechar[0]);
- else if (modename.equals_cs("censor"))
- cm = new ChannelMode("CENSOR", modechar[0]);
- else if (modename.equals_cs("delayjoin"))
- cm = new ChannelMode("DELAYEDJOIN", modechar[0]);
- else if (modename.equals_cs("delaymsg"))
- cm = new SimpleNumberParamMode("DELAYMSG", modechar[0]);
- else if (modename.equals_cs("filter"))
- cm = new ChannelModeList("FILTER", modechar[0]);
- else if (modename.equals_cs("flood"))
- cm = new ChannelModeFlood(modechar[0]);
- else if (modename.equals_cs("founder"))
- cm = new ChannelModeStatus("OWNER", modechar.length() > 1 ? modechar[1] : modechar[0], modechar.length() > 1 ? modechar[0] : 0, 4);
- else if (modename.equals_cs("halfop"))
- cm = new ChannelModeStatus("HALFOP", modechar.length() > 1 ? modechar[1] : modechar[0], modechar.length() > 1 ? modechar[0] : 0, 1);
- else if (modename.equals_cs("history"))
- cm = new ChannelModeHistory(modechar[0]);
- else if (modename.equals_cs("invex"))
- cm = new ChannelModeList("INVITEOVERRIDE", modechar[0]);
- else if (modename.equals_cs("inviteonly"))
- cm = new ChannelMode("INVITE", modechar[0]);
- else if (modename.equals_cs("joinflood"))
- cm = new ColonDelimitedParamMode("JOINFLOOD", modechar[0]);
- else if (modename.equals_cs("key"))
- cm = new ChannelModeKey(modechar[0]);
- else if (modename.equals_cs("kicknorejoin"))
- cm = new SimpleNumberParamMode("NOREJOIN", modechar[0]);
- else if (modename.equals_cs("limit"))
- cm = new ChannelModeParam("LIMIT", modechar[0], true);
- else if (modename.equals_cs("moderated"))
- cm = new ChannelMode("MODERATED", modechar[0]);
- else if (modename.equals_cs("nickflood"))
- cm = new ColonDelimitedParamMode("NICKFLOOD", modechar[0]);
- else if (modename.equals_cs("noctcp"))
- {
- cm = new ChannelMode("NOCTCP", modechar[0]);
- ModeManager::AddChannelMode(new InspIRCdExtban::EntryMatcher("NOCTCPBAN", "BAN", 'C'));
- }
- else if (modename.equals_cs("noextmsg"))
- cm = new ChannelMode("NOEXTERNAL", modechar[0]);
- else if (modename.equals_cs("nokick"))
- {
- cm = new ChannelMode("NOKICK", modechar[0]);
- ModeManager::AddChannelMode(new InspIRCdExtban::EntryMatcher("NOKICKBAN", "BAN", 'Q'));
- }
- else if (modename.equals_cs("noknock"))
- cm = new ChannelMode("NOKNOCK", modechar[0]);
- else if (modename.equals_cs("nonick"))
- {
- cm = new ChannelMode("NONICK", modechar[0]);
- ModeManager::AddChannelMode(new InspIRCdExtban::EntryMatcher("NONICKBAN", "BAN", 'N'));
- }
- else if (modename.equals_cs("nonotice"))
+ if (modechar.empty())
+ continue;
+
+ if (modechar.length() == 2)
{
- cm = new ChannelMode("NONOTICE", modechar[0]);
- ModeManager::AddChannelMode(new InspIRCdExtban::EntryMatcher("NONOTICEBAN", "BAN", 'T'));
+ symbol = modechar[0];
+ modechar = modechar.substr(1);
}
- else if (modename.equals_cs("op"))
- cm = new ChannelModeStatus("OP", modechar.length() > 1 ? modechar[1] : modechar[0], modechar.length() > 1 ? modechar[0] : 0, 2);
- else if (modename.equals_cs("operonly"))
- cm = new ChannelModeOperOnly("OPERONLY", modechar[0]);
- else if (modename.equals_cs("permanent"))
- cm = new ChannelMode("PERM", modechar[0]);
- else if (modename.equals_cs("private"))
- cm = new ChannelMode("PRIVATE", modechar[0]);
- else if (modename.equals_cs("redirect"))
- cm = new ChannelModeRedirect(modechar[0]);
- else if (modename.equals_cs("reginvite"))
- cm = new ChannelMode("REGISTEREDONLY", modechar[0]);
- else if (modename.equals_cs("regmoderated"))
- cm = new ChannelMode("REGMODERATED", modechar[0]);
- else if (modename.equals_cs("secret"))
- cm = new ChannelMode("SECRET", modechar[0]);
- else if (modename.equals_cs("sslonly"))
+
+ ChannelMode *cm = ModeManager::FindChannelModeByChar(modechar[0]);
+ if (cm == nullptr)
{
- cm = new ChannelMode("SSL", modechar[0]);
- ModeManager::AddChannelMode(new InspIRCdExtban::FinerprintMatcher("SSLBAN", "BAN", 'z'));
+ Log(this->GetOwner()) << "Warning: Uplink has unknown channel mode " << modename << "=" << modechar;
+ continue;
}
- else if (modename.equals_cs("stripcolor"))
+
+ char modesymbol = cm->type == MODE_STATUS ? (anope_dynamic_static_cast<ChannelModeStatus *>(cm))->symbol : 0;
+ if (symbol != modesymbol)
{
- cm = new ChannelMode("STRIPCOLOR", modechar[0]);
- ModeManager::AddChannelMode(new InspIRCdExtban::EntryMatcher("STRIPCOLORBAN", "BAN", 'S'));
+ Log(this->GetOwner()) << "Warning: Channel mode " << modename << " has a misconfigured status character";
+ continue;
}
- else if (modename.equals_cs("topiclock"))
- cm = new ChannelMode("TOPIC", modechar[0]);
- else if (modename.equals_cs("voice"))
- cm = new ChannelModeStatus("VOICE", modechar.length() > 1 ? modechar[1] : modechar[0], modechar.length() > 1 ? modechar[0] : 0, 0);
- /* Unknown status mode, (customprefix) - add it */
- else if (modechar.length() == 2)
- cm = new ChannelModeStatus(modename.upper(), modechar[1], modechar[0], -1);
- /* Unknown non status mode, add it to our list for later */
- else
- chmodes[modechar[0]] = modename.upper();
-
- if (cm)
- ModeManager::AddChannelMode(cm);
}
}
if (params[0].equals_cs("USERMODES") && params.size() > 1)
@@ -541,54 +699,21 @@ struct IRCDMessageCapab : Message::Capab
while (ssep.GetToken(capab))
{
+ if (capab.find('=') == Anope::string::npos)
+ continue;
+
Anope::string modename = capab.substr(0, capab.find('='));
Anope::string modechar = capab.substr(capab.find('=') + 1);
- UserMode *um = NULL;
-
- if (modename.equals_cs("bot"))
- um = new UserMode("BOT", modechar[0]);
- else if (modename.equals_cs("callerid"))
- um = new UserMode("CALLERID", modechar[0]);
- else if (modename.equals_cs("cloak"))
- um = new UserMode("CLOAK", modechar[0]);
- else if (modename.equals_cs("deaf"))
- um = new UserMode("DEAF", modechar[0]);
- else if (modename.equals_cs("deaf_commonchan"))
- um = new UserMode("COMMONCHANS", modechar[0]);
- else if (modename.equals_cs("helpop"))
- um = new UserModeOperOnly("HELPOP", modechar[0]);
- else if (modename.equals_cs("hidechans"))
- um = new UserMode("PRIV", modechar[0]);
- else if (modename.equals_cs("hideoper"))
- um = new UserModeOperOnly("HIDEOPER", modechar[0]);
- else if (modename.equals_cs("invisible"))
- um = new UserMode("INVIS", modechar[0]);
- else if (modename.equals_cs("invis-oper"))
- um = new UserModeOperOnly("INVISIBLE_OPER", modechar[0]);
- else if (modename.equals_cs("oper"))
- um = new UserModeOperOnly("OPER", modechar[0]);
- else if (modename.equals_cs("regdeaf"))
- um = new UserMode("REGPRIV", modechar[0]);
- else if (modename.equals_cs("servprotect"))
+
+ if (modechar.empty())
+ continue;
+
+ UserMode *um = ModeManager::FindUserModeByChar(modechar[0]);
+ if (um == nullptr)
{
- um = new UserModeNoone("PROTECTED", modechar[0]);
- IRCD->DefaultPseudoclientModes += modechar;
+ Log(this->GetOwner()) << "Warning: Uplink has unknown user mode " << modename << "=" << modechar;
+ continue;
}
- else if (modename.equals_cs("showwhois"))
- um = new UserMode("WHOIS", modechar[0]);
- else if (modename.equals_cs("u_censor"))
- um = new UserMode("CENSOR", modechar[0]);
- else if (modename.equals_cs("u_registered"))
- um = new UserModeNoone("REGISTERED", modechar[0]);
- else if (modename.equals_cs("u_stripcolor"))
- um = new UserMode("STRIPCOLOR", modechar[0]);
- else if (modename.equals_cs("wallops"))
- um = new UserMode("WALLOPS", modechar[0]);
- else
- umodes[modechar[0]] = modename.upper();
-
- if (um)
- ModeManager::AddUserMode(um);
}
}
else if (params[0].equals_cs("MODULES") && params.size() > 1)
@@ -603,7 +728,7 @@ struct IRCDMessageCapab : Message::Capab
else if (module.find("m_rline.so") == 0)
{
Servers::Capab.insert("RLINE");
- const Anope::string &regexengine = Config->GetBlock("options")->Get<const Anope::string>("regexengine");
+ const Anope::string &regexengine = Config->GetBlock("options")->Get<Anope::string>("regexengine");
if (!regexengine.empty() && module.length() > 11 && regexengine != module.substr(11))
Log() << "Warning: InspIRCd is using regex engine " << module.substr(11) << ", but we have " << regexengine << ". This may cause inconsistencies.";
}
@@ -619,25 +744,11 @@ struct IRCDMessageCapab : Message::Capab
while (ssep.GetToken(module))
{
if (module.equals_cs("m_services_account.so"))
- {
Servers::Capab.insert("SERVICES");
- ModeManager::AddChannelMode(new InspIRCdExtban::AccountMatcher("ACCOUNTBAN", "BAN", 'R'));
- ModeManager::AddChannelMode(new InspIRCdExtban::UnidentifiedMatcher("UNREGISTEREDBAN", "BAN", 'U'));
- }
else if (module.equals_cs("m_chghost.so"))
Servers::Capab.insert("CHGHOST");
else if (module.equals_cs("m_chgident.so"))
Servers::Capab.insert("CHGIDENT");
- else if (module == "m_channelban.so")
- ModeManager::AddChannelMode(new InspIRCdExtban::ChannelMatcher("CHANNELBAN", "BAN", 'j'));
- else if (module == "m_gecosban.so")
- ModeManager::AddChannelMode(new InspIRCdExtban::RealnameMatcher("REALNAMEBAN", "BAN", 'r'));
- else if (module == "m_nopartmessage.so")
- ModeManager::AddChannelMode(new InspIRCdExtban::EntryMatcher("PARTMESSAGEBAN", "BAN", 'p'));
- else if (module == "m_serverban.so")
- ModeManager::AddChannelMode(new InspIRCdExtban::ServerMatcher("SERVERBAN", "BAN", 's'));
- else if (module == "m_muteban.so")
- ModeManager::AddChannelMode(new InspIRCdExtban::EntryMatcher("QUIET", "BAN", 'm'));
}
}
else if (params[0].equals_cs("CAPABILITIES") && params.size() > 1)
@@ -646,89 +757,11 @@ struct IRCDMessageCapab : Message::Capab
Anope::string capab;
while (ssep.GetToken(capab))
{
- if (capab.find("CHANMODES") != Anope::string::npos)
- {
- Anope::string modes(capab.begin() + 10, capab.end());
- commasepstream sep(modes);
- Anope::string modebuf;
-
- sep.GetToken(modebuf);
- for (size_t t = 0, end = modebuf.length(); t < end; ++t)
- {
- if (ModeManager::FindChannelModeByChar(modebuf[t]))
- continue;
- ModeManager::AddChannelMode(new ChannelModeList(chmodes[modebuf[t]], modebuf[t]));
- }
-
- sep.GetToken(modebuf);
- for (size_t t = 0, end = modebuf.length(); t < end; ++t)
- {
- if (ModeManager::FindChannelModeByChar(modebuf[t]))
- continue;
- ModeManager::AddChannelMode(new ChannelModeParam(chmodes[modebuf[t]], modebuf[t]));
- }
-
- sep.GetToken(modebuf);
- for (size_t t = 0, end = modebuf.length(); t < end; ++t)
- {
- if (ModeManager::FindChannelModeByChar(modebuf[t]))
- continue;
- ModeManager::AddChannelMode(new ChannelModeParam(chmodes[modebuf[t]], modebuf[t], true));
- }
-
- sep.GetToken(modebuf);
- for (size_t t = 0, end = modebuf.length(); t < end; ++t)
- {
- if (ModeManager::FindChannelModeByChar(modebuf[t]))
- continue;
- ModeManager::AddChannelMode(new ChannelMode(chmodes[modebuf[t]], modebuf[t]));
- }
- }
- else if (capab.find("USERMODES") != Anope::string::npos)
- {
- Anope::string modes(capab.begin() + 10, capab.end());
- commasepstream sep(modes);
- Anope::string modebuf;
-
- sep.GetToken(modebuf);
- sep.GetToken(modebuf);
-
- if (sep.GetToken(modebuf))
- for (size_t t = 0, end = modebuf.length(); t < end; ++t)
- ModeManager::AddUserMode(new UserModeParam(umodes[modebuf[t]], modebuf[t]));
-
- if (sep.GetToken(modebuf))
- for (size_t t = 0, end = modebuf.length(); t < end; ++t)
- ModeManager::AddUserMode(new UserMode(umodes[modebuf[t]], modebuf[t]));
- }
- else if (capab.find("MAXMODES=") != Anope::string::npos)
+ if (capab.find("MAXMODES=") != Anope::string::npos)
{
Anope::string maxmodes(capab.begin() + 9, capab.end());
IRCD->MaxModes = maxmodes.is_pos_number_only() ? convertTo<unsigned>(maxmodes) : 3;
}
- else if (capab.find("PREFIX=") != Anope::string::npos)
- {
- Anope::string modes(capab.begin() + 8, capab.begin() + capab.find(')'));
- Anope::string chars(capab.begin() + capab.find(')') + 1, capab.end());
- short level = modes.length() - 1;
-
- for (size_t t = 0, end = modes.length(); t < end; ++t)
- {
- ChannelMode *cm = ModeManager::FindChannelModeByChar(modes[t]);
- if (cm == NULL || cm->type != MODE_STATUS)
- {
- Log() << "CAPAB PREFIX gave unknown channel status mode " << modes[t];
- continue;
- }
-
- ChannelModeStatus *cms = anope_dynamic_static_cast<ChannelModeStatus *>(cm);
- cms->level = level--;
-
- Log(LOG_DEBUG) << cms->name << " is now level " << cms->level;
- }
-
- ModeManager::RebuildStatusModes();
- }
else if (capab == "GLOBOPS=1")
Servers::Capab.insert("GLOBOPS");
}
@@ -737,14 +770,14 @@ struct IRCDMessageCapab : Message::Capab
{
if (!Servers::Capab.count("SERVICES"))
{
- UplinkSocket::Message() << "ERROR :m_services_account.so is not loaded. This is required by Anope";
+ Uplink::Send("ERROR", "m_services_account.so is not loaded. This is required by Anope");
Anope::QuitReason = "ERROR: Remote server does not have the m_services_account module loaded, and this is required.";
Anope::Quitting = true;
return;
}
if (!ModeManager::FindUserModeByName("PRIV"))
{
- UplinkSocket::Message() << "ERROR :m_hidechans.so is not loaded. This is required by Anope";
+ Uplink::Send("ERROR", "m_hidechans.so is not loaded. This is required by Anope");
Anope::QuitReason = "ERROR: Remote server does not have the m_hidechans module loaded, and this is required.";
Anope::Quitting = true;
return;
@@ -755,9 +788,6 @@ struct IRCDMessageCapab : Message::Capab
Log() << "CHGHOST missing, Usage disabled until module is loaded.";
if (!Servers::Capab.count("CHGIDENT"))
Log() << "CHGIDENT missing, Usage disabled until module is loaded.";
-
- chmodes.clear();
- umodes.clear();
}
Message::Capab::Run(source, params);
@@ -766,11 +796,9 @@ struct IRCDMessageCapab : Message::Capab
struct IRCDMessageEncap : IRCDMessage
{
- ServiceReference<IRCDMessage> insp12_encap;
-
- IRCDMessageEncap(Module *creator) : IRCDMessage(creator, "ENCAP", 4), insp12_encap("IRCDMessage", "inspircd12/encap") { SetFlag(IRCDMESSAGE_SOFT_LIMIT); }
+ IRCDMessageEncap(Module *creator) : IRCDMessage(creator, "ENCAP", 4) { SetFlag(IRCDMESSAGE_SOFT_LIMIT); }
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
{
if (Anope::Match(Me->GetSID(), params[0]) == false)
return;
@@ -782,7 +810,7 @@ struct IRCDMessageEncap : IRCDMessage
return;
u->SetIdent(params[3]);
- UplinkSocket::Message(u) << "FIDENT " << params[3];
+ Uplink::Send(u, "FIDENT", params[3]);
}
else if (params[1] == "CHGHOST")
{
@@ -791,7 +819,7 @@ struct IRCDMessageEncap : IRCDMessage
return;
u->SetDisplayedHost(params[3]);
- UplinkSocket::Message(u) << "FHOST " << params[3];
+ Uplink::Send(u, "FHOST", params[3]);
}
else if (params[1] == "CHGNAME")
{
@@ -800,11 +828,22 @@ struct IRCDMessageEncap : IRCDMessage
return;
u->SetRealname(params[3]);
- UplinkSocket::Message(u) << "FNAME " << params[3];
+ Uplink::Send(u, "FNAME", params[3]);
}
+ }
+};
- if (insp12_encap)
- insp12_encap->Run(source, params);
+struct IRCDMessageEndburst : IRCDMessage
+{
+ IRCDMessageEndburst(Module *creator) : IRCDMessage(creator, "ENDBURST", 0) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
+
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
+ {
+ Server *s = source.GetServer();
+
+ Log(LOG_DEBUG) << "Processed ENDBURST for " << s->GetName();
+
+ s->Sync(true);
}
};
@@ -812,7 +851,7 @@ struct IRCDMessageFHost : IRCDMessage
{
IRCDMessageFHost(Module *creator) : IRCDMessage(creator, "FHOST", 1) { SetFlag(IRCDMESSAGE_REQUIRE_USER); }
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
{
User *u = source.GetUser();
if (u->HasMode("CLOAK"))
@@ -825,19 +864,337 @@ struct IRCDMessageFIdent : IRCDMessage
{
IRCDMessageFIdent(Module *creator) : IRCDMessage(creator, "FIDENT", 1) { SetFlag(IRCDMESSAGE_REQUIRE_USER); }
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
{
source.GetUser()->SetIdent(params[0]);
}
};
+struct IRCDMessageFJoin : IRCDMessage
+{
+ IRCDMessageFJoin(Module *creator) : IRCDMessage(creator, "FJOIN", 2) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); SetFlag(IRCDMESSAGE_SOFT_LIMIT); }
+
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
+ {
+ Anope::string modes;
+ if (params.size() >= 3)
+ {
+ for (unsigned i = 2; i < params.size() - 1; ++i)
+ modes += " " + params[i];
+ if (!modes.empty())
+ modes.erase(modes.begin());
+ }
+
+ std::list<Message::Join::SJoinUser> users;
+
+ spacesepstream sep(params[params.size() - 1]);
+ Anope::string buf;
+ while (sep.GetToken(buf))
+ {
+ Message::Join::SJoinUser sju;
+
+ /* Loop through prefixes and find modes for them */
+ for (char c; (c = buf[0]) != ',' && c;)
+ {
+ buf.erase(buf.begin());
+ sju.first.AddMode(c);
+ }
+ /* Erase the , */
+ if (!buf.empty())
+ buf.erase(buf.begin());
+
+ sju.second = User::Find(buf);
+ if (!sju.second)
+ {
+ Log(LOG_DEBUG) << "FJOIN for non-existent user " << buf << " on " << params[0];
+ continue;
+ }
+
+ users.push_back(sju);
+ }
+
+ time_t ts = Anope::string(params[1]).is_pos_number_only() ? convertTo<time_t>(params[1]) : Anope::CurTime;
+ Message::Join::SJoin(source, params[0], ts, modes, users);
+ }
+};
+
+struct IRCDMessageFMode : IRCDMessage
+{
+ IRCDMessageFMode(Module *creator) : IRCDMessage(creator, "FMODE", 3) { SetFlag(IRCDMESSAGE_SOFT_LIMIT); }
+
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
+ {
+ /* :source FMODE #test 12345678 +nto foo */
+
+ Anope::string modes = params[2];
+ for (unsigned n = 3; n < params.size(); ++n)
+ modes += " " + params[n];
+
+ Channel *c = Channel::Find(params[0]);
+ time_t ts;
+
+ try
+ {
+ ts = convertTo<time_t>(params[1]);
+ }
+ catch (const ConvertException &)
+ {
+ ts = 0;
+ }
+
+ if (c)
+ c->SetModesInternal(source, modes, ts);
+ }
+};
+
+struct IRCDMessageFTopic : IRCDMessage
+{
+ IRCDMessageFTopic(Module *creator) : IRCDMessage(creator, "FTOPIC", 4) { }
+
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
+ {
+ /* :source FTOPIC channel topicts setby :topic */
+
+ Channel *c = Channel::Find(params[0]);
+ if (c)
+ c->ChangeTopicInternal(NULL, params[2], params[3], Anope::string(params[1]).is_pos_number_only() ? convertTo<time_t>(params[1]) : Anope::CurTime);
+ }
+};
+
+struct IRCDMessageIdle : IRCDMessage
+{
+ IRCDMessageIdle(Module *creator) : IRCDMessage(creator, "IDLE", 1) { }
+
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
+ {
+ ServiceBot *bi = ServiceBot::Find(params[0]);
+ if (bi)
+ {
+ Uplink::Send(bi, "IDLE", source.GetSource(), Anope::StartTime, Anope::CurTime - bi->lastmsg);
+ }
+ else
+ {
+ User *u = User::Find(params[0]);
+ if (u && u->server == Me)
+ Uplink::Send(u, "IDLE", source.GetSource(), Anope::StartTime, 0);
+ }
+ }
+};
+
+/*
+ * source = numeric of the sending server
+ * params[0] = uuid
+ * params[1] = metadata name
+ * params[2] = data
+ */
+struct IRCDMessageMetadata : IRCDMessage
+{
+ const bool &do_topiclock, &do_mlock;
+
+ IRCDMessageMetadata(Module *creator, const bool &handle_topiclock, const bool &handle_mlock)
+ : IRCDMessage(creator, "METADATA", 3)
+ , do_topiclock(handle_topiclock)
+ , do_mlock(handle_mlock)
+ {
+ SetFlag(IRCDMESSAGE_REQUIRE_SERVER);
+ }
+
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
+ {
+ if (isdigit(params[0][0]))
+ {
+ if (params[1].equals_cs("accountname"))
+ {
+ User *u = User::Find(params[0]);
+ NickServ::Account *nc = NickServ::FindAccount(params[2]);
+ if (u && nc)
+ u->Login(nc);
+ }
+
+ /*
+ * possible incoming ssl_cert messages:
+ * Received: :409 METADATA 409AAAAAA ssl_cert :vTrSe c38070ce96e41cc144ed6590a68d45a6 <...> <...>
+ * Received: :409 METADATA 409AAAAAC ssl_cert :vTrSE Could not get peer certificate: error:00000000:lib(0):func(0):reason(0)
+ */
+ else if (params[1].equals_cs("ssl_cert"))
+ {
+ User *u = User::Find(params[0]);
+ if (!u)
+ return;
+ u->Extend<bool>("ssl", true);
+ Anope::string data = params[2].c_str();
+ size_t pos1 = data.find(' ') + 1;
+ size_t pos2 = data.find(' ', pos1);
+ if ((pos2 - pos1) >= 32) // inspircd supports md5 and sha1 fingerprint hashes -> size 32 or 40 bytes.
+ {
+ u->fingerprint = data.substr(pos1, pos2 - pos1);
+ }
+ EventManager::Get()->Dispatch(&Event::Fingerprint::OnFingerprint, u);
+ }
+ }
+ // We deliberately ignore non-bursting servers to avoid pseudoserver fights
+ else if ((params[0][0] == '#') && (!source.GetServer()->IsSynced()))
+ {
+ Channel *c = Channel::Find(params[0]);
+ if (c && c->ci)
+ {
+ if ((do_mlock) && (params[1] == "mlock"))
+ {
+ ModeLocks *modelocks = c->ci->GetExt<ModeLocks>("modelocks");
+ Anope::string modes;
+ if (modelocks)
+ modes = modelocks->GetMLockAsString(c->ci, false).replace_all_cs("+", "").replace_all_cs("-", "");
+
+ // Mode lock string is not what we say it is?
+ if (modes != params[2])
+ Uplink::Send(Me, "METADATA", c->name, "mlock", modes);
+ }
+ else if ((do_topiclock) && (params[1] == "topiclock"))
+ {
+ bool mystate = c->ci->GetExt<bool>("TOPICLOCK");
+ bool serverstate = (params[2] == "1");
+ if (mystate != serverstate)
+ Uplink::Send(Me, "METADATA", c->name, "topiclock", mystate ? "1" : "");
+ }
+ }
+ }
+ else if (params[0] == "*")
+ {
+ // Wed Oct 3 15:40:27 2012: S[14] O :20D METADATA * modules :-m_svstopic.so
+
+ if (params[1].equals_cs("modules") && !params[2].empty())
+ {
+ // only interested when it comes from our uplink
+ Server* server = source.GetServer();
+ if (!server || server->GetUplink() != Me)
+ return;
+
+ bool plus = (params[2][0] == '+');
+ if (!plus && params[2][0] != '-')
+ return;
+
+ bool required = false;
+ Anope::string capab, module = params[2].substr(1);
+
+ if (module.equals_cs("m_services_account.so"))
+ required = true;
+ else if (module.equals_cs("m_hidechans.so"))
+ required = true;
+ else if (module.equals_cs("m_chghost.so"))
+ capab = "CHGHOST";
+ else if (module.equals_cs("m_chgident.so"))
+ capab = "CHGIDENT";
+ else if (module.equals_cs("m_svshold.so"))
+ capab = "SVSHOLD";
+ else if (module.equals_cs("m_rline.so"))
+ capab = "RLINE";
+ else if (module.equals_cs("m_topiclock.so"))
+ capab = "TOPICLOCK";
+ else
+ return;
+
+ if (required)
+ {
+ if (!plus)
+ Log() << "Warning: InspIRCd unloaded module " << module << ", Anope won't function correctly without it";
+ }
+ else
+ {
+ if (plus)
+ Servers::Capab.insert(capab);
+ else
+ Servers::Capab.erase(capab);
+
+ Log() << "InspIRCd " << (plus ? "loaded" : "unloaded") << " module " << module << ", adjusted functionality";
+ }
+
+ }
+ }
+ }
+};
+
+struct IRCDMessageMode : IRCDMessage
+{
+ IRCDMessageMode(Module *creator) : IRCDMessage(creator, "MODE", 2) { SetFlag(IRCDMESSAGE_SOFT_LIMIT); }
+
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
+ {
+ if (IRCD->IsChannelValid(params[0]))
+ {
+ Channel *c = Channel::Find(params[0]);
+
+ Anope::string modes = params[1];
+ for (unsigned n = 2; n < params.size(); ++n)
+ modes += " " + params[n];
+
+ if (c)
+ c->SetModesInternal(source, modes);
+ }
+ else
+ {
+ /* InspIRCd lets opers change another
+ users modes, we have to kludge this
+ as it slightly breaks RFC1459
+ */
+ User *u = source.GetUser();
+ // This can happen with server-origin modes.
+ if (!u)
+ u = User::Find(params[0]);
+ // if it's still null, drop it like fire.
+ // most likely situation was that server introduced a nick which we subsequently akilled
+ if (u)
+ u->SetModesInternal(source, "%s", params[1].c_str());
+ }
+ }
+};
+
+struct IRCDMessageNick : IRCDMessage
+{
+ IRCDMessageNick(Module *creator) : IRCDMessage(creator, "NICK", 2) { SetFlag(IRCDMESSAGE_REQUIRE_USER); }
+
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
+ {
+ source.GetUser()->ChangeNick(params[0]);
+ }
+};
+
+struct IRCDMessageOperType : IRCDMessage
+{
+ IRCDMessageOperType(Module *creator) : IRCDMessage(creator, "OPERTYPE", 0) { SetFlag(IRCDMESSAGE_SOFT_LIMIT); SetFlag(IRCDMESSAGE_REQUIRE_USER); }
+
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
+ {
+ /* opertype is equivalent to mode +o because servers
+ dont do this directly */
+ User *u = source.GetUser();
+ if (!u->HasMode("OPER"))
+ u->SetModesInternal(source, "+o");
+ }
+};
+
+struct IRCDMessageRSQuit : IRCDMessage
+{
+ IRCDMessageRSQuit(Module *creator) : IRCDMessage(creator, "RSQUIT", 1) { SetFlag(IRCDMESSAGE_SOFT_LIMIT); }
+
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
+ {
+ Server *s = Server::Find(params[0]);
+ const Anope::string &reason = params.size() > 1 ? params[1] : "";
+ if (!s)
+ return;
+
+ Uplink::Send(Me, "SQUIT", s->GetSID(), reason);
+ s->Delete(s->GetName() + " " + s->GetUplink()->GetName());
+ }
+};
+
struct IRCDMessageSave : IRCDMessage
{
time_t last_collide;
IRCDMessageSave(Module *creator) : IRCDMessage(creator, "SAVE", 2), last_collide(0) { }
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
{
User *targ = User::Find(params[0]);
time_t ts;
@@ -873,54 +1230,118 @@ struct IRCDMessageSave : IRCDMessage
}
};
-class IRCDMessageMetadata : IRCDMessage
+struct IRCDMessageServer : IRCDMessage
{
- ServiceReference<IRCDMessage> insp12_metadata;
- const bool &do_topiclock;
- const bool &do_mlock;
+ IRCDMessageServer(Module *creator) : IRCDMessage(creator, "SERVER", 5) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
+
+ /*
+ * [Nov 04 00:08:46.308435 2009] debug: Received: SERVER irc.inspircd.com pass 0 964 :Testnet Central!
+ * 0: name
+ * 1: pass
+ * 2: hops
+ * 3: numeric
+ * 4: desc
+ */
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
+ {
+ unsigned int hops = Anope::string(params[2]).is_pos_number_only() ? convertTo<unsigned>(params[2]) : 0;
+ new Server(source.GetServer() == NULL ? Me : source.GetServer(), params[0], hops, params[4], params[3]);
+ }
+};
- public:
- IRCDMessageMetadata(Module *creator, const bool &handle_topiclock, const bool &handle_mlock) : IRCDMessage(creator, "METADATA", 3), insp12_metadata("IRCDMessage", "inspircd12/metadata"), do_topiclock(handle_topiclock), do_mlock(handle_mlock) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
+struct IRCDMessageSQuit : Message::SQuit
+{
+ IRCDMessageSQuit(Module *creator) : Message::SQuit(creator) { }
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
{
- // We deliberately ignore non-bursting servers to avoid pseudoserver fights
- if ((params[0][0] == '#') && (!source.GetServer()->IsSynced()))
+ if (params[0] == rsquit_id || params[0] == rsquit_server)
{
- Channel *c = Channel::Find(params[0]);
- if (c && c->ci)
+ /* squit for a recently squit server, introduce the juped server now */
+ Server *s = Server::Find(rsquit_server);
+
+ rsquit_id.clear();
+ rsquit_server.clear();
+
+ if (s && s->IsJuped())
+ IRCD->SendServer(s);
+ }
+ else
+ Message::SQuit::Run(source, params);
+ }
+};
+
+struct IRCDMessageTime : IRCDMessage
+{
+ IRCDMessageTime(Module *creator) : IRCDMessage(creator, "TIME", 2) { }
+
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
+ {
+ Uplink::Send(Me, "TIME", source.GetSource(), params[1], Anope::CurTime);
+ }
+};
+
+struct IRCDMessageUID : IRCDMessage
+{
+ ServiceReference<SASL::Service> sasl;
+
+ IRCDMessageUID(Module *creator) : IRCDMessage(creator, "UID", 8) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); SetFlag(IRCDMESSAGE_SOFT_LIMIT); }
+
+ /*
+ * [Nov 03 22:09:58.176252 2009] debug: Received: :964 UID 964AAAAAC 1225746297 w00t2 localhost testnet.user w00t 127.0.0.1 1225746302 +iosw +ACGJKLNOQcdfgjklnoqtx :Robin Burchell <w00t@inspircd.org>
+ * 0: uid
+ * 1: ts
+ * 2: nick
+ * 3: host
+ * 4: dhost
+ * 5: ident
+ * 6: ip
+ * 7: signon
+ * 8+: modes and params -- IMPORTANT, some modes (e.g. +s) may have parameters. So don't assume a fixed position of realname!
+ * last: realname
+ */
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
+ {
+ time_t ts = convertTo<time_t>(params[1]);
+
+ Anope::string modes = params[8];
+ for (unsigned i = 9; i < params.size() - 1; ++i)
+ modes += " " + params[i];
+
+ NickServ::Nick *na = NULL;
+ if (sasl)
+ for (std::list<SASLUser>::iterator it = saslusers.begin(); it != saslusers.end();)
{
- if ((do_mlock) && (params[1] == "mlock"))
- {
- ModeLocks *modelocks = c->ci->GetExt<ModeLocks>("modelocks");
- Anope::string modes;
- if (modelocks)
- modes = modelocks->GetMLockAsString(false).replace_all_cs("+", "").replace_all_cs("-", "");
+ SASLUser &u = *it;
- // Mode lock string is not what we say it is?
- if (modes != params[2])
- UplinkSocket::Message(Me) << "METADATA " << c->name << " mlock :" << modes;
- }
- else if ((do_topiclock) && (params[1] == "topiclock"))
+ if (u.created + 30 < Anope::CurTime)
+ it = saslusers.erase(it);
+ else if (u.uid == params[0])
{
- bool mystate = c->ci->GetExt<bool>("TOPICLOCK");
- bool serverstate = (params[2] == "1");
- if (mystate != serverstate)
- UplinkSocket::Message(Me) << "METADATA " << c->name << " topiclock :" << (mystate ? "1" : "");
+ na = NickServ::FindNick(u.acc);
+ it = saslusers.erase(it);
}
+ else
+ ++it;
}
- }
- if (insp12_metadata)
- insp12_metadata->Run(source, params);
+ User *u = User::OnIntroduce(params[2], params[5], params[3], params[4], params[6], source.GetServer(), params[params.size() - 1], ts, modes, params[0], na ? na->GetAccount() : NULL);
+ if (u)
+ u->signon = convertTo<time_t>(params[7]);
}
};
class ProtoInspIRCd20 : public Module
+ , public EventHook<Event::UserNickChange>
+ , public EventHook<Event::ChannelSync>
+ , public EventHook<Event::ChanRegistered>
+ , public EventHook<Event::DelChan>
+ , public EventHook<Event::MLockEvents>
+ , public EventHook<Event::SetChannelOption>
{
- Module *m_insp12;
-
InspIRCd20Proto ircd_proto;
+ ExtensibleItem<bool> ssl;
+ ServiceReference<ModeLocks> mlocks;
/* Core message handlers */
Message::Away message_away;
@@ -938,103 +1359,149 @@ class ProtoInspIRCd20 : public Module
Message::Stats message_stats;
Message::Topic message_topic;
- /* InspIRCd 1.2 message handlers */
- ServiceAlias message_endburst, message_fjoin, message_fmode,
- message_ftopic, message_idle, message_mode,
- message_nick, message_opertype, message_rsquit, message_server,
- message_squit, message_time, message_uid;
-
/* Our message handlers */
IRCDMessageCapab message_capab;
IRCDMessageEncap message_encap;
+ IRCDMessageEndburst message_endburst;
IRCDMessageFHost message_fhost;
IRCDMessageFIdent message_fident;
+ IRCDMessageFJoin message_fjoin;
+ IRCDMessageFMode message_fmode;
+ IRCDMessageFTopic message_ftopic;
+ IRCDMessageIdle message_idle;
IRCDMessageMetadata message_metadata;
+ IRCDMessageMode message_mode;
+ IRCDMessageNick message_nick;
+ IRCDMessageOperType message_opertype;
+ IRCDMessageRSQuit message_rsquit;
IRCDMessageSave message_save;
+ IRCDMessageServer message_server;
+ IRCDMessageSQuit message_squit;
+ IRCDMessageTime message_time;
+ IRCDMessageUID message_uid;
bool use_server_side_topiclock, use_server_side_mlock;
void SendChannelMetadata(Channel *c, const Anope::string &metadataname, const Anope::string &value)
{
- UplinkSocket::Message(Me) << "METADATA " << c->name << " " << metadataname << " :" << value;
+ Uplink::Send(Me, "METADATA", c->name, metadataname, value);
}
public:
- ProtoInspIRCd20(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, PROTOCOL | VENDOR),
- ircd_proto(this),
- message_away(this), message_error(this), message_invite(this), message_join(this), message_kick(this),
- message_kill(this), message_motd(this), message_notice(this), message_part(this), message_ping(this),
- message_privmsg(this), message_quit(this), message_stats(this), message_topic(this),
-
- message_endburst("IRCDMessage", "inspircd20/endburst", "inspircd12/endburst"),
- message_fjoin("IRCDMessage", "inspircd20/fjoin", "inspircd12/fjoin"),
- message_fmode("IRCDMessage", "inspircd20/fmode", "inspircd12/fmode"),
- message_ftopic("IRCDMessage", "inspircd20/ftopic", "inspircd12/ftopic"),
- message_idle("IRCDMessage", "inspircd20/idle", "inspircd12/idle"),
- message_mode("IRCDMessage", "inspircd20/mode", "inspircd12/mode"),
- message_nick("IRCDMessage", "inspircd20/nick", "inspircd12/nick"),
- message_opertype("IRCDMessage", "inspircd20/opertype", "inspircd12/opertype"),
- message_rsquit("IRCDMessage", "inspircd20/rsquit", "inspircd12/rsquit"),
- message_server("IRCDMessage", "inspircd20/server", "inspircd12/server"),
- message_squit("IRCDMessage", "inspircd20/squit", "inspircd12/squit"),
- message_time("IRCDMessage", "inspircd20/time", "inspircd12/time"),
- message_uid("IRCDMessage", "inspircd20/uid", "inspircd12/uid"),
-
- message_capab(this), message_encap(this), message_fhost(this), message_fident(this), message_metadata(this, use_server_side_topiclock, use_server_side_mlock),
- message_save(this)
+ ProtoInspIRCd20(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, PROTOCOL | VENDOR)
+ , EventHook<Event::UserNickChange>(this)
+ , EventHook<Event::ChannelSync>(this)
+ , EventHook<Event::ChanRegistered>(this)
+ , EventHook<Event::DelChan>(this)
+ , EventHook<Event::MLockEvents>(this)
+ , EventHook<Event::SetChannelOption>(this)
+
+ , ircd_proto(this)
+ , ssl(this, "ssl")
+ , message_away(this)
+ , message_error(this)
+ , message_invite(this)
+ , message_join(this)
+ , message_kick(this)
+ , message_kill(this)
+ , message_motd(this)
+ , message_notice(this)
+ , message_part(this)
+ , message_ping(this)
+ , message_privmsg(this)
+ , message_quit(this)
+ , message_stats(this)
+ , message_topic(this)
+
+ , message_capab(this)
+ , message_encap(this)
+ , message_endburst(this)
+ , message_fhost(this)
+ , message_fident(this)
+ , message_fjoin(this)
+ , message_fmode(this)
+ , message_ftopic(this)
+ , message_idle(this)
+ , message_metadata(this, use_server_side_topiclock, use_server_side_mlock)
+ , message_mode(this)
+ , message_nick(this)
+ , message_opertype(this)
+ , message_rsquit(this)
+ , message_save(this)
+ , message_server(this)
+ , message_squit(this)
+ , message_time(this)
+ , message_uid(this)
{
-
- if (ModuleManager::LoadModule("inspircd12", User::Find(creator)) != MOD_ERR_OK)
- throw ModuleException("Unable to load inspircd12");
- m_insp12 = ModuleManager::FindModule("inspircd12");
- if (!m_insp12)
- throw ModuleException("Unable to find inspircd12");
- if (!insp12)
- throw ModuleException("No protocol interface for insp12");
- ModuleManager::DetachAll(m_insp12);
-
}
- ~ProtoInspIRCd20()
- {
- m_insp12 = ModuleManager::FindModule("inspircd12");
- ModuleManager::UnloadModule(m_insp12, NULL);
- }
-
- void OnReload(Configuration::Conf *conf) anope_override
+ void OnReload(Configuration::Conf *conf) override
{
use_server_side_topiclock = conf->GetModule(this)->Get<bool>("use_server_side_topiclock");
use_server_side_mlock = conf->GetModule(this)->Get<bool>("use_server_side_mlock");
+
+ for (int i = 0; i < conf->CountBlock("extban"); ++i)
+ {
+ Configuration::Block *extban = conf->GetBlock("extban", i);
+ Anope::string name = extban->Get<Anope::string>("name"),
+ type = extban->Get<Anope::string>("type"),
+ base = extban->Get<Anope::string>("base"),
+ character = extban->Get<Anope::string>("character");
+
+ ChannelMode *cm;
+
+ if (character.empty())
+ continue;
+
+ if (type == "channel")
+ cm = new InspIRCdExtban::ChannelMatcher(name, base, character[0]);
+ else if (type == "entry")
+ cm = new InspIRCdExtban::EntryMatcher(name, base, character[0]);
+ else if (type == "realname")
+ cm = new InspIRCdExtban::RealnameMatcher(name, base, character[0]);
+ else if (type == "account")
+ cm = new InspIRCdExtban::AccountMatcher(name, base, character[0]);
+ else if (type == "fingerprint")
+ cm = new InspIRCdExtban::FingerprintMatcher(name, base, character[0]);
+ else if (type == "unidentified")
+ cm = new InspIRCdExtban::UnidentifiedMatcher(name, base, character[0]);
+ else if (type == "server")
+ cm = new InspIRCdExtban::ServerMatcher(name, base, character[0]);
+ else
+ continue;
+
+ if (!ModeManager::AddChannelMode(cm))
+ delete cm;
+ }
}
- void OnUserNickChange(User *u, const Anope::string &) anope_override
+ void OnUserNickChange(User *u, const Anope::string &) override
{
u->RemoveModeInternal(Me, ModeManager::FindUserModeByName("REGISTERED"));
}
- void OnChannelSync(Channel *c) anope_override
+ void OnChannelSync(Channel *c) override
{
if (c->ci)
this->OnChanRegistered(c->ci);
}
- void OnChanRegistered(ChannelInfo *ci) anope_override
+ void OnChanRegistered(ChanServ::Channel *ci) override
{
- ModeLocks *modelocks = ci->GetExt<ModeLocks>("modelocks");
- if (use_server_side_mlock && ci->c && modelocks && !modelocks->GetMLockAsString(false).empty())
+ if (use_server_side_mlock && ci->c && mlocks && !mlocks->GetMLockAsString(ci, false).empty())
{
- Anope::string modes = modelocks->GetMLockAsString(false).replace_all_cs("+", "").replace_all_cs("-", "");
+ Anope::string modes = mlocks->GetMLockAsString(ci, false).replace_all_cs("+", "").replace_all_cs("-", "");
SendChannelMetadata(ci->c, "mlock", modes);
}
if (use_server_side_topiclock && Servers::Capab.count("TOPICLOCK") && ci->c)
{
- if (ci->HasExt("TOPICLOCK"))
+ if (ci->HasFieldS("TOPICLOCK"))
SendChannelMetadata(ci->c, "topiclock", "1");
}
}
- void OnDelChan(ChannelInfo *ci) anope_override
+ void OnDelChan(ChanServ::Channel *ci) override
{
if (use_server_side_mlock && ci->c)
SendChannelMetadata(ci->c, "mlock", "");
@@ -1043,35 +1510,33 @@ class ProtoInspIRCd20 : public Module
SendChannelMetadata(ci->c, "topiclock", "");
}
- EventReturn OnMLock(ChannelInfo *ci, ModeLock *lock) anope_override
+ EventReturn OnMLock(ChanServ::Channel *ci, ModeLock *lock) override
{
- ModeLocks *modelocks = ci->GetExt<ModeLocks>("modelocks");
- ChannelMode *cm = ModeManager::FindChannelModeByName(lock->name);
- if (use_server_side_mlock && cm && ci->c && modelocks && (cm->type == MODE_REGULAR || cm->type == MODE_PARAM))
+ ChannelMode *cm = ModeManager::FindChannelModeByName(lock->GetName());
+ if (use_server_side_mlock && cm && ci->c && mlocks && (cm->type == MODE_REGULAR || cm->type == MODE_PARAM))
{
- Anope::string modes = modelocks->GetMLockAsString(false).replace_all_cs("+", "").replace_all_cs("-", "") + cm->mchar;
+ Anope::string modes = mlocks->GetMLockAsString(ci, false).replace_all_cs("+", "").replace_all_cs("-", "") + cm->mchar;
SendChannelMetadata(ci->c, "mlock", modes);
}
return EVENT_CONTINUE;
}
- EventReturn OnUnMLock(ChannelInfo *ci, ModeLock *lock) anope_override
+ EventReturn OnUnMLock(ChanServ::Channel *ci, ModeLock *lock) override
{
- ModeLocks *modelocks = ci->GetExt<ModeLocks>("modelocks");
- ChannelMode *cm = ModeManager::FindChannelModeByName(lock->name);
- if (use_server_side_mlock && cm && ci->c && modelocks && (cm->type == MODE_REGULAR || cm->type == MODE_PARAM))
+ ChannelMode *cm = ModeManager::FindChannelModeByName(lock->GetName());
+ if (use_server_side_mlock && cm && ci->c && mlocks && (cm->type == MODE_REGULAR || cm->type == MODE_PARAM))
{
- Anope::string modes = modelocks->GetMLockAsString(false).replace_all_cs("+", "").replace_all_cs("-", "").replace_all_cs(cm->mchar, "");
+ Anope::string modes = mlocks->GetMLockAsString(ci, false).replace_all_cs("+", "").replace_all_cs("-", "").replace_all_cs(cm->mchar, "");
SendChannelMetadata(ci->c, "mlock", modes);
}
return EVENT_CONTINUE;
}
- EventReturn OnSetChannelOption(CommandSource &source, Command *cmd, ChannelInfo *ci, const Anope::string &setting) anope_override
+ EventReturn OnSetChannelOption(CommandSource &source, Command *cmd, ChanServ::Channel *ci, const Anope::string &setting) override
{
- if (cmd->name == "chanserv/topic" && ci->c)
+ if (cmd->GetName() == "chanserv/topic" && ci->c)
{
if (setting == "topiclock on")
SendChannelMetadata(ci->c, "topiclock", "1");
diff --git a/modules/protocol/ngircd.cpp b/modules/protocol/ngircd.cpp
index e7ae31fce..aa2c23a80 100644
--- a/modules/protocol/ngircd.cpp
+++ b/modules/protocol/ngircd.cpp
@@ -1,19 +1,28 @@
-/* ngIRCd Protocol module for Anope IRC Services
+/*
+ * Anope IRC Services
*
- * (C) 2011-2012, 2014 Alexander Barton <alex@barton.de>
- * (C) 2011-2016 Anope Team <team@anope.org>
+ * Copyright (C) 2011-2016 Anope Team <team@anope.org>
+ * Copyright (C) 2011-2012, 2014 Alexander Barton <alex@barton.de>
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
*
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "module.h"
class ngIRCdProto : public IRCDProto
{
- void SendSVSKillInternal(const MessageSource &source, User *user, const Anope::string &buf) anope_override
+ void SendSVSKillInternal(const MessageSource &source, User *user, const Anope::string &buf) override
{
IRCDProto::SendSVSKillInternal(source, user, buf);
user->KillInternal(source, buf);
@@ -30,64 +39,64 @@ class ngIRCdProto : public IRCDProto
MaxModes = 5;
}
- void SendAkill(User *u, XLine *x) anope_override
+ void SendAkill(User *u, XLine *x) override
{
// Calculate the time left before this would expire, capping it at 2 days
- time_t timeleft = x->expires - Anope::CurTime;
- if (timeleft > 172800 || !x->expires)
+ time_t timeleft = x->GetExpires() - Anope::CurTime;
+ if (timeleft > 172800 || !x->GetExpires())
timeleft = 172800;
- UplinkSocket::Message(Me) << "GLINE " << x->mask << " " << timeleft << " :" << x->GetReason() << " (" << x->by << ")";
+ Uplink::Send(Me, "GLINE", x->GetMask(), timeleft, x->GetReason() + " (" + x->GetBy() + ")");
}
- void SendAkillDel(const XLine *x) anope_override
+ void SendAkillDel(XLine *x) override
{
- UplinkSocket::Message(Me) << "GLINE " << x->mask;
+ Uplink::Send(Me, "GLINE", x->GetMask());
}
- void SendChannel(Channel *c) anope_override
+ void SendChannel(Channel *c) override
{
- UplinkSocket::Message(Me) << "CHANINFO " << c->name << " +" << c->GetModes(true, true);
+ Uplink::Send(Me, "CHANINFO", c->name, "+" + c->GetModes(true, true));
}
// Received: :dev.anope.de NICK DukeP 1 ~DukePyro p57ABF9C9.dip.t-dialin.net 1 +i :DukePyrolator
- void SendClientIntroduction(User *u) anope_override
+ void SendClientIntroduction(User *u) override
{
Anope::string modes = "+" + u->GetModes();
- UplinkSocket::Message(Me) << "NICK " << u->nick << " 1 " << u->GetIdent() << " " << u->host << " 1 " << modes << " :" << u->realname;
+ Uplink::Send(Me, "NICK", u->nick, 1, u->GetIdent(), u->host, 1, modes, u->realname);
}
- void SendConnect() anope_override
+ void SendConnect() override
{
- UplinkSocket::Message() << "PASS " << Config->Uplinks[Anope::CurrentUplink].password << " 0210-IRC+ Anope|" << Anope::VersionShort() << ":CLHMSo P";
+ Uplink::Send("PASS", Config->Uplinks[Anope::CurrentUplink].password, "0210-IRC+", "Anope|" + Anope::VersionShort(), "CLHMSo P");
/* Make myself known to myself in the serverlist */
SendServer(Me);
/* finish the enhanced server handshake and register the connection */
this->SendNumeric(376, "*", ":End of MOTD command");
}
- void SendForceNickChange(User *u, const Anope::string &newnick, time_t when) anope_override
+ void SendForceNickChange(User *u, const Anope::string &newnick, time_t when) override
{
- UplinkSocket::Message(Me) << "SVSNICK " << u->nick << " " << newnick;
+ Uplink::Send(Me, "SVSNICK", u->nick, newnick);
}
- void SendGlobalNotice(BotInfo *bi, const Server *dest, const Anope::string &msg) anope_override
+ void SendGlobalNotice(ServiceBot *bi, const Server *dest, const Anope::string &msg) override
{
- UplinkSocket::Message(bi) << "NOTICE $" << dest->GetName() << " :" << msg;
+ Uplink::Send(bi, "NOTICE", "$" + dest->GetName(), msg);
}
- void SendGlobalPrivmsg(BotInfo *bi, const Server *dest, const Anope::string &msg) anope_override
+ void SendGlobalPrivmsg(ServiceBot *bi, const Server *dest, const Anope::string &msg) override
{
- UplinkSocket::Message(bi) << "PRIVMSG $" << dest->GetName() << " :" << msg;
+ Uplink::Send(bi, "PRIVMSG", "$" + dest->GetName(), msg);
}
- void SendGlobopsInternal(const MessageSource &source, const Anope::string &buf) anope_override
+ void SendGlobopsInternal(const MessageSource &source, const Anope::string &buf) override
{
- UplinkSocket::Message(source) << "WALLOPS :" << buf;
+ Uplink::Send(source, "WALLOPS", buf);
}
- void SendJoin(User *user, Channel *c, const ChannelStatus *status) anope_override
+ void SendJoin(User *user, Channel *c, const ChannelStatus *status) override
{
- UplinkSocket::Message(user) << "JOIN " << c->name;
+ Uplink::Send(user, "JOIN", c->name);
if (status)
{
/* First save the channel status incase uc->Status == status */
@@ -99,7 +108,7 @@ class ngIRCdProto : public IRCDProto
if (uc != NULL)
uc->status.Clear();
- BotInfo *setter = BotInfo::Find(user->GetUID());
+ ServiceBot *setter = ServiceBot::Find(user->GetUID());
for (size_t i = 0; i < cs.Modes().length(); ++i)
c->SetMode(setter, ModeManager::FindChannelModeByChar(cs.Modes()[i]), user->GetUID(), false);
@@ -108,54 +117,56 @@ class ngIRCdProto : public IRCDProto
}
}
- void SendKickInternal(const MessageSource &source, const Channel *chan, User *user, const Anope::string &buf) anope_override
+ void SendKickInternal(const MessageSource &source, const Channel *chan, User *user, const Anope::string &buf) override
{
if (!buf.empty())
- UplinkSocket::Message(source) << "KICK " << chan->name << " " << user->nick << " :" << buf;
+ Uplink::Send(source, "KICK", chan->name, user->nick, buf);
else
- UplinkSocket::Message(source) << "KICK " << chan->name << " " << user->nick;
+ Uplink::Send(source, "KICK", chan->name, user->nick);
}
- void SendLogin(User *u, NickAlias *na) anope_override
+ void SendLogin(User *u, NickServ::Nick *na) override
{
- UplinkSocket::Message(Me) << "METADATA " << u->GetUID() << " accountname :" << na->nc->display;
+ Uplink::Send(Me, "METADATA", u->GetUID(), "accountname", na->GetAccount()->GetDisplay());
}
- void SendLogout(User *u) anope_override
+ void SendLogout(User *u) override
{
- UplinkSocket::Message(Me) << "METADATA " << u->GetUID() << " accountname :";
- }
+ Uplink::Send(Me, "METADATA", u->GetUID(), "accountname", "");
+ }
- void SendModeInternal(const MessageSource &source, const Channel *dest, const Anope::string &buf) anope_override
+ void SendModeInternal(const MessageSource &source, const Channel *dest, const Anope::string &buf) override
{
- UplinkSocket::Message(source) << "MODE " << dest->name << " " << buf;
+ IRCMessage message(source, "MODE", dest->name);
+ message.TokenizeAndPush(buf);
+ Uplink::SendMessage(message);
}
- void SendPartInternal(User *u, const Channel *chan, const Anope::string &buf) anope_override
+ void SendPartInternal(User *u, const Channel *chan, const Anope::string &buf) override
{
if (!buf.empty())
- UplinkSocket::Message(u) << "PART " << chan->name << " :" << buf;
+ Uplink::Send(u, "PART", chan->name, buf);
else
- UplinkSocket::Message(u) << "PART " << chan->name;
+ Uplink::Send(u, "PART", chan->name);
}
/* SERVER name hop descript */
- void SendServer(const Server *server) anope_override
+ void SendServer(const Server *server) override
{
- UplinkSocket::Message() << "SERVER " << server->GetName() << " " << server->GetHops() << " :" << server->GetDescription();
+ Uplink::Send("SERVER", server->GetName(), server->GetHops(), server->GetDescription());
}
- void SendTopic(const MessageSource &source, Channel *c) anope_override
+ void SendTopic(const MessageSource &source, Channel *c) override
{
- UplinkSocket::Message(source) << "TOPIC " << c->name << " :" << c->topic;
+ Uplink::Send(source, "TOPIC", c->name, c->topic);
}
- void SendVhost(User *u, const Anope::string &vIdent, const Anope::string &vhost) anope_override
+ void SendVhost(User *u, const Anope::string &vIdent, const Anope::string &vhost) override
{
if (!vIdent.empty())
- UplinkSocket::Message(Me) << "METADATA " << u->nick << " user :" << vIdent;
+ Uplink::Send(Me, "METADATA", u->nick, "user", vIdent);
- UplinkSocket::Message(Me) << "METADATA " << u->nick << " cloakhost :" << vhost;
+ Uplink::Send(Me, "METADATA", u->nick, "cloakhost", vhost);
if (!u->HasMode("CLOAK"))
{
u->SetMode(Config->GetClient("HostServ"), "CLOAK");
@@ -163,14 +174,16 @@ class ngIRCdProto : public IRCDProto
}
}
- void SendVhostDel(User *u) anope_override
+ void SendVhostDel(User *u) override
{
this->SendVhost(u, u->GetIdent(), "");
}
- Anope::string Format(const Anope::string &source, const Anope::string &message) anope_override
+ Anope::string Format(IRCMessage &message)
{
- return IRCDProto::Format(source.empty() ? Me->GetSID() : source, message);
+ if (message.GetSource().GetSource().empty())
+ message.SetSource(Me);
+ return IRCDProto::Format(message);
}
};
@@ -179,7 +192,7 @@ struct IRCDMessage005 : IRCDMessage
IRCDMessage005(Module *creator) : IRCDMessage(creator, "005", 1) { SetFlag(IRCDMESSAGE_SOFT_LIMIT); }
// Please see <http://www.irc.org/tech_docs/005.html> for details.
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
{
size_t pos;
Anope::string parameter, data;
@@ -220,7 +233,7 @@ struct IRCDMessage376 : IRCDMessage
*
*/
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
{
}
};
@@ -246,7 +259,7 @@ struct IRCDMessageChaninfo : IRCDMessage
* a channel has no user limit (the parameter <modes> doesn't list the "l"
* channel mode). In this case <limit> should be "0".
*/
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
{
bool created;
Channel *c = Channel::FindOrCreate(params[0], created);
@@ -288,7 +301,7 @@ struct IRCDMessageJoin : Message::Join
*
* if a user joins a new channel, the ircd sends <channelname>\7<umode>
*/
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
{
User *user = source.GetUser();
size_t pos = params[0].find('\7');
@@ -338,7 +351,7 @@ struct IRCDMessageMetadata : IRCDMessage
* - "user": the user name (ident) of a client (can't be empty)
*/
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
{
User *u = User::Find(params[0]);
if (!u)
@@ -348,14 +361,14 @@ struct IRCDMessageMetadata : IRCDMessage
}
if (params[1].equals_cs("accountname"))
{
- NickCore *nc = NickCore::Find(params[2]);
+ NickServ::Account *nc = NickServ::FindAccount(params[2]);
if (nc)
u->Login(nc);
}
else if (params[1].equals_cs("certfp"))
{
u->fingerprint = params[2];
- FOREACH_MOD(OnFingerprint, (u));
+ EventManager::Get()->Dispatch(&Event::Fingerprint::OnFingerprint, u);
}
else if (params[1].equals_cs("cloakhost"))
{
@@ -389,7 +402,7 @@ struct IRCDMessageMode : IRCDMessage
* params[n] = parameters
*/
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
{
Anope::string modes = params[1];
@@ -436,7 +449,7 @@ struct IRCDMessageNick : IRCDMessage
* params[0] = newnick
*
*/
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
{
if (params.size() == 1)
{
@@ -477,7 +490,7 @@ struct IRCDMessageNJoin : IRCDMessage
*
* Received: :dev.anope.de NJOIN #test :DukeP2,@DukeP,%test,+test2
*/
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
{
std::list<Message::Join::SJoinUser> users;
@@ -502,7 +515,7 @@ struct IRCDMessageNJoin : IRCDMessage
continue;
}
users.push_back(sju);
- }
+ }
Message::Join::SJoin(source, params[0], 0, "", users);
}
@@ -517,7 +530,7 @@ struct IRCDMessagePong : IRCDMessage
* when receiving a new server and then finish sync once we
* get a pong back from that server.
*/
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
{
if (!source.GetServer()->IsSynced())
source.GetServer()->Sync(false);
@@ -553,7 +566,7 @@ struct IRCDMessageServer : IRCDMessage
* params[3] = server description
*/
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
{
if (params.size() == 3)
{
@@ -580,7 +593,7 @@ struct IRCDMessageTopic : IRCDMessage
IRCDMessageTopic(Module *creator) : IRCDMessage(creator, "TOPIC", 2) { SetFlag(IRCDMESSAGE_SOFT_LIMIT); }
// Received: :DukeP TOPIC #anope :test
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
{
Channel *c = Channel::Find(params[0]);
if (!c)
@@ -595,6 +608,7 @@ struct IRCDMessageTopic : IRCDMessage
class ProtongIRCd : public Module
+ , public EventHook<Event::UserNickChange>
{
ngIRCdProto ircd_proto;
@@ -629,71 +643,46 @@ class ProtongIRCd : public Module
IRCDMessageServer message_server;
IRCDMessageTopic message_topic;
- void AddModes()
- {
- /* Add user modes */
- ModeManager::AddUserMode(new UserMode("NOCTCP", 'b'));
- ModeManager::AddUserMode(new UserMode("BOT", 'B'));
- ModeManager::AddUserMode(new UserMode("COMMONCHANS", 'C'));
- ModeManager::AddUserMode(new UserMode("INVIS", 'i'));
- ModeManager::AddUserMode(new UserModeOperOnly("OPER", 'o'));
- ModeManager::AddUserMode(new UserModeOperOnly("PROTECTED", 'q'));
- ModeManager::AddUserMode(new UserModeOperOnly("RESTRICTED", 'r'));
- ModeManager::AddUserMode(new UserModeNoone("REGISTERED", 'R'));
- ModeManager::AddUserMode(new UserModeOperOnly("SNOMASK", 's'));
- ModeManager::AddUserMode(new UserMode("WALLOPS", 'w'));
- ModeManager::AddUserMode(new UserMode("CLOAK", 'x'));
-
- /* Add modes for ban, exception, and invite lists */
- ModeManager::AddChannelMode(new ChannelModeList("BAN", 'b'));
- ModeManager::AddChannelMode(new ChannelModeList("EXCEPT", 'e'));
- ModeManager::AddChannelMode(new ChannelModeList("INVITEOVERRIDE", 'I'));
-
- /* Add channel user modes */
- ModeManager::AddChannelMode(new ChannelModeStatus("VOICE", 'v', '+', 0));
- ModeManager::AddChannelMode(new ChannelModeStatus("HALFOP", 'h', '%', 1));
- ModeManager::AddChannelMode(new ChannelModeStatus("OP", 'o', '@', 2));
- ModeManager::AddChannelMode(new ChannelModeStatus("PROTECT", 'a', '&', 3));
- ModeManager::AddChannelMode(new ChannelModeStatus("OWNER", 'q', '~', 4));
-
- /* Add channel modes */
- ModeManager::AddChannelMode(new ChannelMode("INVITE", 'i'));
- ModeManager::AddChannelMode(new ChannelModeKey('k'));
- ModeManager::AddChannelMode(new ChannelModeParam("LIMIT", 'l', true));
- ModeManager::AddChannelMode(new ChannelMode("MODERATED", 'm'));
- ModeManager::AddChannelMode(new ChannelMode("REGMODERATED", 'M'));
- ModeManager::AddChannelMode(new ChannelMode("NOEXTERNAL", 'n'));
- ModeManager::AddChannelMode(new ChannelMode("OPERONLY", 'O'));
- ModeManager::AddChannelMode(new ChannelMode("PERM", 'P'));
- ModeManager::AddChannelMode(new ChannelMode("NOKICK", 'Q'));
- ModeManager::AddChannelMode(new ChannelModeNoone("REGISTERED", 'r'));
- ModeManager::AddChannelMode(new ChannelMode("REGISTEREDONLY", 'R'));
- ModeManager::AddChannelMode(new ChannelMode("SECRET", 's'));
- ModeManager::AddChannelMode(new ChannelMode("TOPIC", 't'));
- ModeManager::AddChannelMode(new ChannelMode("NOINVITE", 'V'));
- ModeManager::AddChannelMode(new ChannelMode("SSL", 'z'));
- }
-
public:
- ProtongIRCd(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, PROTOCOL | VENDOR),
- ircd_proto(this),
- message_capab(this), message_error(this), message_invite(this), message_kick(this), message_kill(this),
- message_motd(this), message_notice(this), message_part(this), message_ping(this), message_privmsg(this),
- message_squery(this, "SQUERY"), message_quit(this), message_squit(this), message_stats(this), message_time(this),
- message_version(this), message_whois(this),
-
- message_005(this), message_376(this), message_chaninfo(this), message_join(this), message_metadata(this),
- message_mode(this), message_nick(this), message_njoin(this), message_pong(this), message_server(this),
- message_topic(this)
+ ProtongIRCd(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, PROTOCOL | VENDOR)
+ , EventHook<Event::UserNickChange>(this)
+ , ircd_proto(this)
+ , message_capab(this)
+ , message_error(this)
+ , message_invite(this)
+ , message_kick(this)
+ , message_kill(this)
+ , message_motd(this)
+ , message_notice(this)
+ , message_part(this)
+ , message_ping(this)
+ , message_privmsg(this)
+ , message_squery(this, "SQUERY")
+ , message_quit(this)
+ , message_squit(this)
+ , message_stats(this)
+ , message_time(this)
+ , message_version(this)
+ , message_whois(this)
+
+ , message_005(this)
+ , message_376(this)
+ , message_chaninfo(this)
+ , message_join(this)
+ , message_metadata(this)
+ , message_mode(this)
+ , message_nick(this)
+ , message_njoin(this)
+ , message_pong(this)
+ , message_server(this)
+ , message_topic(this)
{
Servers::Capab.insert("QS");
- this->AddModes();
-
}
- void OnUserNickChange(User *u, const Anope::string &) anope_override
+ void OnUserNickChange(User *u, const Anope::string &) override
{
u->RemoveMode(Config->GetClient("NickServ"), "REGISTERED");
}
diff --git a/modules/protocol/plexus.cpp b/modules/protocol/plexus.cpp
index eeb22d4fe..02d2a4d6e 100644
--- a/modules/protocol/plexus.cpp
+++ b/modules/protocol/plexus.cpp
@@ -1,24 +1,37 @@
-/* Plexus 3+ IRCD functions
+/*
+ * Anope IRC Services
*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2005-2016 Anope Team <team@anope.org>
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
*
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
+/* Dependencies: anope_protocol.hybrid */
+
#include "module.h"
+#include "modules/protocol/plexus.h"
+#include "modules/protocol/hybrid.h"
static Anope::string UplinkSID;
-static ServiceReference<IRCDProto> hybrid("IRCDProto", "hybrid");
-
class PlexusProto : public IRCDProto
{
+ ServiceReference<IRCDProto> hybrid; // XXX use moddeps + inheritance here
+
public:
PlexusProto(Module *creator) : IRCDProto(creator, "hybrid-7.2.3+plexus-3.0.1")
+ , hybrid("hybrid")
{
DefaultPseudoclientModes = "+oiU";
CanSVSNick = true;
@@ -34,28 +47,28 @@ class PlexusProto : public IRCDProto
MaxModes = 4;
}
- void SendSVSKillInternal(const MessageSource &source, User *targ, const Anope::string &reason) anope_override { hybrid->SendSVSKillInternal(source, targ, reason); }
- void SendGlobalNotice(BotInfo *bi, const Server *dest, const Anope::string &msg) anope_override { hybrid->SendGlobalNotice(bi, dest, msg); }
- void SendGlobalPrivmsg(BotInfo *bi, const Server *dest, const Anope::string &msg) anope_override { hybrid->SendGlobalPrivmsg(bi, dest, msg); }
- void SendSQLine(User *u, const XLine *x) anope_override { hybrid->SendSQLine(u, x); }
- void SendSQLineDel(const XLine *x) anope_override { hybrid->SendSQLineDel(x); }
- void SendSGLineDel(const XLine *x) anope_override { hybrid->SendSGLineDel(x); }
- void SendSGLine(User *u, const XLine *x) anope_override { hybrid->SendSGLine(u, x); }
- void SendAkillDel(const XLine *x) anope_override { hybrid->SendAkillDel(x); }
- void SendAkill(User *u, XLine *x) anope_override { hybrid->SendAkill(u, x); }
- void SendServer(const Server *server) anope_override { hybrid->SendServer(server); }
- void SendChannel(Channel *c) anope_override { hybrid->SendChannel(c); }
- void SendSVSHold(const Anope::string &nick, time_t t) anope_override { hybrid->SendSVSHold(nick, t); }
- void SendSVSHoldDel(const Anope::string &nick) anope_override { hybrid->SendSVSHoldDel(nick); }
-
- void SendGlobopsInternal(const MessageSource &source, const Anope::string &buf) anope_override
+ void SendSVSKillInternal(const MessageSource &source, User *targ, const Anope::string &reason) override { hybrid->SendSVSKillInternal(source, targ, reason); }
+ void SendGlobalNotice(ServiceBot *bi, const Server *dest, const Anope::string &msg) override { hybrid->SendGlobalNotice(bi, dest, msg); }
+ void SendGlobalPrivmsg(ServiceBot *bi, const Server *dest, const Anope::string &msg) override { hybrid->SendGlobalPrivmsg(bi, dest, msg); }
+ void SendSQLine(User *u, XLine *x) override { hybrid->SendSQLine(u, x); }
+ void SendSQLineDel(XLine *x) override { hybrid->SendSQLineDel(x); }
+ void SendSGLineDel(XLine *x) override { hybrid->SendSGLineDel(x); }
+ void SendSGLine(User *u, XLine *x) override { hybrid->SendSGLine(u, x); }
+ void SendAkillDel(XLine *x) override { hybrid->SendAkillDel(x); }
+ void SendAkill(User *u, XLine *x) override { hybrid->SendAkill(u, x); }
+ void SendServer(const Server *server) override { hybrid->SendServer(server); }
+ void SendChannel(Channel *c) override { hybrid->SendChannel(c); }
+ void SendSVSHold(const Anope::string &nick, time_t t) override { hybrid->SendSVSHold(nick, t); }
+ void SendSVSHoldDel(const Anope::string &nick) override { hybrid->SendSVSHoldDel(nick); }
+
+ void SendGlobopsInternal(const MessageSource &source, const Anope::string &buf) override
{
- UplinkSocket::Message(source) << "OPERWALL :" << buf;
+ Uplink::Send(source, "OPERWALL", buf);
}
- void SendJoin(User *user, Channel *c, const ChannelStatus *status) anope_override
+ void SendJoin(User *user, Channel *c, const ChannelStatus *status) override
{
- UplinkSocket::Message(Me) << "SJOIN " << c->creation_time << " " << c->name << " +" << c->GetModes(true, true) << " :" << user->GetUID();
+ Uplink::Send(Me, "SJOIN", c->creation_time, c->name, "+" + c->GetModes(true, true), user->GetUID());
if (status)
{
/* First save the channel status incase uc->Status == status */
@@ -67,7 +80,7 @@ class PlexusProto : public IRCDProto
if (uc != NULL)
uc->status.Clear();
- BotInfo *setter = BotInfo::Find(user->GetUID());
+ ServiceBot *setter = ServiceBot::Find(user->GetUID());
for (size_t i = 0; i < cs.Modes().length(); ++i)
c->SetMode(setter, ModeManager::FindChannelModeByChar(cs.Modes()[i]), user->GetUID(), false);
@@ -76,27 +89,28 @@ class PlexusProto : public IRCDProto
}
}
- void SendForceNickChange(User *u, const Anope::string &newnick, time_t when) anope_override
+ void SendForceNickChange(User *u, const Anope::string &newnick, time_t when) override
{
- UplinkSocket::Message(Me) << "ENCAP " << u->server->GetName() << " SVSNICK " << u->GetUID() << " " << u->timestamp << " " << newnick << " " << when;
+ Uplink::Send(Me, "ENCAP", u->server->GetName(), "SVSNICK", u->GetUID(), u->timestamp, newnick, when);
}
- void SendVhost(User *u, const Anope::string &ident, const Anope::string &host) anope_override
+ void SendVhost(User *u, const Anope::string &ident, const Anope::string &host) override
{
if (!ident.empty())
- UplinkSocket::Message(Me) << "ENCAP * CHGIDENT " << u->GetUID() << " " << ident;
- UplinkSocket::Message(Me) << "ENCAP * CHGHOST " << u->GetUID() << " " << host;
+ Uplink::Send(Me, "ENCAP", "*", "CHGIDENT", u->GetUID(), ident);
+ Uplink::Send(Me, "ENCAP", "*", "CHGHOST", u->GetUID(), host);
u->SetMode(Config->GetClient("HostServ"), "CLOAK");
}
- void SendVhostDel(User *u) anope_override
+ void SendVhostDel(User *u) override
{
u->RemoveMode(Config->GetClient("HostServ"), "CLOAK");
}
- void SendConnect() anope_override
+ void SendConnect() override
{
- UplinkSocket::Message() << "PASS " << Config->Uplinks[Anope::CurrentUplink].password << " TS 6 :" << Me->GetSID();
+ Uplink::Send("PASS", Config->Uplinks[Anope::CurrentUplink].password, "TS", 6, Me->GetSID());
+
/* CAPAB
* QS - Can handle quit storm removal
* EX - Can do channel +e exemptions
@@ -117,9 +131,11 @@ class PlexusProto : public IRCDProto
* ENCAP - Supports encapsulization of protocol messages
* SVS - Supports services protocol extensions
*/
- UplinkSocket::Message() << "CAPAB :QS EX CHW IE EOB KLN UNKLN GLN HUB KNOCK TBURST PARA ENCAP SVS";
+ Uplink::Send("CAPAB", "QS EX CHW IE EOB KLN UNKLN GLN HUB KNOCK TBURST PARA ENCAP SVS");
+
/* Make myself known to myself in the serverlist */
SendServer(Me);
+
/*
* SVINFO
* parv[0] = sender prefix
@@ -128,163 +144,148 @@ class PlexusProto : public IRCDProto
* parv[3] = server is standalone or connected to non-TS only
* parv[4] = server's idea of UTC time
*/
- UplinkSocket::Message() << "SVINFO 6 5 0 :" << Anope::CurTime;
+ Uplink::Send("SVINFO", 6, 6, 0, Anope::CurTime);
}
- void SendClientIntroduction(User *u) anope_override
+ void SendClientIntroduction(User *u) override
{
Anope::string modes = "+" + u->GetModes();
- UplinkSocket::Message(Me) << "UID " << u->nick << " 1 " << u->timestamp << " " << modes << " " << u->GetIdent() << " " << u->host << " 255.255.255.255 " << u->GetUID() << " 0 " << u->host << " :" << u->realname;
+ Uplink::Send(Me, "UID", u->nick, 1, u->timestamp, modes, u->GetIdent(), u->host, "255.255.255.255", u->GetUID(), 0, u->host, u->realname);
}
- void SendModeInternal(const MessageSource &source, User *u, const Anope::string &buf) anope_override
+ void SendModeInternal(const MessageSource &source, User *u, const Anope::string &buf) override
{
- UplinkSocket::Message(source) << "ENCAP * SVSMODE " << u->GetUID() << " " << u->timestamp << " " << buf;
+ Uplink::Send(source, "ENCAP", "*", "SVSMODE", u->GetUID(), u->timestamp, buf);
}
- void SendLogin(User *u, NickAlias *na) anope_override
+ void SendLogin(User *u, NickServ::Nick *na) override
{
- UplinkSocket::Message(Me) << "ENCAP * SU " << u->GetUID() << " " << na->nc->display;
+ Uplink::Send(Me, "ENCAP", "*", "SU", u->GetUID(), na->GetAccount()->GetDisplay());
}
- void SendLogout(User *u) anope_override
+ void SendLogout(User *u) override
{
- UplinkSocket::Message(Me) << "ENCAP * SU " << u->GetUID();
+ Uplink::Send(Me, "ENCAP", "*", "SU", u->GetUID(), "");
}
- void SendTopic(const MessageSource &source, Channel *c) anope_override
+ void SendTopic(const MessageSource &source, Channel *c) override
{
- UplinkSocket::Message(source) << "ENCAP * TOPIC " << c->name << " " << c->topic_setter << " " << c->topic_ts << " :" << c->topic;
+ Uplink::Send(source, "ENCAP", "*", "TOPIC", c->name, c->topic_setter, c->topic_ts, c->topic);
}
- void SendSVSJoin(const MessageSource &source, User *user, const Anope::string &chan, const Anope::string &param) anope_override
+ void SendSVSJoin(const MessageSource &source, User *user, const Anope::string &chan, const Anope::string &param) override
{
- UplinkSocket::Message(source) << "ENCAP " << user->server->GetName() << " SVSJOIN " << user->GetUID() << " " << chan;
+ Uplink::Send(source, "ENCAP", user->server->GetName(), "SVSJOIN", user->GetUID(), chan);
}
- void SendSVSPart(const MessageSource &source, User *user, const Anope::string &chan, const Anope::string &param) anope_override
+ void SendSVSPart(const MessageSource &source, User *user, const Anope::string &chan, const Anope::string &param) override
{
- UplinkSocket::Message(source) << "ENCAP " << user->server->GetName() << " SVSPART " << user->GetUID() << " " << chan;
+ Uplink::Send(source, "ENCAP", user->server->GetName(), "SVSPART", user->GetUID(), chan);
}
};
-struct IRCDMessageEncap : IRCDMessage
+void plexus::Encap::Run(MessageSource &source, const std::vector<Anope::string> &params)
{
- IRCDMessageEncap(Module *creator) : IRCDMessage(creator, "ENCAP", 4) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
-
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ /*
+ * Received: :dev.anope.de ENCAP * SU DukePyrolator DukePyrolator
+ * params[0] = *
+ * params[1] = SU
+ * params[2] = nickname
+ * params[3] = account
+ */
+ if (params[1].equals_cs("SU"))
{
- /*
- * Received: :dev.anope.de ENCAP * SU DukePyrolator DukePyrolator
- * params[0] = *
- * params[1] = SU
- * params[2] = nickname
- * params[3] = account
- */
- if (params[1].equals_cs("SU"))
+ User *u = User::Find(params[2]);
+ NickServ::Account *nc = NickServ::FindAccount(params[3]);
+ if (u && nc)
{
- User *u = User::Find(params[2]);
- NickCore *nc = NickCore::Find(params[3]);
- if (u && nc)
- {
- u->Login(nc);
- }
+ u->Login(nc);
}
+ }
- /*
- * Received: :dev.anope.de ENCAP * CERTFP DukePyrolator :3F122A9CC7811DBAD3566BF2CEC3009007C0868F
- * params[0] = *
- * params[1] = CERTFP
- * params[2] = nickname
- * params[3] = fingerprint
- */
- else if (params[1].equals_cs("CERTFP"))
+ /*
+ * Received: :dev.anope.de ENCAP * CERTFP DukePyrolator :3F122A9CC7811DBAD3566BF2CEC3009007C0868F
+ * params[0] = *
+ * params[1] = CERTFP
+ * params[2] = nickname
+ * params[3] = fingerprint
+ */
+ else if (params[1].equals_cs("CERTFP"))
+ {
+ User *u = User::Find(params[2]);
+ if (u)
{
- User *u = User::Find(params[2]);
- if (u)
- {
- u->fingerprint = params[3];
- FOREACH_MOD(OnFingerprint, (u));
- }
+ u->fingerprint = params[3];
+ EventManager::Get()->Dispatch(&Event::Fingerprint::OnFingerprint, u);
}
- return;
}
-};
+ return;
+}
struct IRCDMessagePass : IRCDMessage
{
IRCDMessagePass(Module *creator) : IRCDMessage(creator, "PASS", 4) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
{
UplinkSID = params[3];
}
};
-struct IRCDMessageServer : IRCDMessage
+/* 0 1 2 */
+/* SERVER hades.arpa 1 :ircd-hybrid test server */
+void plexus::Server::Run(MessageSource &source, const std::vector<Anope::string> &params)
{
- IRCDMessageServer(Module *creator) : IRCDMessage(creator, "SERVER", 3) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
-
- /* 0 1 2 */
- /* SERVER hades.arpa 1 :ircd-hybrid test server */
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- /* Servers other than our immediate uplink are introduced via SID */
- if (params[1] != "1")
- return;
-
- new Server(source.GetServer() == NULL ? Me : source.GetServer(), params[0], 1, params[2], UplinkSID);
- }
-};
+ /* Servers other than our immediate uplink are introduced via SID */
+ if (params[1] != "1")
+ return;
-struct IRCDMessageUID : IRCDMessage
+ new ::Server(source.GetServer() == NULL ? Me : source.GetServer(), params[0], 1, params[2], UplinkSID);
+}
+
+/*
+ params[0] = nick
+ params[1] = hop
+ params[2] = ts
+ params[3] = modes
+ params[4] = user
+ params[5] = host
+ params[6] = IP
+ params[7] = UID
+ params[8] = services stamp
+ params[9] = realhost
+ params[10] = info
+*/
+// :42X UID Adam 1 1348535644 +aow Adam 192.168.0.5 192.168.0.5 42XAAAAAB 0 192.168.0.5 :Adam
+void plexus::UID::Run(MessageSource &source, const std::vector<Anope::string> &params)
{
- IRCDMessageUID(Module *creator) : IRCDMessage(creator, "UID", 11) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
+ /* An IP of 0 means the user is spoofed */
+ Anope::string ip = params[6];
+ if (ip == "0")
+ ip.clear();
- /*
- params[0] = nick
- params[1] = hop
- params[2] = ts
- params[3] = modes
- params[4] = user
- params[5] = host
- params[6] = IP
- params[7] = UID
- params[8] = services stamp
- params[9] = realhost
- params[10] = info
- */
- // :42X UID Adam 1 1348535644 +aow Adam 192.168.0.5 192.168.0.5 42XAAAAAB 0 192.168.0.5 :Adam
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ time_t ts;
+ try
{
- /* An IP of 0 means the user is spoofed */
- Anope::string ip = params[6];
- if (ip == "0")
- ip.clear();
-
- time_t ts;
- try
- {
- ts = convertTo<time_t>(params[2]);
- }
- catch (const ConvertException &)
- {
- ts = Anope::CurTime;
- }
-
- NickAlias *na = NULL;
- try
- {
- if (params[8].is_pos_number_only() && convertTo<time_t>(params[8]) == ts)
- na = NickAlias::Find(params[0]);
- }
- catch (const ConvertException &) { }
- if (params[8] != "0" && !na)
- na = NickAlias::Find(params[8]);
+ ts = convertTo<time_t>(params[2]);
+ }
+ catch (const ConvertException &)
+ {
+ ts = Anope::CurTime;
+ }
- User::OnIntroduce(params[0], params[4], params[9], params[5], ip, source.GetServer(), params[10], ts, params[3], params[7], na ? *na->nc : NULL);
+ NickServ::Nick *na = NULL;
+ try
+ {
+ if (params[8].is_pos_number_only() && convertTo<time_t>(params[8]) == ts)
+ na = NickServ::FindNick(params[0]);
}
-};
+ catch (const ConvertException &) { }
+ if (params[8] != "0" && !na)
+ na = NickServ::FindNick(params[8]);
+
+ User::OnIntroduce(params[0], params[4], params[9], params[5], ip, source.GetServer(), params[10], ts, params[3], params[7], na ? na->GetAccount() : NULL);
+}
class ProtoPlexus : public Module
{
@@ -312,108 +313,63 @@ class ProtoPlexus : public Module
Message::Topic message_topic;
Message::Version message_version;
Message::Whois message_whois;
-
- /* Hybrid message handlers */
- ServiceAlias message_bmask, message_eob, message_join, message_nick, message_sid, message_sjoin,
- message_tburst, message_tmode;
/* Our message handlers */
- IRCDMessageEncap message_encap;
+ hybrid::BMask message_bmask;
+ hybrid::EOB message_eob;
+ plexus::Encap message_encap;
+ hybrid::Join message_join;
+ hybrid::Nick message_nick;
IRCDMessagePass message_pass;
- IRCDMessageServer message_server;
- IRCDMessageUID message_uid;
-
- void AddModes()
- {
- /* Add user modes */
- ModeManager::AddUserMode(new UserModeOperOnly("ADMIN", 'a'));
- ModeManager::AddUserMode(new UserMode("NOCTCP", 'C'));
- ModeManager::AddUserMode(new UserMode("DEAF", 'D'));
- ModeManager::AddUserMode(new UserMode("SOFTCALLERID", 'G'));
- ModeManager::AddUserMode(new UserMode("CALLERID", 'g'));
- ModeManager::AddUserMode(new UserMode("INVIS", 'i'));
- ModeManager::AddUserMode(new UserModeOperOnly("LOCOPS", 'l'));
- ModeManager::AddUserMode(new UserModeOperOnly("OPER", 'o'));
- ModeManager::AddUserMode(new UserModeOperOnly("NETADMIN", 'N'));
- ModeManager::AddUserMode(new UserMode("PRIV", 'p'));
- ModeManager::AddUserMode(new UserModeOperOnly("ROUTING", 'q'));
- ModeManager::AddUserMode(new UserModeNoone("REGISTERED", 'r'));
- ModeManager::AddUserMode(new UserMode("REGPRIV", 'R'));
- ModeManager::AddUserMode(new UserModeOperOnly("SNOMASK", 's'));
- ModeManager::AddUserMode(new UserModeNoone("SSL", 'S'));
- ModeManager::AddUserMode(new UserModeNoone("PROTECTED", 'U'));
- ModeManager::AddUserMode(new UserMode("WALLOPS", 'w'));
- ModeManager::AddUserMode(new UserModeNoone("WEBIRC", 'W'));
- ModeManager::AddUserMode(new UserMode("CLOAK", 'x'));
- ModeManager::AddUserMode(new UserModeOperOnly("OPERWALLS", 'z'));
-
- /* b/e/I */
- ModeManager::AddChannelMode(new ChannelModeList("BAN", 'b'));
- ModeManager::AddChannelMode(new ChannelModeList("EXCEPT", 'e'));
- ModeManager::AddChannelMode(new ChannelModeList("INVITEOVERRIDE", 'I'));
-
- /* v/h/o/a/q */
- ModeManager::AddChannelMode(new ChannelModeStatus("VOICE", 'v', '+', 0));
- ModeManager::AddChannelMode(new ChannelModeStatus("HALFOP", 'h', '%', 1));
- ModeManager::AddChannelMode(new ChannelModeStatus("OP", 'o', '@', 2));
- ModeManager::AddChannelMode(new ChannelModeStatus("PROTECT", 'a', '&', 3));
- ModeManager::AddChannelMode(new ChannelModeStatus("OWNER", 'q', '~', 4));
-
- /* l/k */
- ModeManager::AddChannelMode(new ChannelModeParam("LIMIT", 'l', true));
- ModeManager::AddChannelMode(new ChannelModeKey('k'));
-
- /* Add channel modes */
- ModeManager::AddChannelMode(new ChannelMode("BANDWIDTH", 'B'));
- ModeManager::AddChannelMode(new ChannelMode("NOCTCP", 'C'));
- ModeManager::AddChannelMode(new ChannelMode("BLOCKCOLOR", 'c'));
- ModeManager::AddChannelMode(new ChannelMode("INVITE", 'i'));
- ModeManager::AddChannelMode(new ChannelMode("MODERATED", 'm'));
- ModeManager::AddChannelMode(new ChannelMode("REGMODERATED", 'M'));
- ModeManager::AddChannelMode(new ChannelMode("NOEXTERNAL", 'n'));
- ModeManager::AddChannelMode(new ChannelMode("NONOTICE", 'N'));
- ModeManager::AddChannelMode(new ChannelMode("PRIVATE", 'p'));
- ModeManager::AddChannelMode(new ChannelMode("SECRET", 's'));
- ModeManager::AddChannelMode(new ChannelMode("TOPIC", 't'));
- ModeManager::AddChannelMode(new ChannelModeOperOnly("OPERONLY", 'O'));
- ModeManager::AddChannelMode(new ChannelMode("REGISTEREDONLY", 'R'));
- ModeManager::AddChannelMode(new ChannelMode("SSL", 'S'));
- ModeManager::AddChannelMode(new ChannelMode("PERM", 'z'));
- }
+ plexus::Server message_server;
+ hybrid::SID message_sid;
+ hybrid::SJoin message_sjoin;
+ hybrid::TBurst message_tburst;
+ hybrid::TMode message_tmode;
+ plexus::UID message_uid;
public:
- ProtoPlexus(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, PROTOCOL | VENDOR),
- ircd_proto(this),
- message_away(this), message_capab(this), message_error(this), message_invite(this), message_kick(this), message_kill(this),
- message_mode(this), message_motd(this), message_notice(this), message_part(this), message_ping(this), message_privmsg(this),
- message_quit(this), message_squit(this), message_stats(this), message_time(this), message_topic(this), message_version(this),
- message_whois(this),
-
- message_bmask("IRCDMessage", "plexus/bmask", "hybrid/bmask"), message_eob("IRCDMessage", "plexus/eob", "hybrid/eob"),
- message_join("IRCDMessage", "plexus/join", "hybrid/join"), message_nick("IRCDMessage", "plexus/nick", "hybrid/nick"),
- message_sid("IRCDMessage", "plexus/sid", "hybrid/sid"),
- message_sjoin("IRCDMessage", "plexus/sjoin", "hybrid/sjoin"), message_tburst("IRCDMessage", "plexus/tburst", "hybrid/tburst"),
- message_tmode("IRCDMessage", "plexus/tmode", "hybrid/tmode"),
-
- message_encap(this), message_pass(this), message_server(this), message_uid(this)
+ ProtoPlexus(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, PROTOCOL | VENDOR)
+ , ircd_proto(this)
+ , message_away(this)
+ , message_capab(this)
+ , message_error(this)
+ , message_invite(this)
+ , message_kick(this)
+ , message_kill(this)
+ , message_mode(this)
+ , message_motd(this)
+ , message_notice(this)
+ , message_part(this)
+ , message_ping(this)
+ , message_privmsg(this)
+ , message_quit(this)
+ , message_squit(this)
+ , message_stats(this)
+ , message_time(this)
+ , message_topic(this)
+ , message_version(this)
+ , message_whois(this)
+
+ , message_bmask(this)
+ , message_eob(this)
+ , message_encap(this)
+ , message_join(this)
+ , message_nick(this)
+ , message_pass(this)
+ , message_server(this)
+ , message_sid(this)
+ , message_sjoin(this)
+ , message_tburst(this)
+ , message_tmode(this)
+ , message_uid(this)
{
-
- if (ModuleManager::LoadModule("hybrid", User::Find(creator)) != MOD_ERR_OK)
- throw ModuleException("Unable to load hybrid");
- m_hybrid = ModuleManager::FindModule("hybrid");
- if (!m_hybrid)
- throw ModuleException("Unable to find hybrid");
- if (!hybrid)
- throw ModuleException("No protocol interface for hybrid");
-
- this->AddModes();
- }
-
- ~ProtoPlexus()
- {
- m_hybrid = ModuleManager::FindModule("hybrid");
- ModuleManager::UnloadModule(m_hybrid, NULL);
}
};
+template<> void ModuleInfo<ProtoPlexus>(ModuleDef *def)
+{
+ def->Depends("hybrid");
+}
+
MODULE_INIT(ProtoPlexus)
diff --git a/modules/protocol/ratbox.cpp b/modules/protocol/ratbox.cpp
index d89bff602..223902457 100644
--- a/modules/protocol/ratbox.cpp
+++ b/modules/protocol/ratbox.cpp
@@ -1,24 +1,36 @@
-/* Ratbox IRCD functions
+/*
+ * Anope IRC Services
*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2005-2016 Anope Team <team@anope.org>
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
*
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
+/* Dependencies: anope_protocol.hybrid */
+
#include "module.h"
+#include "modules/protocol/hybrid.h"
+#include "modules/protocol/ratbox.h"
static Anope::string UplinkSID;
-static ServiceReference<IRCDProto> hybrid("IRCDProto", "hybrid");
-
class RatboxProto : public IRCDProto
{
+ ServiceReference<IRCDProto> hybrid; // XXX
public:
RatboxProto(Module *creator) : IRCDProto(creator, "Ratbox 3.0+")
+ , hybrid("hybrid")
{
DefaultPseudoclientModes = "+oiS";
CanSNLine = true;
@@ -28,29 +40,30 @@ class RatboxProto : public IRCDProto
MaxModes = 4;
}
- void SendSVSKillInternal(const MessageSource &source, User *targ, const Anope::string &reason) anope_override { hybrid->SendSVSKillInternal(source, targ, reason); }
- void SendGlobalNotice(BotInfo *bi, const Server *dest, const Anope::string &msg) anope_override { hybrid->SendGlobalNotice(bi, dest, msg); }
- void SendGlobalPrivmsg(BotInfo *bi, const Server *dest, const Anope::string &msg) anope_override { hybrid->SendGlobalPrivmsg(bi, dest, msg); }
- void SendSQLine(User *u, const XLine *x) anope_override { hybrid->SendSQLine(u, x); }
- void SendSGLine(User *u, const XLine *x) anope_override { hybrid->SendSGLine(u, x); }
- void SendSGLineDel(const XLine *x) anope_override { hybrid->SendSGLineDel(x); }
- void SendAkill(User *u, XLine *x) anope_override { hybrid->SendAkill(u, x); }
- void SendAkillDel(const XLine *x) anope_override { hybrid->SendAkillDel(x); }
- void SendSQLineDel(const XLine *x) anope_override { hybrid->SendSQLineDel(x); }
- void SendJoin(User *user, Channel *c, const ChannelStatus *status) anope_override { hybrid->SendJoin(user, c, status); }
- void SendServer(const Server *server) anope_override { hybrid->SendServer(server); }
- void SendModeInternal(const MessageSource &source, User *u, const Anope::string &buf) anope_override { hybrid->SendModeInternal(source, u, buf); }
- void SendChannel(Channel *c) anope_override { hybrid->SendChannel(c); }
- bool IsIdentValid(const Anope::string &ident) anope_override { return hybrid->IsIdentValid(ident); }
-
- void SendGlobopsInternal(const MessageSource &source, const Anope::string &buf) anope_override
+ void SendSVSKillInternal(const MessageSource &source, User *targ, const Anope::string &reason) override { hybrid->SendSVSKillInternal(source, targ, reason); }
+ void SendGlobalNotice(ServiceBot *bi, const Server *dest, const Anope::string &msg) override { hybrid->SendGlobalNotice(bi, dest, msg); }
+ void SendGlobalPrivmsg(ServiceBot *bi, const Server *dest, const Anope::string &msg) override { hybrid->SendGlobalPrivmsg(bi, dest, msg); }
+ void SendSQLine(User *u, XLine *x) override { hybrid->SendSQLine(u, x); }
+ void SendSGLine(User *u, XLine *x) override { hybrid->SendSGLine(u, x); }
+ void SendSGLineDel(XLine *x) override { hybrid->SendSGLineDel(x); }
+ void SendAkill(User *u, XLine *x) override { hybrid->SendAkill(u, x); }
+ void SendAkillDel(XLine *x) override { hybrid->SendAkillDel(x); }
+ void SendSQLineDel(XLine *x) override { hybrid->SendSQLineDel(x); }
+ void SendJoin(User *user, Channel *c, const ChannelStatus *status) override { hybrid->SendJoin(user, c, status); }
+ void SendServer(const Server *server) override { hybrid->SendServer(server); }
+ void SendModeInternal(const MessageSource &source, User *u, const Anope::string &buf) override { hybrid->SendModeInternal(source, u, buf); }
+ void SendChannel(Channel *c) override { hybrid->SendChannel(c); }
+ bool IsIdentValid(const Anope::string &ident) override { return hybrid->IsIdentValid(ident); }
+
+ void SendGlobopsInternal(const MessageSource &source, const Anope::string &buf) override
{
- UplinkSocket::Message(source) << "OPERWALL :" << buf;
+ Uplink::Send(source, "OPERWALL", buf);
}
- void SendConnect() anope_override
+ void SendConnect() override
{
- UplinkSocket::Message() << "PASS " << Config->Uplinks[Anope::CurrentUplink].password << " TS 6 :" << Me->GetSID();
+ Uplink::Send("PASS", Config->Uplinks[Anope::CurrentUplink].password, "TS", 6, Me->GetSID());
+
/*
QS - Can handle quit storm removal
EX - Can do channel +e exemptions
@@ -61,9 +74,11 @@ class RatboxProto : public IRCDProto
TB - supports topic burst
ENCAP - supports ENCAP
*/
- UplinkSocket::Message() << "CAPAB :QS EX CHW IE GLN TB ENCAP";
+ Uplink::Send("CAPAB", "QS EX CHW IE GLN TB ENCAP");
+
/* Make myself known to myself in the serverlist */
SendServer(Me);
+
/*
* SVINFO
* parv[0] = sender prefix
@@ -72,31 +87,31 @@ class RatboxProto : public IRCDProto
* parv[3] = server is standalone or connected to non-TS only
* parv[4] = server's idea of UTC time
*/
- UplinkSocket::Message() << "SVINFO 6 3 0 :" << Anope::CurTime;
+ Uplink::Send("SVINFO", 6, 6, 0, Anope::CurTime);
}
- void SendClientIntroduction(User *u) anope_override
+ void SendClientIntroduction(User *u) override
{
Anope::string modes = "+" + u->GetModes();
- UplinkSocket::Message(Me) << "UID " << u->nick << " 1 " << u->timestamp << " " << modes << " " << u->GetIdent() << " " << u->host << " 0 " << u->GetUID() << " :" << u->realname;
+ Uplink::Send(Me, "UID", u->nick, 1, u->timestamp, modes, u->GetIdent(), u->host, 0, u->GetUID(), u->realname);
}
- void SendLogin(User *u, NickAlias *na) anope_override
+ void SendLogin(User *u, NickServ::Nick *na) override
{
- if (na->nc->HasExt("UNCONFIRMED"))
+ if (na->GetAccount()->HasFieldS("UNCONFIRMED"))
return;
- UplinkSocket::Message(Me) << "ENCAP * SU " << u->GetUID() << " " << na->nc->display;
+ Uplink::Send(Me, "ENCAP", "*", "SU", u->GetUID(), na->GetAccount()->GetDisplay());
}
- void SendLogout(User *u) anope_override
+ void SendLogout(User *u) override
{
- UplinkSocket::Message(Me) << "ENCAP * SU " << u->GetUID();
+ Uplink::Send(Me, "ENCAP", "*", "SU", u->GetUID());
}
- void SendTopic(const MessageSource &source, Channel *c) anope_override
+ void SendTopic(const MessageSource &source, Channel *c) override
{
- BotInfo *bi = source.GetBot();
+ ServiceBot *bi = source.GetBot();
bool needjoin = c->FindUser(bi) == NULL;
if (needjoin)
@@ -114,116 +129,89 @@ class RatboxProto : public IRCDProto
}
};
-struct IRCDMessageEncap : IRCDMessage
+// Debug: Received: :00BAAAAAB ENCAP * LOGIN Adam
+void ratbox::Encap::Run(MessageSource &source, const std::vector<Anope::string> &params)
{
- IRCDMessageEncap(Module *creator) : IRCDMessage(creator, "ENCAP", 3) { SetFlag(IRCDMESSAGE_REQUIRE_USER); }
-
- // Debug: Received: :00BAAAAAB ENCAP * LOGIN Adam
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ if (params[1] == "LOGIN" || params[1] == "SU")
{
- if (params[1] == "LOGIN" || params[1] == "SU")
- {
- User *u = source.GetUser();
-
- NickCore *nc = NickCore::Find(params[2]);
- if (!nc)
- return;
- u->Login(nc);
-
- /* Sometimes a user connects, we send them the usual "this nickname is registered" mess (if
- * their server isn't syncing) and then we receive this.. so tell them about it.
- */
- if (u->server->IsSynced())
- u->SendMessage(Config->GetClient("NickServ"), _("You have been logged in as \002%s\002."), nc->display.c_str());
- }
+ User *u = source.GetUser();
+
+ NickServ::Account *nc = NickServ::FindAccount(params[2]);
+ if (!nc)
+ return;
+ u->Login(nc);
+
+ /* Sometimes a user connects, we send them the usual "this nickname is registered" mess (if
+ * their server isn't syncing) and then we receive this.. so tell them about it.
+ */
+ if (u->server->IsSynced())
+ u->SendMessage(Config->GetClient("NickServ"), _("You have been logged in as \002%s\002."), nc->GetDisplay().c_str());
}
-};
+}
-struct IRCDMessageJoin : Message::Join
+void ratbox::Join::Run(MessageSource &source, const std::vector<Anope::string> &params)
{
- IRCDMessageJoin(Module *creator) : Message::Join(creator, "JOIN") { }
+ if (params.size() == 1 && params[0] == "0")
+ return Message::Join::Run(source, params);
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (params.size() == 1 && params[0] == "0")
- return Message::Join::Run(source, params);
-
- if (params.size() < 2)
- return;
+ if (params.size() < 2)
+ return;
- std::vector<Anope::string> p = params;
- p.erase(p.begin());
+ std::vector<Anope::string> p = params;
+ p.erase(p.begin());
- return Message::Join::Run(source, p);
- }
-};
+ return Message::Join::Run(source, p);
+}
struct IRCDMessagePass : IRCDMessage
{
IRCDMessagePass(Module *creator) : IRCDMessage(creator, "PASS", 4) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
{
UplinkSID = params[3];
}
};
-struct IRCDMessageServer : IRCDMessage
+// SERVER hades.arpa 1 :ircd-ratbox test server
+void ratbox::Server::Run(MessageSource &source, const std::vector<Anope::string> &params)
{
- IRCDMessageServer(Module *creator) : IRCDMessage(creator, "SERVER", 3) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
-
- // SERVER hades.arpa 1 :ircd-ratbox test server
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- // Servers other then our immediate uplink are introduced via SID
- if (params[1] != "1")
- return;
- new Server(source.GetServer() == NULL ? Me : source.GetServer(), params[0], 1, params[2], UplinkSID);
- IRCD->SendPing(Me->GetName(), params[0]);
- }
-};
-
-struct IRCDMessageTBurst : IRCDMessage
+ // Servers other then our immediate uplink are introduced via SID
+ if (params[1] != "1")
+ return;
+ new ::Server(source.GetServer() == NULL ? Me : source.GetServer(), params[0], 1, params[2], UplinkSID);
+ IRCD->SendPing(Me->GetName(), params[0]);
+}
+
+/*
+ * params[0] = channel
+ * params[1] = ts
+ * params[2] = topic OR who set the topic
+ * params[3] = topic if params[2] isn't the topic
+ */
+void ratbox::TB::Run(MessageSource &source, const std::vector<Anope::string> &params)
{
- IRCDMessageTBurst(Module *creator) : IRCDMessage(creator, "TB", 3) { SetFlag(IRCDMESSAGE_SOFT_LIMIT); }
-
- /*
- * params[0] = channel
- * params[1] = ts
- * params[2] = topic OR who set the topic
- * params[3] = topic if params[2] isn't the topic
- */
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- time_t topic_time = Anope::string(params[1]).is_pos_number_only() ? convertTo<time_t>(params[1]) : Anope::CurTime;
- Channel *c = Channel::Find(params[0]);
+ time_t topic_time = Anope::string(params[1]).is_pos_number_only() ? convertTo<time_t>(params[1]) : Anope::CurTime;
+ Channel *c = Channel::Find(params[0]);
- if (!c)
- return;
+ if (!c)
+ return;
- const Anope::string &setter = params.size() == 4 ? params[2] : "",
- topic = params.size() == 4 ? params[3] : params[2];
+ const Anope::string &setter = params.size() == 4 ? params[2] : "",
+ topic = params.size() == 4 ? params[3] : params[2];
- c->ChangeTopicInternal(NULL, setter, topic, topic_time);
- }
-};
+ c->ChangeTopicInternal(NULL, setter, topic, topic_time);
+}
-struct IRCDMessageUID : IRCDMessage
+// :42X UID Adam 1 1348535644 +aow Adam 192.168.0.5 192.168.0.5 42XAAAAAB :Adam
+void ratbox::UID::Run(MessageSource &source, const std::vector<Anope::string> &params)
{
- IRCDMessageUID(Module *creator) : IRCDMessage(creator, "UID", 9) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
-
- // :42X UID Adam 1 1348535644 +aow Adam 192.168.0.5 192.168.0.5 42XAAAAAB :Adam
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- /* Source is always the server */
- User::OnIntroduce(params[0], params[4], params[5], "", params[6], source.GetServer(), params[8], params[2].is_pos_number_only() ? convertTo<time_t>(params[2]) : 0, params[3], params[7], NULL);
- }
-};
+ /* Source is always the server */
+ User::OnIntroduce(params[0], params[4], params[5], "", params[6], source.GetServer(), params[8], params[2].is_pos_number_only() ? convertTo<time_t>(params[2]) : 0, params[3], params[7], NULL);
+}
class ProtoRatbox : public Module
{
- Module *m_hybrid;
-
RatboxProto ircd_proto;
/* Core message handlers */
@@ -247,98 +235,62 @@ class ProtoRatbox : public Module
Message::Version message_version;
Message::Whois message_whois;
- /* Hybrid message handlers */
- ServiceAlias message_bmask, message_nick, message_pong, message_sid,
- message_sjoin, message_tmode;
-
/* Our message handlers */
- IRCDMessageEncap message_encap;
- IRCDMessageJoin message_join;
+ hybrid::BMask message_bmask;
+ ratbox::Encap message_encap;
+ ratbox::Join message_join;
+ hybrid::Nick message_nick;
IRCDMessagePass message_pass;
- IRCDMessageServer message_server;
- IRCDMessageTBurst message_tburst;
- IRCDMessageUID message_uid;
-
- void AddModes()
- {
- /* user modes */
- ModeManager::AddUserMode(new UserModeOperOnly("ADMIN", 'a'));
- ModeManager::AddUserMode(new UserModeOperOnly("BOT", 'b'));
- // c/C = con
- // d = debug?
- ModeManager::AddUserMode(new UserMode("DEAF", 'D'));
- // f = full?
- ModeManager::AddUserMode(new UserMode("CALLERID", 'g'));
- ModeManager::AddUserMode(new UserMode("INVIS", 'i'));
- // k = skill?
- ModeManager::AddUserMode(new UserModeOperOnly("LOCOPS", 'l'));
- // n = nchange
- ModeManager::AddUserMode(new UserModeOperOnly("OPER", 'o'));
- // r = rej
- ModeManager::AddUserMode(new UserModeOperOnly("SNOMASK", 's'));
- ModeManager::AddUserMode(new UserModeNoone("PROTECTED", 'S'));
- // u = unauth?
- ModeManager::AddUserMode(new UserMode("WALLOPS", 'w'));
- // x = external?
- // y = spy?
- ModeManager::AddUserMode(new UserModeOperOnly("OPERWALLS", 'z'));
- // Z = spy?
-
- /* b/e/I */
- ModeManager::AddChannelMode(new ChannelModeList("BAN", 'b'));
- ModeManager::AddChannelMode(new ChannelModeList("EXCEPT", 'e'));
- ModeManager::AddChannelMode(new ChannelModeList("INVITEOVERRIDE", 'I'));
-
- /* v/h/o/a/q */
- ModeManager::AddChannelMode(new ChannelModeStatus("VOICE", 'v', '+', 0));
- ModeManager::AddChannelMode(new ChannelModeStatus("OP", 'o', '@', 1));
-
- /* l/k */
- ModeManager::AddChannelMode(new ChannelModeParam("LIMIT", 'l', true));
- ModeManager::AddChannelMode(new ChannelModeKey('k'));
-
- /* channel modes */
- ModeManager::AddChannelMode(new ChannelMode("INVITE", 'i'));
- ModeManager::AddChannelMode(new ChannelMode("MODERATED", 'm'));
- ModeManager::AddChannelMode(new ChannelMode("NOEXTERNAL", 'n'));
- ModeManager::AddChannelMode(new ChannelMode("PRIVATE", 'p'));
- ModeManager::AddChannelMode(new ChannelMode("REGISTEREDONLY", 'r'));
- ModeManager::AddChannelMode(new ChannelMode("SECRET", 's'));
- ModeManager::AddChannelMode(new ChannelMode("TOPIC", 't'));
- ModeManager::AddChannelMode(new ChannelMode("SSL", 'S'));
- }
+ hybrid::Pong message_pong;
+ ratbox::Server message_server;
+ hybrid::SID message_sid;
+ hybrid::SJoin message_sjoin;
+ ratbox::TB message_tb;
+ hybrid::TMode message_tmode;
+ ratbox::UID message_uid;
public:
- ProtoRatbox(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, PROTOCOL | VENDOR),
- ircd_proto(this),
- message_away(this), message_capab(this), message_error(this), message_invite(this), message_kick(this),
- message_kill(this), message_mode(this), message_motd(this), message_notice(this), message_part(this),
- message_ping(this), message_privmsg(this), message_quit(this), message_squit(this), message_stats(this),
- message_time(this), message_topic(this), message_version(this), message_whois(this),
-
- message_bmask("IRCDMessage", "ratbox/bmask", "hybrid/bmask"), message_nick("IRCDMessage", "ratbox/nick", "hybrid/nick"),
- message_pong("IRCDMessage", "ratbox/pong", "hybrid/pong"), message_sid("IRCDMessage", "ratbox/sid", "hybrid/sid"),
- message_sjoin("IRCDMessage", "ratbox/sjoin", "hybrid/sjoin"), message_tmode("IRCDMessage", "ratbox/tmode", "hybrid/tmode"),
-
- message_encap(this), message_join(this), message_pass(this), message_server(this), message_tburst(this), message_uid(this)
- {
-
- if (ModuleManager::LoadModule("hybrid", User::Find(creator)) != MOD_ERR_OK)
- throw ModuleException("Unable to load hybrid");
- m_hybrid = ModuleManager::FindModule("hybrid");
- if (!m_hybrid)
- throw ModuleException("Unable to find hybrid");
- if (!hybrid)
- throw ModuleException("No protocol interface for hybrid");
-
- this->AddModes();
- }
-
- ~ProtoRatbox()
+ ProtoRatbox(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, PROTOCOL | VENDOR)
+ , ircd_proto(this)
+ , message_away(this)
+ , message_capab(this)
+ , message_error(this)
+ , message_invite(this)
+ , message_kick(this)
+ , message_kill(this)
+ , message_mode(this)
+ , message_motd(this)
+ , message_notice(this)
+ , message_part(this)
+ , message_ping(this)
+ , message_privmsg(this)
+ , message_quit(this)
+ , message_squit(this)
+ , message_stats(this)
+ , message_time(this)
+ , message_topic(this)
+ , message_version(this)
+ , message_whois(this)
+
+ , message_bmask(this)
+ , message_encap(this)
+ , message_join(this)
+ , message_nick(this)
+ , message_pass(this)
+ , message_pong(this)
+ , message_server(this)
+ , message_sid(this)
+ , message_sjoin(this)
+ , message_tb(this)
+ , message_tmode(this)
+ , message_uid(this)
{
- m_hybrid = ModuleManager::FindModule("hybrid");
- ModuleManager::UnloadModule(m_hybrid, NULL);
}
};
+template<> void ModuleInfo<ProtoRatbox>(ModuleDef *def)
+{
+ def->Depends("hybrid");
+}
+
MODULE_INIT(ProtoRatbox)
diff --git a/modules/protocol/unreal.cpp b/modules/protocol/unreal.cpp
index cff2f440b..d805e60cb 100644
--- a/modules/protocol/unreal.cpp
+++ b/modules/protocol/unreal.cpp
@@ -1,22 +1,33 @@
-/* Unreal IRCD 3.2.x functions
+/*
+ * Anope IRC Services
*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2003-2016 Anope Team <team@anope.org>
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
*
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "module.h"
-#include "modules/cs_mode.h"
+#include "modules/chanserv/mode.h"
#include "modules/sasl.h"
+#include "modules/operserv/stats.h"
+
+static Anope::string UplinkSID;
class UnrealIRCdProto : public IRCDProto
{
public:
- UnrealIRCdProto(Module *creator) : IRCDProto(creator, "UnrealIRCd 3.2.x")
+ UnrealIRCdProto(Module *creator) : IRCDProto(creator, "UnrealIRCd 4")
{
DefaultPseudoclientModes = "+Soiq";
CanSVSNick = true;
@@ -27,18 +38,19 @@ class UnrealIRCdProto : public IRCDProto
CanSQLine = true;
CanSZLine = true;
CanSVSHold = true;
- CanSVSO = true;
+ CanCertFP = true;
+ RequiresID = true;
MaxModes = 12;
}
private:
/* SVSNOOP */
- void SendSVSNOOP(const Server *server, bool set) anope_override
+ void SendSVSNOOP(const Server *server, bool set) override
{
- UplinkSocket::Message() << "SVSNOOP " << server->GetName() << " " << (set ? "+" : "-");
+ Uplink::Send("SVSNOOP", server->GetSID(), set ? "+" : "-");
}
- void SendAkillDel(const XLine *x) anope_override
+ void SendAkillDel(XLine *x) override
{
if (x->IsRegex() || x->HasNickOrReal())
return;
@@ -54,34 +66,34 @@ class UnrealIRCdProto : public IRCDProto
}
}
- UplinkSocket::Message() << "TKL - G " << x->GetUser() << " " << x->GetHost() << " " << x->by;
+ Uplink::Send("TKL", "-", "G", x->GetUser(), x->GetHost(), x->GetBy());
}
- void SendTopic(const MessageSource &source, Channel *c) anope_override
+ void SendTopic(const MessageSource &source, Channel *c) override
{
- UplinkSocket::Message(source) << "TOPIC " << c->name << " " << c->topic_setter << " " << c->topic_ts << " :" << c->topic;
+ Uplink::Send(source, "TOPIC", c->name, c->topic_setter, c->topic_ts, c->topic);
}
- void SendGlobalNotice(BotInfo *bi, const Server *dest, const Anope::string &msg) anope_override
+ void SendGlobalNotice(ServiceBot *bi, const Server *dest, const Anope::string &msg) override
{
- UplinkSocket::Message(bi) << "NOTICE $" << dest->GetName() << " :" << msg;
+ Uplink::Send(bi, "NOTICE", "$" + dest->GetName(), msg);
}
- void SendGlobalPrivmsg(BotInfo *bi, const Server *dest, const Anope::string &msg) anope_override
+ void SendGlobalPrivmsg(ServiceBot *bi, const Server *dest, const Anope::string &msg) override
{
- UplinkSocket::Message(bi) << "PRIVMSG $" << dest->GetName() << " :" << msg;
+ Uplink::Send(bi, "PRIVMSG", "$" + dest->GetName(), msg);
}
- void SendVhostDel(User *u) anope_override
+ void SendVhostDel(User *u) override
{
- BotInfo *HostServ = Config->GetClient("HostServ");
+ ServiceBot *HostServ = Config->GetClient("HostServ");
u->RemoveMode(HostServ, "CLOAK");
u->RemoveMode(HostServ, "VHOST");
ModeManager::ProcessModes();
u->SetMode(HostServ, "CLOAK");
}
- void SendAkill(User *u, XLine *x) anope_override
+ void SendAkill(User *u, XLine *x) override
{
if (x->IsRegex() || x->HasNickOrReal())
{
@@ -89,22 +101,28 @@ class UnrealIRCdProto : public IRCDProto
{
/* No user (this akill was just added), and contains nick and/or realname. Find users that match and ban them */
for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it)
- if (x->manager->Check(it->second, x))
+ if (x->GetManager()->Check(it->second, x))
this->SendAkill(it->second, x);
return;
}
- const XLine *old = x;
+ XLine *old = x;
- if (old->manager->HasEntry("*@" + u->host))
+ if (old->GetManager()->HasEntry("*@" + u->host))
return;
/* We can't akill x as it has a nick and/or realname included, so create a new akill for *@host */
- XLine *xline = new XLine("*@" + u->host, old->by, old->expires, old->reason, old->id);
- old->manager->AddXLine(xline);
- x = xline;
+ XLine *xl = Serialize::New<XLine *>();
+ xl->SetMask("*@" + u->host);
+ xl->SetBy(old->GetBy());
+ xl->SetExpires(old->GetExpires());
+ xl->SetReason(old->GetReason());
+ xl->SetID(old->GetID());
+
+ old->GetManager()->AddXLine(xl);
+ x = xl;
- Log(Config->GetClient("OperServ"), "akill") << "AKILL: Added an akill for " << x->mask << " because " << u->GetMask() << "#" << u->realname << " matches " << old->mask;
+ Log(Config->GetClient("OperServ"), "akill") << "AKILL: Added an akill for " << x->GetMask() << " because " << u->GetMask() << "#" << u->realname << " matches " << old->GetMask();
}
/* ZLine if we can instead */
@@ -119,43 +137,45 @@ class UnrealIRCdProto : public IRCDProto
}
// Calculate the time left before this would expire, capping it at 2 days
- time_t timeleft = x->expires - Anope::CurTime;
- if (timeleft > 172800 || !x->expires)
+ time_t timeleft = x->GetExpires() - Anope::CurTime;
+ if (timeleft > 172800 || !x->GetExpires())
timeleft = 172800;
- UplinkSocket::Message() << "TKL + G " << x->GetUser() << " " << x->GetHost() << " " << x->by << " " << Anope::CurTime + timeleft << " " << x->created << " :" << x->GetReason();
+ Uplink::Send("TKL", "+", "G", x->GetUser(), x->GetHost(), x->GetBy(), Anope::CurTime + timeleft, x->GetCreated(), x->GetReason());
}
- void SendSVSKillInternal(const MessageSource &source, User *user, const Anope::string &buf) anope_override
+ void SendSVSKillInternal(const MessageSource &source, User *user, const Anope::string &buf) override
{
- UplinkSocket::Message(source) << "SVSKILL " << user->nick << " :" << buf;
+ Uplink::Send(source, "SVSKILL", user->GetUID(), buf);
user->KillInternal(source, buf);
}
- void SendModeInternal(const MessageSource &source, User *u, const Anope::string &buf) anope_override
+ void SendModeInternal(const MessageSource &source, User *u, const Anope::string &buf) override
{
- UplinkSocket::Message(source) << "SVS2MODE " << u->nick <<" " << buf;
+ IRCMessage message(source, "SVS2MODE", u->GetUID());
+ message.TokenizeAndPush(buf);
+ Uplink::SendMessage(message);
}
- void SendClientIntroduction(User *u) anope_override
+ void SendClientIntroduction(User *u) override
{
Anope::string modes = "+" + u->GetModes();
- UplinkSocket::Message() << "NICK " << u->nick << " 1 " << u->timestamp << " " << u->GetIdent() << " " << u->host << " " << u->server->GetName() << " 0 " << modes << " " << u->host << " * :" << u->realname;
+ Uplink::Send("UID", u->nick, 1, u->timestamp, u->GetIdent(), u->host, u->GetUID(), "*", modes, !u->vhost.empty() ? u->vhost : "*", !u->chost.empty() ? u->chost : "*", "*", u->realname);
}
/* SERVER name hop descript */
/* Unreal 3.2 actually sends some info about itself in the descript area */
- void SendServer(const Server *server) anope_override
+ void SendServer(const Server *server) override
{
if (!server->GetSID().empty() && server == Me)
- UplinkSocket::Message() << "SERVER " << server->GetName() << " " << server->GetHops() << " :U0-*-" << server->GetSID() << " " << server->GetDescription();
+ Uplink::Send("SERVER", server->GetName(), server->GetHops() + 1, server->GetDescription());
else
- UplinkSocket::Message() << "SERVER " << server->GetName() << " " << server->GetHops() << " :" << server->GetDescription();
+ Uplink::Send("SID", server->GetName(), server->GetHops() + 1, server->GetSID(), server->GetDescription());
}
/* JOIN */
- void SendJoin(User *user, Channel *c, const ChannelStatus *status) anope_override
+ void SendJoin(User *user, Channel *c, const ChannelStatus *status) override
{
- UplinkSocket::Message(Me) << "SJOIN " << c->creation_time << " " << c->name << " :" << user->nick;
+ Uplink::Send(Me, "SJOIN", c->creation_time, c->name, user->GetUID());
if (status)
{
/* First save the channel status incase uc->Status == status */
@@ -167,7 +187,7 @@ class UnrealIRCdProto : public IRCDProto
if (uc != NULL)
uc->status.Clear();
- BotInfo *setter = BotInfo::Find(user->GetUID());
+ ServiceBot *setter = ServiceBot::Find(user->GetUID());
for (size_t i = 0; i < cs.Modes().length(); ++i)
c->SetMode(setter, ModeManager::FindChannelModeByChar(cs.Modes()[i]), user->GetUID(), false);
@@ -178,9 +198,9 @@ class UnrealIRCdProto : public IRCDProto
/* unsqline
*/
- void SendSQLineDel(const XLine *x) anope_override
+ void SendSQLineDel(XLine *x) override
{
- UplinkSocket::Message() << "UNSQLINE " << x->mask;
+ Uplink::Send("UNSQLINE", x->GetMask());
}
/* SQLINE */
@@ -188,103 +208,88 @@ class UnrealIRCdProto : public IRCDProto
** - Unreal will translate this to TKL for us
**
*/
- void SendSQLine(User *, const XLine *x) anope_override
+ void SendSQLine(User *, XLine *x) override
{
- UplinkSocket::Message() << "SQLINE " << x->mask << " :" << x->GetReason();
- }
-
- /*
- ** svso
- ** parv[0] = sender prefix
- ** parv[1] = nick
- ** parv[2] = options
- */
- void SendSVSO(BotInfo *source, const Anope::string &nick, const Anope::string &flag) anope_override
- {
- UplinkSocket::Message(source) << "SVSO " << nick << " " << flag;
+ Uplink::Send("SQLINE", x->GetMask(), x->GetReason());
}
/* Functions that use serval cmd functions */
- void SendVhost(User *u, const Anope::string &vIdent, const Anope::string &vhost) anope_override
+ void SendVhost(User *u, const Anope::string &vIdent, const Anope::string &vhost) override
{
if (!vIdent.empty())
- UplinkSocket::Message(Me) << "CHGIDENT " << u->nick << " " << vIdent;
+ Uplink::Send(Me, "CHGIDENT", u->GetUID(), vIdent);
if (!vhost.empty())
- UplinkSocket::Message(Me) << "CHGHOST " << u->nick << " " << vhost;
+ Uplink::Send(Me, "CHGHOST", u->GetUID(), vhost);
}
- void SendConnect() anope_override
+ void SendConnect() override
{
/*
NICKv2 = Nick Version 2
VHP = Sends hidden host
UMODE2 = sends UMODE2 on user modes
NICKIP = Sends IP on NICK
- TOKEN = Use tokens to talk
SJ3 = Supports SJOIN
NOQUIT = No Quit
TKLEXT = Extended TKL we don't use it but best to have it
- SJB64 = Base64 encoded time stamps
- ESVID = Allows storing account names as services stamp
MLOCK = Supports the MLOCK server command
VL = Version Info
- NS = Config->Numeric Server
+ SID = SID/UID mode
*/
- Anope::string protoctl = "NICKv2 VHP UMODE2 NICKIP SJOIN SJOIN2 SJ3 NOQUIT TKLEXT ESVID MLOCK VL";
- if (!Me->GetSID().empty())
- protoctl += " VL";
- UplinkSocket::Message() << "PROTOCTL " << protoctl;
- UplinkSocket::Message() << "PASS :" << Config->Uplinks[Anope::CurrentUplink].password;
+ Uplink::Send("PASS", Config->Uplinks[Anope::CurrentUplink].password);
+ Uplink::Send("PROTOCTL", "NICKv2", "VHP", "UMODE2", "NICKIP", "SJOIN", "SJOIN2", "SJ3", "NOQUIT", "TKLEXT", "MLOCK", "SID");
+ Uplink::Send("PROTOCTL", "EAUTH=" + Me->GetName() + ",,,Anope-" + Anope::VersionShort());
+ Uplink::Send("PROTOCTL", "SID=" + Me->GetSID());
SendServer(Me);
}
/* SVSHOLD - set */
- void SendSVSHold(const Anope::string &nick, time_t t) anope_override
+ void SendSVSHold(const Anope::string &nick, time_t t) override
{
- UplinkSocket::Message() << "TKL + Q H " << nick << " " << Me->GetName() << " " << Anope::CurTime + t << " " << Anope::CurTime << " :Being held for registered user";
+ Uplink::Send("TKL", "+", "Q", "H", nick, Me->GetName(), Anope::CurTime + t, Anope::CurTime, "Being held for registered user");
}
/* SVSHOLD - release */
- void SendSVSHoldDel(const Anope::string &nick) anope_override
+ void SendSVSHoldDel(const Anope::string &nick) override
{
- UplinkSocket::Message() << "TKL - Q * " << nick << " " << Me->GetName();
+ Uplink::Send("TKL", "-", "Q", "*", nick, Me->GetName());
}
/* UNSGLINE */
/*
* SVSNLINE - :realname mask
*/
- void SendSGLineDel(const XLine *x) anope_override
+ void SendSGLineDel(XLine *x) override
{
- UplinkSocket::Message() << "SVSNLINE - :" << x->mask;
+ Uplink::Send("SVSNLINE", "-", x->GetMask());
}
/* UNSZLINE */
- void SendSZLineDel(const XLine *x) anope_override
+ void SendSZLineDel(XLine *x) override
{
- UplinkSocket::Message() << "TKL - Z * " << x->GetHost() << " " << x->by;
+ Uplink::Send("TKL", "-", "Z", "*", x->GetHost(), x->GetBy());
}
/* SZLINE */
- void SendSZLine(User *, const XLine *x) anope_override
+ void SendSZLine(User *, XLine *x) override
{
// Calculate the time left before this would expire, capping it at 2 days
- time_t timeleft = x->expires - Anope::CurTime;
- if (timeleft > 172800 || !x->expires)
+ time_t timeleft = x->GetExpires() - Anope::CurTime;
+ if (timeleft > 172800 || !x->GetExpires())
timeleft = 172800;
- UplinkSocket::Message() << "TKL + Z * " << x->GetHost() << " " << x->by << " " << Anope::CurTime + timeleft << " " << x->created << " :" << x->GetReason();
+ Uplink::Send("TKL", "+", "Z", "*", x->GetHost(), x->GetBy(), Anope::CurTime + timeleft, x->GetCreated(), x->GetReason());
}
/* SGLINE */
/*
* SVSNLINE + reason_where_is_space :realname mask with spaces
*/
- void SendSGLine(User *, const XLine *x) anope_override
+ void SendSGLine(User *, XLine *x) override
{
Anope::string edited_reason = x->GetReason();
edited_reason = edited_reason.replace_all_cs(" ", "_");
- UplinkSocket::Message() << "SVSNLINE + " << edited_reason << " :" << x->mask;
+ Uplink::Send("SVSNLINE", "+", edited_reason, x->GetMask());
}
/* svsjoin
@@ -296,33 +301,33 @@ class UnrealIRCdProto : public IRCDProto
/* In older Unreal SVSJOIN and SVSNLINE tokens were mixed so SVSJOIN and SVSNLINE are broken
when coming from a none TOKEN'd server
*/
- void SendSVSJoin(const MessageSource &source, User *user, const Anope::string &chan, const Anope::string &param) anope_override
+ void SendSVSJoin(const MessageSource &source, User *user, const Anope::string &chan, const Anope::string &param) override
{
if (!param.empty())
- UplinkSocket::Message(source) << "SVSJOIN " << user->GetUID() << " " << chan << " :" << param;
+ Uplink::Send(source, "SVSJOIN", user->GetUID(), chan, param);
else
- UplinkSocket::Message(source) << "SVSJOIN " << user->GetUID() << " " << chan;
+ Uplink::Send(source, "SVSJOIN", user->GetUID(), chan);
}
- void SendSVSPart(const MessageSource &source, User *user, const Anope::string &chan, const Anope::string &param) anope_override
+ void SendSVSPart(const MessageSource &source, User *user, const Anope::string &chan, const Anope::string &param) override
{
if (!param.empty())
- UplinkSocket::Message(source) << "SVSPART " << user->GetUID() << " " << chan << " :" << param;
+ Uplink::Send(source, "SVSPART", user->GetUID(), chan, param);
else
- UplinkSocket::Message(source) << "SVSPART " << user->GetUID() << " " << chan;
+ Uplink::Send(source, "SVSPART", user->GetUID(), chan);
}
- void SendSWhois(const MessageSource &source, const Anope::string &who, const Anope::string &mask) anope_override
+ void SendSWhois(const MessageSource &source, const Anope::string &who, const Anope::string &mask) override
{
- UplinkSocket::Message(source) << "SWHOIS " << who << " :" << mask;
+ Uplink::Send(source, "SWHOIS", who, mask);
}
- void SendEOB() anope_override
+ void SendEOB() override
{
- UplinkSocket::Message(Me) << "EOS";
+ Uplink::Send(Me, "EOS");
}
- bool IsNickValid(const Anope::string &nick) anope_override
+ bool IsNickValid(const Anope::string &nick) override
{
if (nick.equals_ci("ircd") || nick.equals_ci("irc"))
return false;
@@ -330,7 +335,7 @@ class UnrealIRCdProto : public IRCDProto
return IRCDProto::IsNickValid(nick);
}
- bool IsChannelValid(const Anope::string &chan) anope_override
+ bool IsChannelValid(const Anope::string &chan) override
{
if (chan.find(':') != Anope::string::npos)
return false;
@@ -338,31 +343,31 @@ class UnrealIRCdProto : public IRCDProto
return IRCDProto::IsChannelValid(chan);
}
- bool IsExtbanValid(const Anope::string &mask) anope_override
+ bool IsExtbanValid(const Anope::string &mask) override
{
return mask.length() >= 4 && mask[0] == '~' && mask[2] == ':';
}
- void SendLogin(User *u, NickAlias *na) anope_override
+ void SendLogin(User *u, NickServ::Nick *na) override
{
/* 3.2.10.4+ treats users logged in with accounts as fully registered, even if -r, so we can not set this here. Just use the timestamp. */
- if (Servers::Capab.count("ESVID") > 0 && !na->nc->HasExt("UNCONFIRMED"))
- IRCD->SendMode(Config->GetClient("NickServ"), u, "+d %s", na->nc->display.c_str());
+ if (Servers::Capab.count("ESVID") > 0 && !na->GetAccount()->HasFieldS("UNCONFIRMED"))
+ IRCD->SendMode(Config->GetClient("NickServ"), u, "+d %s", na->GetAccount()->GetDisplay().c_str());
else
IRCD->SendMode(Config->GetClient("NickServ"), u, "+d %d", u->signon);
}
- void SendLogout(User *u) anope_override
+ void SendLogout(User *u) override
{
IRCD->SendMode(Config->GetClient("NickServ"), u, "+d 0");
}
- void SendChannel(Channel *c) anope_override
+ void SendChannel(Channel *c) override
{
/* Unreal does not support updating a channels TS without actually joining a user,
* so we will join and part us now
*/
- BotInfo *bi = c->ci->WhoSends();
+ ServiceBot *bi = c->ci->WhoSends();
if (!bi)
;
else if (c->FindUser(bi) == NULL)
@@ -377,24 +382,27 @@ class UnrealIRCdProto : public IRCDProto
}
}
- void SendSASLMessage(const SASL::Message &message) anope_override
+ void SendSASLMessage(const SASL::Message &message) override
{
size_t p = message.target.find('!');
if (p == Anope::string::npos)
return;
- UplinkSocket::Message(BotInfo::Find(message.source)) << "SASL " << message.target.substr(0, p) << " " << message.target << " " << message.type << " " << message.data << (message.ext.empty() ? "" : " " + message.ext);
+ if (!message.ext.empty())
+ Uplink::Send(ServiceBot::Find(message.source), "SASL", message.target.substr(0, p), message.target, message.type, message.data, message.ext);
+ else
+ Uplink::Send(ServiceBot::Find(message.source), "SASL", message.target.substr(0, p), message.target, message.type, message.data);
}
- void SendSVSLogin(const Anope::string &uid, const Anope::string &acc, const Anope::string &vident, const Anope::string &vhost) anope_override
+ void SendSVSLogin(const Anope::string &uid, const Anope::string &acc, const Anope::string &vident, const Anope::string &vhost) override
{
size_t p = uid.find('!');
if (p == Anope::string::npos)
return;
- UplinkSocket::Message(Me) << "SVSLOGIN " << uid.substr(0, p) << " " << uid << " " << acc;
+ Uplink::Send(Me, "SVSLOGIN", uid.substr(0, p), uid, acc);
}
- bool IsIdentValid(const Anope::string &ident) anope_override
+ bool IsIdentValid(const Anope::string &ident) override
{
if (ident.empty() || ident.length() > Config->GetBlock("networkinfo")->Get<unsigned>("userlen"))
return false;
@@ -426,13 +434,13 @@ class UnrealExtBan : public ChannelModeVirtual<ChannelModeList>
{
}
- ChannelMode *Wrap(Anope::string &param) anope_override
+ ChannelMode *Wrap(Anope::string &param) override
{
param = "~" + Anope::string(ext) + ":" + param;
return ChannelModeVirtual<ChannelModeList>::Wrap(param);
}
- ChannelMode *Unwrap(ChannelMode *cm, Anope::string &param) anope_override
+ ChannelMode *Unwrap(ChannelMode *cm, Anope::string &param) override
{
if (cm->type != MODE_LIST || param.length() < 4 || param[0] != '~' || param[1] != ext || param[2] != ':')
return cm;
@@ -451,7 +459,7 @@ namespace UnrealExtban
{
}
- bool Matches(User *u, const Entry *e) anope_override
+ bool Matches(User *u, const Entry *e) override
{
const Anope::string &mask = e->GetMask();
Anope::string channel = mask.substr(3);
@@ -486,7 +494,7 @@ namespace UnrealExtban
{
}
- bool Matches(User *u, const Entry *e) anope_override
+ bool Matches(User *u, const Entry *e) override
{
const Anope::string &mask = e->GetMask();
Anope::string real_mask = mask.substr(3);
@@ -502,7 +510,7 @@ namespace UnrealExtban
{
}
- bool Matches(User *u, const Entry *e) anope_override
+ bool Matches(User *u, const Entry *e) override
{
const Anope::string &mask = e->GetMask();
Anope::string real_mask = mask.substr(3);
@@ -518,7 +526,7 @@ namespace UnrealExtban
{
}
- bool Matches(User *u, const Entry *e) anope_override
+ bool Matches(User *u, const Entry *e) override
{
const Anope::string &mask = e->GetMask();
return u->HasMode("REGISTERED") && mask.equals_ci(u->nick);
@@ -532,14 +540,29 @@ namespace UnrealExtban
{
}
- bool Matches(User *u, const Entry *e) anope_override
+ bool Matches(User *u, const Entry *e) override
{
const Anope::string &mask = e->GetMask();
Anope::string real_mask = mask.substr(3);
- return u->Account() && Anope::Match(u->Account()->display, real_mask);
+ return u->Account() && Anope::Match(u->Account()->GetDisplay(), real_mask);
}
};
+
+ class FingerprintMatcher : public UnrealExtBan
+ {
+ public:
+ FingerprintMatcher(const Anope::string &mname, const Anope::string &mbase, char c) : UnrealExtBan(mname, mbase, c)
+ {
+ }
+
+ bool Matches(User *u, const Entry *e) override
+ {
+ const Anope::string &mask = e->GetMask();
+ Anope::string real_mask = mask.substr(3);
+ return !u->fingerprint.empty() && Anope::Match(u->fingerprint, real_mask);
+ }
+ };
}
class ChannelModeFlood : public ChannelModeParam
@@ -548,7 +571,7 @@ class ChannelModeFlood : public ChannelModeParam
ChannelModeFlood(char modeChar, bool minusNoArg) : ChannelModeParam("FLOOD", modeChar, minusNoArg) { }
/* Borrowed part of this check from UnrealIRCd */
- bool IsValid(Anope::string &value) const anope_override
+ bool IsValid(Anope::string &value) const override
{
if (value.empty())
return false;
@@ -559,7 +582,7 @@ class ChannelModeFlood : public ChannelModeParam
return true;
}
catch (const ConvertException &) { }
-
+
/* '['<number><1 letter>[optional: '#'+1 letter],[next..]']'':'<number> */
size_t end_bracket = value.find(']', 1);
if (end_bracket == Anope::string::npos)
@@ -600,180 +623,17 @@ class ChannelModeUnrealSSL : public ChannelMode
{
}
- bool CanSet(User *u) const anope_override
+ bool CanSet(User *u) const override
{
return false;
}
};
-struct IRCDMessageCapab : Message::Capab
-{
- IRCDMessageCapab(Module *creator) : Message::Capab(creator, "PROTOCTL") { }
-
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- for (unsigned i = 0; i < params.size(); ++i)
- {
- Anope::string capab = params[i];
-
- if (capab.find("CHANMODES") != Anope::string::npos)
- {
- Anope::string modes(capab.begin() + 10, capab.end());
- commasepstream sep(modes);
- Anope::string modebuf;
-
- sep.GetToken(modebuf);
- for (size_t t = 0, end = modebuf.length(); t < end; ++t)
- {
- switch (modebuf[t])
- {
- case 'b':
- ModeManager::AddChannelMode(new ChannelModeList("BAN", 'b'));
-
- ModeManager::AddChannelMode(new UnrealExtban::ChannelMatcher("CHANNELBAN", "BAN", 'c'));
- ModeManager::AddChannelMode(new UnrealExtban::EntryMatcher("JOINBAN", "BAN", 'j'));
- ModeManager::AddChannelMode(new UnrealExtban::EntryMatcher("NONICKBAN", "BAN", 'n'));
- ModeManager::AddChannelMode(new UnrealExtban::EntryMatcher("QUIET", "BAN", 'q'));
- ModeManager::AddChannelMode(new UnrealExtban::RealnameMatcher("REALNAMEBAN", "BAN", 'r'));
- ModeManager::AddChannelMode(new UnrealExtban::RegisteredMatcher("REGISTEREDBAN", "BAN", 'R'));
- ModeManager::AddChannelMode(new UnrealExtban::AccountMatcher("ACCOUNTBAN", "BAN", 'a'));
- continue;
- case 'e':
- ModeManager::AddChannelMode(new ChannelModeList("EXCEPT", 'e'));
- continue;
- case 'I':
- ModeManager::AddChannelMode(new ChannelModeList("INVITEOVERRIDE", 'I'));
- continue;
- default:
- ModeManager::AddChannelMode(new ChannelModeList("", modebuf[t]));
- }
- }
-
- sep.GetToken(modebuf);
- for (size_t t = 0, end = modebuf.length(); t < end; ++t)
- {
- switch (modebuf[t])
- {
- case 'k':
- ModeManager::AddChannelMode(new ChannelModeKey('k'));
- continue;
- case 'f':
- ModeManager::AddChannelMode(new ChannelModeFlood('f', false));
- continue;
- case 'L':
- ModeManager::AddChannelMode(new ChannelModeParam("REDIRECT", 'L'));
- continue;
- default:
- ModeManager::AddChannelMode(new ChannelModeParam("", modebuf[t]));
- }
- }
-
- sep.GetToken(modebuf);
- for (size_t t = 0, end = modebuf.length(); t < end; ++t)
- {
- switch (modebuf[t])
- {
- case 'l':
- ModeManager::AddChannelMode(new ChannelModeParam("LIMIT", 'l', true));
- continue;
- case 'j':
- ModeManager::AddChannelMode(new ChannelModeParam("JOINFLOOD", 'j', true));
- continue;
- default:
- ModeManager::AddChannelMode(new ChannelModeParam("", modebuf[t], true));
- }
- }
-
- sep.GetToken(modebuf);
- for (size_t t = 0, end = modebuf.length(); t < end; ++t)
- {
- switch (modebuf[t])
- {
- case 'p':
- ModeManager::AddChannelMode(new ChannelMode("PRIVATE", 'p'));
- continue;
- case 's':
- ModeManager::AddChannelMode(new ChannelMode("SECRET", 's'));
- continue;
- case 'm':
- ModeManager::AddChannelMode(new ChannelMode("MODERATED", 'm'));
- continue;
- case 'n':
- ModeManager::AddChannelMode(new ChannelMode("NOEXTERNAL", 'n'));
- continue;
- case 't':
- ModeManager::AddChannelMode(new ChannelMode("TOPIC", 't'));
- continue;
- case 'i':
- ModeManager::AddChannelMode(new ChannelMode("INVITE", 'i'));
- continue;
- case 'r':
- ModeManager::AddChannelMode(new ChannelModeNoone("REGISTERED", 'r'));
- continue;
- case 'R':
- ModeManager::AddChannelMode(new ChannelMode("REGISTEREDONLY", 'R'));
- continue;
- case 'c':
- ModeManager::AddChannelMode(new ChannelMode("BLOCKCOLOR", 'c'));
- continue;
- case 'O':
- ModeManager::AddChannelMode(new ChannelModeOperOnly("OPERONLY", 'O'));
- continue;
- case 'A':
- ModeManager::AddChannelMode(new ChannelModeOperOnly("ADMINONLY", 'A'));
- continue;
- case 'Q':
- ModeManager::AddChannelMode(new ChannelMode("NOKICK", 'Q'));
- continue;
- case 'K':
- ModeManager::AddChannelMode(new ChannelMode("NOKNOCK", 'K'));
- continue;
- case 'V':
- ModeManager::AddChannelMode(new ChannelMode("NOINVITE", 'V'));
- continue;
- case 'C':
- ModeManager::AddChannelMode(new ChannelMode("NOCTCP", 'C'));
- continue;
- case 'u':
- ModeManager::AddChannelMode(new ChannelMode("AUDITORIUM", 'u'));
- continue;
- case 'z':
- ModeManager::AddChannelMode(new ChannelMode("SSL", 'z'));
- continue;
- case 'N':
- ModeManager::AddChannelMode(new ChannelMode("NONICK", 'N'));
- continue;
- case 'S':
- ModeManager::AddChannelMode(new ChannelMode("STRIPCOLOR", 'S'));
- continue;
- case 'M':
- ModeManager::AddChannelMode(new ChannelMode("REGMODERATED", 'M'));
- continue;
- case 'T':
- ModeManager::AddChannelMode(new ChannelMode("NONOTICE", 'T'));
- continue;
- case 'G':
- ModeManager::AddChannelMode(new ChannelMode("CENSOR", 'G'));
- continue;
- case 'Z':
- ModeManager::AddChannelMode(new ChannelModeUnrealSSL("", 'Z'));
- continue;
- default:
- ModeManager::AddChannelMode(new ChannelMode("", modebuf[t]));
- }
- }
- }
- }
-
- Message::Capab::Run(source, params);
- }
-};
-
struct IRCDMessageChgHost : IRCDMessage
{
IRCDMessageChgHost(Module *creator) : IRCDMessage(creator, "CHGHOST", 2) { }
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
{
User *u = User::Find(params[0]);
if (u)
@@ -785,7 +645,7 @@ struct IRCDMessageChgIdent : IRCDMessage
{
IRCDMessageChgIdent(Module *creator) : IRCDMessage(creator, "CHGIDENT", 2) { }
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
{
User *u = User::Find(params[0]);
if (u)
@@ -797,7 +657,7 @@ struct IRCDMessageChgName : IRCDMessage
{
IRCDMessageChgName(Module *creator) : IRCDMessage(creator, "CHGNAME", 2) { }
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
{
User *u = User::Find(params[0]);
if (u)
@@ -805,11 +665,39 @@ struct IRCDMessageChgName : IRCDMessage
}
};
+struct IRCDMessageMD : IRCDMessage
+{
+ IRCDMessageMD(Module *creator) : IRCDMessage(creator, "MD", 3) { SetFlag(IRCDMESSAGE_SOFT_LIMIT); }
+
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
+ {
+ const Anope::string &mdtype = params[0],
+ &obj = params[1],
+ &var = params[2],
+ &value = params.size() > 3 ? params[3] : "";
+
+ if (mdtype == "client")
+ {
+ User *u = User::Find(obj);
+
+ if (u == nullptr)
+ return;
+
+ if (var == "certfp" && !value.empty())
+ {
+ u->Extend<bool>("ssl", true);
+ u->fingerprint = value;
+ EventManager::Get()->Dispatch(&Event::Fingerprint::OnFingerprint, u);
+ }
+ }
+ }
+};
+
struct IRCDMessageMode : IRCDMessage
{
IRCDMessageMode(Module *creator, const Anope::string &mname) : IRCDMessage(creator, mname, 2) { SetFlag(IRCDMESSAGE_SOFT_LIMIT); }
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
{
bool server_source = source.GetServer() != NULL;
Anope::string modes = params[1];
@@ -854,9 +742,10 @@ struct IRCDMessageNetInfo : IRCDMessage
{
IRCDMessageNetInfo(Module *creator) : IRCDMessage(creator, "NETINFO", 8) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
{
- UplinkSocket::Message() << "NETINFO " << MaxUserCount << " " << Anope::CurTime << " " << convertTo<int>(params[2]) << " " << params[3] << " 0 0 0 :" << params[7];
+ Stats *stats = Serialize::GetObject<Stats *>();
+ Uplink::Send("NETINFO", stats ? stats->GetMaxUserCount() : 0, Anope::CurTime, params[2], params[3], "0", "0", "0", params[7]);
}
};
@@ -884,7 +773,7 @@ struct IRCDMessageNick : IRCDMessage
** parv[0] = new nickname
** parv[1] = hopcount
*/
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
{
if (params.size() == 11)
{
@@ -911,22 +800,22 @@ struct IRCDMessageNick : IRCDMessage
Log(LOG_DEBUG) << "User " << params[0] << " introduced from non-existent server " << params[5] << "?";
return;
}
-
- NickAlias *na = NULL;
+
+ NickServ::Nick *na = NULL;
if (params[6] == "0")
;
else if (params[6].is_pos_number_only())
{
if (convertTo<time_t>(params[6]) == user_ts)
- na = NickAlias::Find(params[0]);
+ na = NickServ::FindNick(params[0]);
}
else
{
- na = NickAlias::Find(params[6]);
+ na = NickServ::FindNick(params[6]);
}
- User::OnIntroduce(params[0], params[3], params[4], vhost, ip, s, params[10], user_ts, params[7], "", na ? *na->nc : NULL);
+ User::OnIntroduce(params[0], params[3], params[4], vhost, ip, s, params[10], user_ts, params[7], "", na ? na->GetAccount() : NULL);
}
else
source.GetUser()->ChangeNick(params[0]);
@@ -946,21 +835,43 @@ struct IRCDMessagePong : IRCDMessage
{
IRCDMessagePong(Module *creator) : IRCDMessage(creator, "PONG", 0) { SetFlag(IRCDMESSAGE_SOFT_LIMIT); SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
{
if (!source.GetServer()->IsSynced())
source.GetServer()->Sync(false);
}
};
+struct IRCDMessageProtoctl : Message::Capab
+{
+ IRCDMessageProtoctl(Module *creator) : Message::Capab(creator, "PROTOCTL") { }
+
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
+ {
+ for (unsigned int i = 0; i < params.size(); ++i)
+ {
+ Anope::string capab = params[i];
+
+ if (!capab.find("SID="))
+ {
+ UplinkSID = capab.substr(4);
+ }
+ }
+
+ Message::Capab::Run(source, params);
+ }
+};
+
struct IRCDMessageSASL : IRCDMessage
{
+ ServiceReference<SASL::Service> sasl;
+
IRCDMessageSASL(Module *creator) : IRCDMessage(creator, "SASL", 4) { SetFlag(IRCDMESSAGE_SOFT_LIMIT); SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
{
size_t p = params[1].find('!');
- if (!SASL::sasl || p == Anope::string::npos)
+ if (!sasl || p == Anope::string::npos)
return;
SASL::Message m;
@@ -970,7 +881,7 @@ struct IRCDMessageSASL : IRCDMessage
m.data = params[3];
m.ext = params.size() > 4 ? params[4] : "";
- SASL::sasl->ProcessMessage(m);
+ sasl->ProcessMessage(m);
}
};
@@ -978,7 +889,7 @@ struct IRCDMessageSDesc : IRCDMessage
{
IRCDMessageSDesc(Module *creator) : IRCDMessage(creator, "SDESC", 1) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
{
source.GetServer()->SetDescription(params[0]);
}
@@ -988,7 +899,7 @@ struct IRCDMessageSetHost : IRCDMessage
{
IRCDMessageSetHost(Module *creator) : IRCDMessage(creator, "SETHOST", 1) { SetFlag(IRCDMESSAGE_REQUIRE_USER); }
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
{
User *u = source.GetUser();
@@ -1004,7 +915,7 @@ struct IRCDMessageSetIdent : IRCDMessage
{
IRCDMessageSetIdent(Module *creator) : IRCDMessage(creator, "SETIDENT", 1) { SetFlag(IRCDMESSAGE_REQUIRE_USER); }
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
{
User *u = source.GetUser();
u->SetVIdent(params[0]);
@@ -1015,7 +926,7 @@ struct IRCDMessageSetName : IRCDMessage
{
IRCDMessageSetName(Module *creator) : IRCDMessage(creator, "SETNAME", 1) { SetFlag(IRCDMESSAGE_REQUIRE_USER); }
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
{
User *u = source.GetUser();
u->SetRealname(params[0]);
@@ -1026,7 +937,7 @@ struct IRCDMessageServer : IRCDMessage
{
IRCDMessageServer(Module *creator) : IRCDMessage(creator, "SERVER", 3) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
{
unsigned int hops = Anope::string(params[1]).is_pos_number_only() ? convertTo<unsigned>(params[1]) : 0;
@@ -1035,7 +946,7 @@ struct IRCDMessageServer : IRCDMessage
Anope::string desc;
spacesepstream(params[2]).GetTokenRemainder(desc, 1);
- new Server(source.GetServer() == NULL ? Me : source.GetServer(), params[0], hops, desc);
+ new Server(source.GetServer() == NULL ? Me : source.GetServer(), params[0], hops, desc, UplinkSID);
}
else
new Server(source.GetServer(), params[0], hops, params[2]);
@@ -1044,11 +955,25 @@ struct IRCDMessageServer : IRCDMessage
}
};
+struct IRCDMessageSID : IRCDMessage
+{
+ IRCDMessageSID(Module *creator) : IRCDMessage(creator, "SID", 4) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
+
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
+ {
+ unsigned int hops = Anope::string(params[1]).is_pos_number_only() ? convertTo<unsigned>(params[1]) : 0;
+
+ new Server(source.GetServer(), params[0], hops, params[3], params[2]);
+
+ IRCD->SendPing(Me->GetName(), params[0]);
+ }
+};
+
struct IRCDMessageSJoin : IRCDMessage
{
IRCDMessageSJoin(Module *creator) : IRCDMessage(creator, "SJOIN", 3) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); SetFlag(IRCDMESSAGE_SOFT_LIMIT); }
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
{
Anope::string modes;
if (params.size() >= 4)
@@ -1103,7 +1028,7 @@ struct IRCDMessageSJoin : IRCDMessage
users.push_back(sju);
}
}
-
+
time_t ts = Anope::string(params[0]).is_pos_number_only() ? convertTo<time_t>(params[0]) : Anope::CurTime;
Message::Join::SJoin(source, params[1], ts, modes, users);
@@ -1142,7 +1067,7 @@ struct IRCDMessageTopic : IRCDMessage
** parv[2] = topic time
** parv[3] = topic text
*/
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
{
Channel *c = Channel::Find(params[0]);
if (c)
@@ -1150,20 +1075,108 @@ struct IRCDMessageTopic : IRCDMessage
}
};
+/*
+ * parv[0] = nickname
+ * parv[1] = hopcount
+ * parv[2] = timestamp
+ * parv[3] = username
+ * parv[4] = hostname
+ * parv[5] = UID
+ * parv[6] = servicestamp
+ * parv[7] = umodes
+ * parv[8] = virthost, * if none
+ * parv[9] = cloaked host, * if none
+ * parv[10] = ip
+ * parv[11] = info
+ */
+struct IRCDMessageUID : IRCDMessage
+{
+ IRCDMessageUID(Module *creator) : IRCDMessage(creator, "UID", 12) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
+
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
+ {
+ Anope::string
+ nickname = params[0],
+ hopcount = params[1],
+ timestamp = params[2],
+ username = params[3],
+ hostname = params[4],
+ uid = params[5],
+ account = params[6],
+ umodes = params[7],
+ vhost = params[8],
+ chost = params[9],
+ ip = params[10],
+ info = params[11];
+
+ if (ip != "*")
+ {
+ Anope::string decoded_ip;
+ Anope::B64Decode(ip, decoded_ip);
+
+ sockaddrs ip_addr;
+ ip_addr.ntop(ip.length() == 8 ? AF_INET : AF_INET6, decoded_ip.c_str());
+ ip = ip_addr.addr();
+ }
+
+ if (vhost == "*")
+ vhost.clear();
+
+ if (chost == "*")
+ chost.clear();
+
+ time_t user_ts;
+ try
+ {
+ user_ts = convertTo<time_t>(timestamp);
+ }
+ catch (const ConvertException &)
+ {
+ user_ts = Anope::CurTime;
+ }
+
+ NickServ::Nick *na = NULL;
+
+ if (account == "0")
+ {
+ /* nothing */
+ }
+ else if (account.is_pos_number_only())
+ {
+ if (convertTo<time_t>(account) == user_ts)
+ na = NickServ::FindNick(nickname);
+ }
+ else
+ {
+ na = NickServ::FindNick(account);
+ }
+
+ User *u = User::OnIntroduce(nickname, username, hostname, vhost, ip, source.GetServer(), info, user_ts, umodes, uid, na ? na->GetAccount() : NULL);
+
+ if (u && !chost.empty() && chost != u->GetCloakedHost())
+ u->SetCloakedHost(chost);
+ }
+};
struct IRCDMessageUmode2 : IRCDMessage
{
IRCDMessageUmode2(Module *creator) : IRCDMessage(creator, "UMODE2", 1) { SetFlag(IRCDMESSAGE_REQUIRE_USER); }
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
+ void Run(MessageSource &source, const std::vector<Anope::string> &params) override
{
source.GetUser()->SetModesInternal(source, "%s", params[0].c_str());
}
};
class ProtoUnreal : public Module
+ , public EventHook<Event::UserNickChange>
+ , public EventHook<Event::ChannelSync>
+ , public EventHook<Event::ChanRegistered>
+ , public EventHook<Event::DelChan>
+ , public EventHook<Event::MLockEvents>
{
UnrealIRCdProto ircd_proto;
+ ServiceReference<ModeLocks> mlocks;
/* Core message handlers */
Message::Away message_away;
@@ -1185,148 +1198,171 @@ class ProtoUnreal : public Module
Message::Whois message_whois;
/* Our message handlers */
- IRCDMessageCapab message_capab;
IRCDMessageChgHost message_chghost;
IRCDMessageChgIdent message_chgident;
IRCDMessageChgName message_chgname;
+ IRCDMessageMD message_md;
IRCDMessageMode message_mode, message_svsmode, message_svs2mode;
IRCDMessageNetInfo message_netinfo;
IRCDMessageNick message_nick;
IRCDMessagePong message_pong;
+ IRCDMessageProtoctl message_protoctl;
IRCDMessageSASL message_sasl;
IRCDMessageSDesc message_sdesc;
IRCDMessageSetHost message_sethost;
IRCDMessageSetIdent message_setident;
IRCDMessageSetName message_setname;
IRCDMessageServer message_server;
+ IRCDMessageSID message_sid;
IRCDMessageSJoin message_sjoin;
IRCDMessageTopic message_topic;
+ IRCDMessageUID message_uid;
IRCDMessageUmode2 message_umode2;
bool use_server_side_mlock;
- void AddModes()
- {
- ModeManager::AddChannelMode(new ChannelModeStatus("VOICE", 'v', '+', 0));
- ModeManager::AddChannelMode(new ChannelModeStatus("HALFOP", 'h', '%', 1));
- ModeManager::AddChannelMode(new ChannelModeStatus("OP", 'o', '@', 2));
- /* Unreal sends +q as * and +a as ~ */
- ModeManager::AddChannelMode(new ChannelModeStatus("PROTECT", 'a', '~', 3));
- ModeManager::AddChannelMode(new ChannelModeStatus("OWNER", 'q', '*', 4));
-
- /* Add user modes */
- ModeManager::AddUserMode(new UserModeOperOnly("SERV_ADMIN", 'A'));
- ModeManager::AddUserMode(new UserMode("BOT", 'B'));
- ModeManager::AddUserMode(new UserModeOperOnly("CO_ADMIN", 'C'));
- ModeManager::AddUserMode(new UserMode("CENSOR", 'G'));
- ModeManager::AddUserMode(new UserModeOperOnly("HIDEOPER", 'H'));
- ModeManager::AddUserMode(new UserModeOperOnly("HIDEIDLE", 'I'));
- ModeManager::AddUserMode(new UserModeOperOnly("NETADMIN", 'N'));
- ModeManager::AddUserMode(new UserMode("REGPRIV", 'R'));
- ModeManager::AddUserMode(new UserModeOperOnly("PROTECTED", 'S'));
- ModeManager::AddUserMode(new UserMode("NOCTCP", 'T'));
- ModeManager::AddUserMode(new UserMode("WEBTV", 'V'));
- ModeManager::AddUserMode(new UserModeOperOnly("WHOIS", 'W'));
- ModeManager::AddUserMode(new UserModeOperOnly("ADMIN", 'a'));
- ModeManager::AddUserMode(new UserMode("DEAF", 'd'));
- ModeManager::AddUserMode(new UserModeOperOnly("GLOBOPS", 'g'));
- ModeManager::AddUserMode(new UserModeOperOnly("HELPOP", 'h'));
- ModeManager::AddUserMode(new UserMode("INVIS", 'i'));
- ModeManager::AddUserMode(new UserModeOperOnly("OPER", 'o'));
- ModeManager::AddUserMode(new UserMode("PRIV", 'p'));
- ModeManager::AddUserMode(new UserModeOperOnly("GOD", 'q'));
- ModeManager::AddUserMode(new UserModeNoone("REGISTERED", 'r'));
- ModeManager::AddUserMode(new UserModeOperOnly("SNOMASK", 's'));
- ModeManager::AddUserMode(new UserModeNoone("VHOST", 't'));
- ModeManager::AddUserMode(new UserMode("WALLOPS", 'w'));
- ModeManager::AddUserMode(new UserMode("CLOAK", 'x'));
- ModeManager::AddUserMode(new UserModeNoone("SSL", 'z'));
- }
-
public:
- ProtoUnreal(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, PROTOCOL | VENDOR),
- ircd_proto(this),
- message_away(this), message_error(this), message_invite(this), message_join(this), message_kick(this),
- message_kill(this), message_svskill(this, "SVSKILL"), message_motd(this), message_notice(this), message_part(this), message_ping(this),
- message_privmsg(this), message_quit(this), message_squit(this), message_stats(this), message_time(this),
- message_version(this), message_whois(this),
-
- message_capab(this), message_chghost(this), message_chgident(this), message_chgname(this), message_mode(this, "MODE"),
- message_svsmode(this, "SVSMODE"), message_svs2mode(this, "SVS2MODE"), message_netinfo(this), message_nick(this), message_pong(this),
- message_sasl(this), message_sdesc(this), message_sethost(this), message_setident(this), message_setname(this), message_server(this),
- message_sjoin(this), message_topic(this), message_umode2(this)
+ ProtoUnreal(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, PROTOCOL | VENDOR)
+ , EventHook<Event::UserNickChange>(this, EventHook<Event::UserNickChange>::Priority::FIRST)
+ , EventHook<Event::ChannelSync>(this, EventHook<Event::ChannelSync>::Priority::FIRST)
+ , EventHook<Event::ChanRegistered>(this, EventHook<Event::ChanRegistered>::Priority::FIRST)
+ , EventHook<Event::DelChan>(this, EventHook<Event::DelChan>::Priority::FIRST)
+ , EventHook<Event::MLockEvents>(this, EventHook<Event::MLockEvents>::Priority::FIRST)
+ , ircd_proto(this)
+ , message_away(this)
+ , message_error(this)
+ , message_invite(this)
+ , message_join(this)
+ , message_kick(this)
+ , message_kill(this)
+ , message_svskill(this, "SVSKILL")
+ , message_motd(this)
+ , message_notice(this)
+ , message_part(this)
+ , message_ping(this)
+ , message_privmsg(this)
+ , message_quit(this)
+ , message_squit(this)
+ , message_stats(this)
+ , message_time(this)
+ , message_version(this)
+ , message_whois(this)
+
+ , message_chghost(this)
+ , message_chgident(this)
+ , message_chgname(this)
+ , message_md(this)
+ , message_mode(this, "MODE")
+ , message_svsmode(this, "SVSMODE")
+ , message_svs2mode(this, "SVS2MODE")
+ , message_netinfo(this)
+ , message_nick(this)
+ , message_pong(this)
+ , message_protoctl(this)
+ , message_sasl(this)
+ , message_sdesc(this)
+ , message_sethost(this)
+ , message_setident(this)
+ , message_setname(this)
+ , message_server(this)
+ , message_sid(this)
+ , message_sjoin(this)
+ , message_topic(this)
+ , message_uid(this)
+ , message_umode2(this)
+ {
+ }
+
+ void OnReload(Configuration::Conf *conf) override
{
+ use_server_side_mlock = conf->GetModule(this)->Get<bool>("use_server_side_mlock");
- this->AddModes();
- }
+ for (int i = 0; i < conf->CountBlock("extban"); ++i)
+ {
+ Configuration::Block *extban = conf->GetBlock("extban", i);
+ Anope::string name = extban->Get<Anope::string>("name"),
+ type = extban->Get<Anope::string>("type"),
+ base = extban->Get<Anope::string>("base"),
+ character = extban->Get<Anope::string>("character");
- void Prioritize() anope_override
- {
- ModuleManager::SetPriority(this, PRIORITY_FIRST);
- }
+ ChannelMode *cm;
- void OnReload(Configuration::Conf *conf) anope_override
- {
- use_server_side_mlock = conf->GetModule(this)->Get<bool>("use_server_side_mlock");
+ if (character.empty())
+ continue;
+
+ if (type == "channel")
+ cm = new UnrealExtban::ChannelMatcher(name, base, character[0]);
+ else if (type == "entry")
+ cm = new UnrealExtban::EntryMatcher(name, base, character[0]);
+ else if (type == "realname")
+ cm = new UnrealExtban::RealnameMatcher(name, base, character[0]);
+ else if (type == "registered")
+ cm = new UnrealExtban::RegisteredMatcher(name, base, character[0]);
+ else if (type == "account")
+ cm = new UnrealExtban::AccountMatcher(name, base, character[0]);
+ else if (type == "fingerprint")
+ cm = new UnrealExtban::FingerprintMatcher(name, base, character[0]);
+ else
+ continue;
+
+ if (!ModeManager::AddChannelMode(cm))
+ delete cm;
+ }
}
- void OnUserNickChange(User *u, const Anope::string &) anope_override
+ void OnUserNickChange(User *u, const Anope::string &) override
{
u->RemoveModeInternal(Me, ModeManager::FindUserModeByName("REGISTERED"));
if (Servers::Capab.count("ESVID") == 0)
IRCD->SendLogout(u);
}
- void OnChannelSync(Channel *c) anope_override
+ void OnChannelSync(Channel *c) override
{
if (!c->ci)
return;
- ModeLocks *modelocks = c->ci->GetExt<ModeLocks>("modelocks");
- if (use_server_side_mlock && Servers::Capab.count("MLOCK") > 0 && modelocks)
+ if (use_server_side_mlock && Servers::Capab.count("MLOCK") > 0 && mlocks)
{
- Anope::string modes = modelocks->GetMLockAsString(false).replace_all_cs("+", "").replace_all_cs("-", "");
- UplinkSocket::Message(Me) << "MLOCK " << static_cast<long>(c->creation_time) << " " << c->ci->name << " " << modes;
+ Anope::string modes = mlocks->GetMLockAsString(c->ci, false).replace_all_cs("+", "").replace_all_cs("-", "");
+ Uplink::Send(Me, "MLOCK", c->creation_time, c->ci->GetName(), modes);
}
}
- void OnChanRegistered(ChannelInfo *ci) anope_override
+ void OnChanRegistered(ChanServ::Channel *ci) override
{
- ModeLocks *modelocks = ci->GetExt<ModeLocks>("modelocks");
- if (!ci->c || !use_server_side_mlock || !modelocks || !Servers::Capab.count("MLOCK"))
+ if (!ci->c || !use_server_side_mlock || !mlocks || !Servers::Capab.count("MLOCK"))
return;
- Anope::string modes = modelocks->GetMLockAsString(false).replace_all_cs("+", "").replace_all_cs("-", "");
- UplinkSocket::Message(Me) << "MLOCK " << static_cast<long>(ci->c->creation_time) << " " << ci->name << " " << modes;
+ Anope::string modes = mlocks->GetMLockAsString(ci, false).replace_all_cs("+", "").replace_all_cs("-", "");
+ Uplink::Send(Me, "MLOCK", ci->c->creation_time, ci->GetName(), modes);
}
- void OnDelChan(ChannelInfo *ci) anope_override
+ void OnDelChan(ChanServ::Channel *ci) override
{
if (!ci->c || !use_server_side_mlock || !Servers::Capab.count("MLOCK"))
return;
- UplinkSocket::Message(Me) << "MLOCK " << static_cast<long>(ci->c->creation_time) << " " << ci->name << " :";
+ Uplink::Send(Me, "MLOCK", ci->c->creation_time, ci->GetName(), "");
}
- EventReturn OnMLock(ChannelInfo *ci, ModeLock *lock) anope_override
+ EventReturn OnMLock(ChanServ::Channel *ci, ModeLock *lock) override
{
- ModeLocks *modelocks = ci->GetExt<ModeLocks>("modelocks");
- ChannelMode *cm = ModeManager::FindChannelModeByName(lock->name);
- if (use_server_side_mlock && cm && modelocks && ci->c && (cm->type == MODE_REGULAR || cm->type == MODE_PARAM) && Servers::Capab.count("MLOCK") > 0)
+ ChannelMode *cm = ModeManager::FindChannelModeByName(lock->GetName());
+ if (use_server_side_mlock && cm && mlocks && ci->c && (cm->type == MODE_REGULAR || cm->type == MODE_PARAM) && Servers::Capab.count("MLOCK") > 0)
{
- Anope::string modes = modelocks->GetMLockAsString(false).replace_all_cs("+", "").replace_all_cs("-", "") + cm->mchar;
- UplinkSocket::Message(Me) << "MLOCK " << static_cast<long>(ci->c->creation_time) << " " << ci->name << " " << modes;
+ Anope::string modes = mlocks->GetMLockAsString(ci, false).replace_all_cs("+", "").replace_all_cs("-", "") + cm->mchar;
+ Uplink::Send(Me, "MLOCK", ci->c->creation_time, ci->GetName(), modes);
}
return EVENT_CONTINUE;
}
- EventReturn OnUnMLock(ChannelInfo *ci, ModeLock *lock) anope_override
+ EventReturn OnUnMLock(ChanServ::Channel *ci, ModeLock *lock) override
{
- ModeLocks *modelocks = ci->GetExt<ModeLocks>("modelocks");
- ChannelMode *cm = ModeManager::FindChannelModeByName(lock->name);
- if (use_server_side_mlock && cm && modelocks && ci->c && (cm->type == MODE_REGULAR || cm->type == MODE_PARAM) && Servers::Capab.count("MLOCK") > 0)
+ ChannelMode *cm = ModeManager::FindChannelModeByName(lock->GetName());
+ if (use_server_side_mlock && cm && mlocks && ci->c && (cm->type == MODE_REGULAR || cm->type == MODE_PARAM) && Servers::Capab.count("MLOCK") > 0)
{
- Anope::string modes = modelocks->GetMLockAsString(false).replace_all_cs("+", "").replace_all_cs("-", "").replace_all_cs(cm->mchar, "");
- UplinkSocket::Message(Me) << "MLOCK " << static_cast<long>(ci->c->creation_time) << " " << ci->name << " " << modes;
+ Anope::string modes = mlocks->GetMLockAsString(ci, false).replace_all_cs("+", "").replace_all_cs("-", "").replace_all_cs(cm->mchar, "");
+ Uplink::Send(Me, "MLOCK", ci->c->creation_time, ci->GetName(), modes);
}
return EVENT_CONTINUE;
diff --git a/modules/protocol/unreal4.cpp b/modules/protocol/unreal4.cpp
deleted file mode 100644
index 6e5f6ec55..000000000
--- a/modules/protocol/unreal4.cpp
+++ /dev/null
@@ -1,1468 +0,0 @@
-/* Unreal IRCD 4 functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-#include "modules/cs_mode.h"
-#include "modules/sasl.h"
-
-static Anope::string UplinkSID;
-
-class UnrealIRCdProto : public IRCDProto
-{
- public:
- UnrealIRCdProto(Module *creator) : IRCDProto(creator, "UnrealIRCd 4")
- {
- DefaultPseudoclientModes = "+Soiq";
- CanSVSNick = true;
- CanSVSJoin = true;
- CanSetVHost = true;
- CanSetVIdent = true;
- CanSNLine = true;
- CanSQLine = true;
- CanSZLine = true;
- CanSVSHold = true;
- CanCertFP = true;
- RequiresID = true;
- MaxModes = 12;
- }
-
- private:
- /* SVSNOOP */
- void SendSVSNOOP(const Server *server, bool set) anope_override
- {
- UplinkSocket::Message() << "SVSNOOP " << server->GetSID() << " " << (set ? "+" : "-");
- }
-
- void SendAkillDel(const XLine *x) anope_override
- {
- if (x->IsRegex() || x->HasNickOrReal())
- return;
-
- /* ZLine if we can instead */
- if (x->GetUser() == "*")
- {
- cidr a(x->GetHost());
- if (a.valid())
- {
- IRCD->SendSZLineDel(x);
- return;
- }
- }
-
- UplinkSocket::Message() << "TKL - G " << x->GetUser() << " " << x->GetHost() << " " << x->by;
- }
-
- void SendTopic(const MessageSource &source, Channel *c) anope_override
- {
- UplinkSocket::Message(source) << "TOPIC " << c->name << " " << c->topic_setter << " " << c->topic_ts << " :" << c->topic;
- }
-
- void SendGlobalNotice(BotInfo *bi, const Server *dest, const Anope::string &msg) anope_override
- {
- UplinkSocket::Message(bi) << "NOTICE $" << dest->GetName() << " :" << msg;
- }
-
- void SendGlobalPrivmsg(BotInfo *bi, const Server *dest, const Anope::string &msg) anope_override
- {
- UplinkSocket::Message(bi) << "PRIVMSG $" << dest->GetName() << " :" << msg;
- }
-
- void SendVhostDel(User *u) anope_override
- {
- BotInfo *HostServ = Config->GetClient("HostServ");
- u->RemoveMode(HostServ, "CLOAK");
- u->RemoveMode(HostServ, "VHOST");
- ModeManager::ProcessModes();
- u->SetMode(HostServ, "CLOAK");
- }
-
- void SendAkill(User *u, XLine *x) anope_override
- {
- if (x->IsRegex() || x->HasNickOrReal())
- {
- if (!u)
- {
- /* No user (this akill was just added), and contains nick and/or realname. Find users that match and ban them */
- for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it)
- if (x->manager->Check(it->second, x))
- this->SendAkill(it->second, x);
- return;
- }
-
- const XLine *old = x;
-
- if (old->manager->HasEntry("*@" + u->host))
- return;
-
- /* We can't akill x as it has a nick and/or realname included, so create a new akill for *@host */
- XLine *xline = new XLine("*@" + u->host, old->by, old->expires, old->reason, old->id);
- old->manager->AddXLine(xline);
- x = xline;
-
- Log(Config->GetClient("OperServ"), "akill") << "AKILL: Added an akill for " << x->mask << " because " << u->GetMask() << "#" << u->realname << " matches " << old->mask;
- }
-
- /* ZLine if we can instead */
- if (x->GetUser() == "*")
- {
- cidr a(x->GetHost());
- if (a.valid())
- {
- IRCD->SendSZLine(u, x);
- return;
- }
- }
-
- // Calculate the time left before this would expire, capping it at 2 days
- time_t timeleft = x->expires - Anope::CurTime;
- if (timeleft > 172800 || !x->expires)
- timeleft = 172800;
- UplinkSocket::Message() << "TKL + G " << x->GetUser() << " " << x->GetHost() << " " << x->by << " " << Anope::CurTime + timeleft << " " << x->created << " :" << x->GetReason();
- }
-
- void SendSVSKillInternal(const MessageSource &source, User *user, const Anope::string &buf) anope_override
- {
- UplinkSocket::Message(source) << "SVSKILL " << user->GetUID() << " :" << buf;
- user->KillInternal(source, buf);
- }
-
- void SendModeInternal(const MessageSource &source, User *u, const Anope::string &buf) anope_override
- {
- UplinkSocket::Message(source) << "SVS2MODE " << u->GetUID() <<" " << buf;
- }
-
- void SendClientIntroduction(User *u) anope_override
- {
- Anope::string modes = "+" + u->GetModes();
- UplinkSocket::Message(u->server) << "UID " << u->nick << " 1 " << u->timestamp << " " << u->GetIdent() << " " << u->host << " "
- << u->GetUID() << " * " << modes << " " << (!u->vhost.empty() ? u->vhost : "*") << " "
- << (!u->chost.empty() ? u->chost : "*") << " " << "*" << " :" << u->realname;
- }
-
- void SendServer(const Server *server) anope_override
- {
- if (server == Me)
- UplinkSocket::Message() << "SERVER " << server->GetName() << " " << server->GetHops() + 1 << " :" << server->GetDescription();
- else
- UplinkSocket::Message(Me) << "SID " << server->GetName() << " " << server->GetHops() + 1 << " " << server->GetSID() << " :" << server->GetDescription();
- }
-
- /* JOIN */
- void SendJoin(User *user, Channel *c, const ChannelStatus *status) anope_override
- {
- UplinkSocket::Message(Me) << "SJOIN " << c->creation_time << " " << c->name << " :" << user->GetUID();
- if (status)
- {
- /* First save the channel status incase uc->Status == status */
- ChannelStatus cs = *status;
- /* If the user is internally on the channel with flags, kill them so that
- * the stacker will allow this.
- */
- ChanUserContainer *uc = c->FindUser(user);
- if (uc != NULL)
- uc->status.Clear();
-
- BotInfo *setter = BotInfo::Find(user->GetUID());
- for (size_t i = 0; i < cs.Modes().length(); ++i)
- c->SetMode(setter, ModeManager::FindChannelModeByChar(cs.Modes()[i]), user->GetUID(), false);
-
- if (uc != NULL)
- uc->status = cs;
- }
- }
-
- /* unsqline
- */
- void SendSQLineDel(const XLine *x) anope_override
- {
- UplinkSocket::Message() << "UNSQLINE " << x->mask;
- }
-
- /* SQLINE */
- /*
- ** - Unreal will translate this to TKL for us
- **
- */
- void SendSQLine(User *, const XLine *x) anope_override
- {
- UplinkSocket::Message() << "SQLINE " << x->mask << " :" << x->GetReason();
- }
-
- /* Functions that use serval cmd functions */
-
- void SendVhost(User *u, const Anope::string &vIdent, const Anope::string &vhost) anope_override
- {
- if (!vIdent.empty())
- UplinkSocket::Message(Me) << "CHGIDENT " << u->GetUID() << " " << vIdent;
- if (!vhost.empty())
- UplinkSocket::Message(Me) << "CHGHOST " << u->GetUID() << " " << vhost;
- }
-
- void SendConnect() anope_override
- {
- /*
- NICKv2 = Nick Version 2
- VHP = Sends hidden host
- UMODE2 = sends UMODE2 on user modes
- NICKIP = Sends IP on NICK
- SJ3 = Supports SJOIN
- NOQUIT = No Quit
- TKLEXT = Extended TKL we don't use it but best to have it
- MLOCK = Supports the MLOCK server command
- VL = Version Info
- SID = SID/UID mode
- */
- UplinkSocket::Message() << "PASS :" << Config->Uplinks[Anope::CurrentUplink].password;
- UplinkSocket::Message() << "PROTOCTL " << "NICKv2 VHP UMODE2 NICKIP SJOIN SJOIN2 SJ3 NOQUIT TKLEXT MLOCK SID";
- UplinkSocket::Message() << "PROTOCTL " << "EAUTH=" << Me->GetName() << ",,,Anope-" << Anope::VersionShort();
- UplinkSocket::Message() << "PROTOCTL " << "SID=" << Me->GetSID();
- SendServer(Me);
- }
-
- /* SVSHOLD - set */
- void SendSVSHold(const Anope::string &nick, time_t t) anope_override
- {
- UplinkSocket::Message() << "TKL + Q H " << nick << " " << Me->GetName() << " " << Anope::CurTime + t << " " << Anope::CurTime << " :Being held for registered user";
- }
-
- /* SVSHOLD - release */
- void SendSVSHoldDel(const Anope::string &nick) anope_override
- {
- UplinkSocket::Message() << "TKL - Q * " << nick << " " << Me->GetName();
- }
-
- /* UNSGLINE */
- /*
- * SVSNLINE - :realname mask
- */
- void SendSGLineDel(const XLine *x) anope_override
- {
- UplinkSocket::Message() << "SVSNLINE - :" << x->mask;
- }
-
- /* UNSZLINE */
- void SendSZLineDel(const XLine *x) anope_override
- {
- UplinkSocket::Message() << "TKL - Z * " << x->GetHost() << " " << x->by;
- }
-
- /* SZLINE */
- void SendSZLine(User *, const XLine *x) anope_override
- {
- // Calculate the time left before this would expire, capping it at 2 days
- time_t timeleft = x->expires - Anope::CurTime;
- if (timeleft > 172800 || !x->expires)
- timeleft = 172800;
- UplinkSocket::Message() << "TKL + Z * " << x->GetHost() << " " << x->by << " " << Anope::CurTime + timeleft << " " << x->created << " :" << x->GetReason();
- }
-
- /* SGLINE */
- /*
- * SVSNLINE + reason_where_is_space :realname mask with spaces
- */
- void SendSGLine(User *, const XLine *x) anope_override
- {
- Anope::string edited_reason = x->GetReason();
- edited_reason = edited_reason.replace_all_cs(" ", "_");
- UplinkSocket::Message() << "SVSNLINE + " << edited_reason << " :" << x->mask;
- }
-
- /* svsjoin
- parv[0] - sender
- parv[1] - nick to make join
- parv[2] - channel to join
- parv[3] - (optional) channel key(s)
- */
- /* In older Unreal SVSJOIN and SVSNLINE tokens were mixed so SVSJOIN and SVSNLINE are broken
- when coming from a none TOKEN'd server
- */
- void SendSVSJoin(const MessageSource &source, User *user, const Anope::string &chan, const Anope::string &param) anope_override
- {
- if (!param.empty())
- UplinkSocket::Message() << "SVSJOIN " << user->GetUID() << " " << chan << " :" << param;
- else
- UplinkSocket::Message() << "SVSJOIN " << user->GetUID() << " " << chan;
- }
-
- void SendSVSPart(const MessageSource &source, User *user, const Anope::string &chan, const Anope::string &param) anope_override
- {
- if (!param.empty())
- UplinkSocket::Message() << "SVSPART " << user->GetUID() << " " << chan << " :" << param;
- else
- UplinkSocket::Message() << "SVSPART " << user->GetUID() << " " << chan;
- }
-
- void SendSWhois(const MessageSource &source, const Anope::string &who, const Anope::string &mask) anope_override
- {
- UplinkSocket::Message(source) << "SWHOIS " << who << " :" << mask;
- }
-
- void SendEOB() anope_override
- {
- UplinkSocket::Message(Me) << "EOS";
- }
-
- bool IsNickValid(const Anope::string &nick) anope_override
- {
- if (nick.equals_ci("ircd") || nick.equals_ci("irc"))
- return false;
-
- return IRCDProto::IsNickValid(nick);
- }
-
- bool IsChannelValid(const Anope::string &chan) anope_override
- {
- if (chan.find(':') != Anope::string::npos)
- return false;
-
- return IRCDProto::IsChannelValid(chan);
- }
-
- bool IsExtbanValid(const Anope::string &mask) anope_override
- {
- return mask.length() >= 4 && mask[0] == '~' && mask[2] == ':';
- }
-
- void SendLogin(User *u, NickAlias *na) anope_override
- {
- /* 3.2.10.4+ treats users logged in with accounts as fully registered, even if -r, so we can not set this here. Just use the timestamp. */
- if (Servers::Capab.count("ESVID") > 0 && !na->nc->HasExt("UNCONFIRMED"))
- IRCD->SendMode(Config->GetClient("NickServ"), u, "+d %s", na->nc->display.c_str());
- else
- IRCD->SendMode(Config->GetClient("NickServ"), u, "+d %d", u->signon);
- }
-
- void SendLogout(User *u) anope_override
- {
- IRCD->SendMode(Config->GetClient("NickServ"), u, "+d 0");
- }
-
- void SendChannel(Channel *c) anope_override
- {
- /* Unreal does not support updating a channels TS without actually joining a user,
- * so we will join and part us now
- */
- BotInfo *bi = c->ci->WhoSends();
- if (!bi)
- ;
- else if (c->FindUser(bi) == NULL)
- {
- bi->Join(c);
- bi->Part(c);
- }
- else
- {
- bi->Part(c);
- bi->Join(c);
- }
- }
-
- void SendSASLMessage(const SASL::Message &message) anope_override
- {
- size_t p = message.target.find('!');
- if (p == Anope::string::npos)
- return;
-
- UplinkSocket::Message(BotInfo::Find(message.source)) << "SASL " << message.target.substr(0, p) << " " << message.target << " " << message.type << " " << message.data << (message.ext.empty() ? "" : " " + message.ext);
- }
-
- void SendSVSLogin(const Anope::string &uid, const Anope::string &acc, const Anope::string &vident, const Anope::string &vhost) anope_override
- {
- size_t p = uid.find('!');
- if (p == Anope::string::npos)
- return;
- UplinkSocket::Message(Me) << "SVSLOGIN " << uid.substr(0, p) << " " << uid << " " << acc;
- }
-
- bool IsIdentValid(const Anope::string &ident) anope_override
- {
- if (ident.empty() || ident.length() > Config->GetBlock("networkinfo")->Get<unsigned>("userlen"))
- return false;
-
- for (unsigned i = 0; i < ident.length(); ++i)
- {
- const char &c = ident[i];
-
- if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '.' || c == '-')
- continue;
-
- if (c == '-' || c == '.' || c == '_')
- continue;
-
- return false;
- }
-
- return true;
- }
-};
-
-class UnrealExtBan : public ChannelModeVirtual<ChannelModeList>
-{
- char ext;
-
- public:
- UnrealExtBan(const Anope::string &mname, const Anope::string &basename, char extban) : ChannelModeVirtual<ChannelModeList>(mname, basename)
- , ext(extban)
- {
- }
-
- ChannelMode *Wrap(Anope::string &param) anope_override
- {
- param = "~" + Anope::string(ext) + ":" + param;
- return ChannelModeVirtual<ChannelModeList>::Wrap(param);
- }
-
- ChannelMode *Unwrap(ChannelMode *cm, Anope::string &param) anope_override
- {
- if (cm->type != MODE_LIST || param.length() < 4 || param[0] != '~' || param[1] != ext || param[2] != ':')
- return cm;
-
- param = param.substr(3);
- return this;
- }
-};
-
-namespace UnrealExtban
-{
- class ChannelMatcher : public UnrealExtBan
- {
- public:
- ChannelMatcher(const Anope::string &mname, const Anope::string &mbase, char c) : UnrealExtBan(mname, mbase, c)
- {
- }
-
- bool Matches(User *u, const Entry *e) anope_override
- {
- const Anope::string &mask = e->GetMask();
- Anope::string channel = mask.substr(3);
-
- ChannelMode *cm = NULL;
- if (channel[0] != '#')
- {
- char modeChar = ModeManager::GetStatusChar(channel[0]);
- channel.erase(channel.begin());
- cm = ModeManager::FindChannelModeByChar(modeChar);
- if (cm != NULL && cm->type != MODE_STATUS)
- cm = NULL;
- }
-
- Channel *c = Channel::Find(channel);
- if (c != NULL)
- {
- ChanUserContainer *uc = c->FindUser(u);
- if (uc != NULL)
- if (cm == NULL || uc->status.HasMode(cm->mchar))
- return true;
- }
-
- return false;
- }
- };
-
- class EntryMatcher : public UnrealExtBan
- {
- public:
- EntryMatcher(const Anope::string &mname, const Anope::string &mbase, char c) : UnrealExtBan(mname, mbase, c)
- {
- }
-
- bool Matches(User *u, const Entry *e) anope_override
- {
- const Anope::string &mask = e->GetMask();
- Anope::string real_mask = mask.substr(3);
-
- return Entry(this->name, real_mask).Matches(u);
- }
- };
-
- class RealnameMatcher : public UnrealExtBan
- {
- public:
- RealnameMatcher(const Anope::string &mname, const Anope::string &mbase, char c) : UnrealExtBan(mname, mbase, c)
- {
- }
-
- bool Matches(User *u, const Entry *e) anope_override
- {
- const Anope::string &mask = e->GetMask();
- Anope::string real_mask = mask.substr(3);
-
- return Anope::Match(u->realname, real_mask);
- }
- };
-
- class RegisteredMatcher : public UnrealExtBan
- {
- public:
- RegisteredMatcher(const Anope::string &mname, const Anope::string &mbase, char c) : UnrealExtBan(mname, mbase, c)
- {
- }
-
- bool Matches(User *u, const Entry *e) anope_override
- {
- const Anope::string &mask = e->GetMask();
- return u->HasMode("REGISTERED") && mask.equals_ci(u->nick);
- }
- };
-
- class AccountMatcher : public UnrealExtBan
- {
- public:
- AccountMatcher(const Anope::string &mname, const Anope::string &mbase, char c) : UnrealExtBan(mname, mbase, c)
- {
- }
-
- bool Matches(User *u, const Entry *e) anope_override
- {
- const Anope::string &mask = e->GetMask();
- Anope::string real_mask = mask.substr(3);
-
- return u->Account() && Anope::Match(u->Account()->display, real_mask);
- }
- };
-
- class FingerprintMatcher : public UnrealExtBan
- {
- public:
- FingerprintMatcher(const Anope::string &mname, const Anope::string &mbase, char c) : UnrealExtBan(mname, mbase, c)
- {
- }
-
- bool Matches(User *u, const Entry *e) anope_override
- {
- const Anope::string &mask = e->GetMask();
- Anope::string real_mask = mask.substr(3);
- return !u->fingerprint.empty() && Anope::Match(u->fingerprint, real_mask);
- }
- };
-}
-
-class ChannelModeFlood : public ChannelModeParam
-{
- public:
- ChannelModeFlood(char modeChar, bool minusNoArg) : ChannelModeParam("FLOOD", modeChar, minusNoArg) { }
-
- /* Borrowed part of this check from UnrealIRCd */
- bool IsValid(Anope::string &value) const anope_override
- {
- if (value.empty())
- return false;
- try
- {
- Anope::string rest;
- if (value[0] != ':' && convertTo<unsigned>(value[0] == '*' ? value.substr(1) : value, rest, false) > 0 && rest[0] == ':' && rest.length() > 1 && convertTo<unsigned>(rest.substr(1), rest, false) > 0 && rest.empty())
- return true;
- }
- catch (const ConvertException &) { }
-
- /* '['<number><1 letter>[optional: '#'+1 letter],[next..]']'':'<number> */
- size_t end_bracket = value.find(']', 1);
- if (end_bracket == Anope::string::npos)
- return false;
- Anope::string xbuf = value.substr(0, end_bracket);
- if (value[end_bracket + 1] != ':')
- return false;
- commasepstream args(xbuf.substr(1));
- Anope::string arg;
- while (args.GetToken(arg))
- {
- /* <number><1 letter>[optional: '#'+1 letter] */
- size_t p = 0;
- while (p < arg.length() && isdigit(arg[p]))
- ++p;
- if (p == arg.length() || !(arg[p] == 'c' || arg[p] == 'j' || arg[p] == 'k' || arg[p] == 'm' || arg[p] == 'n' || arg[p] == 't'))
- continue; /* continue instead of break for forward compatibility. */
- try
- {
- int v = arg.substr(0, p).is_number_only() ? convertTo<int>(arg.substr(0, p)) : 0;
- if (v < 1 || v > 999)
- return false;
- }
- catch (const ConvertException &)
- {
- return false;
- }
- }
-
- return true;
- }
-};
-
-class ChannelModeUnrealSSL : public ChannelMode
-{
- public:
- ChannelModeUnrealSSL(const Anope::string &n, char c) : ChannelMode(n, c)
- {
- }
-
- bool CanSet(User *u) const anope_override
- {
- return false;
- }
-};
-
-struct IRCDMessageCapab : Message::Capab
-{
- IRCDMessageCapab(Module *creator) : Message::Capab(creator, "PROTOCTL") { }
-
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- for (unsigned i = 0; i < params.size(); ++i)
- {
- Anope::string capab = params[i];
-
- if (capab.find("CHANMODES") != Anope::string::npos)
- {
- Anope::string modes(capab.begin() + 10, capab.end());
- commasepstream sep(modes);
- Anope::string modebuf;
-
- sep.GetToken(modebuf);
- for (size_t t = 0, end = modebuf.length(); t < end; ++t)
- {
- switch (modebuf[t])
- {
- case 'b':
- ModeManager::AddChannelMode(new ChannelModeList("BAN", 'b'));
-
- ModeManager::AddChannelMode(new UnrealExtban::ChannelMatcher("CHANNELBAN", "BAN", 'c'));
- ModeManager::AddChannelMode(new UnrealExtban::EntryMatcher("JOINBAN", "BAN", 'j'));
- ModeManager::AddChannelMode(new UnrealExtban::EntryMatcher("NONICKBAN", "BAN", 'n'));
- ModeManager::AddChannelMode(new UnrealExtban::EntryMatcher("QUIET", "BAN", 'q'));
- ModeManager::AddChannelMode(new UnrealExtban::RealnameMatcher("REALNAMEBAN", "BAN", 'r'));
- ModeManager::AddChannelMode(new UnrealExtban::RegisteredMatcher("REGISTEREDBAN", "BAN", 'R'));
- ModeManager::AddChannelMode(new UnrealExtban::AccountMatcher("ACCOUNTBAN", "BAN", 'a'));
- ModeManager::AddChannelMode(new UnrealExtban::FingerprintMatcher("SSLBAN", "BAN", 'S'));
- // also has O for opertype extban, but it doesn't send us users opertypes
- continue;
- case 'e':
- ModeManager::AddChannelMode(new ChannelModeList("EXCEPT", 'e'));
- continue;
- case 'I':
- ModeManager::AddChannelMode(new ChannelModeList("INVITEOVERRIDE", 'I'));
- continue;
- default:
- ModeManager::AddChannelMode(new ChannelModeList("", modebuf[t]));
- }
- }
-
- sep.GetToken(modebuf);
- for (size_t t = 0, end = modebuf.length(); t < end; ++t)
- {
- switch (modebuf[t])
- {
- case 'k':
- ModeManager::AddChannelMode(new ChannelModeKey('k'));
- continue;
- case 'f':
- ModeManager::AddChannelMode(new ChannelModeFlood('f', false));
- continue;
- case 'L':
- ModeManager::AddChannelMode(new ChannelModeParam("REDIRECT", 'L'));
- continue;
- default:
- ModeManager::AddChannelMode(new ChannelModeParam("", modebuf[t]));
- }
- }
-
- sep.GetToken(modebuf);
- for (size_t t = 0, end = modebuf.length(); t < end; ++t)
- {
- switch (modebuf[t])
- {
- case 'l':
- ModeManager::AddChannelMode(new ChannelModeParam("LIMIT", 'l', true));
- continue;
- default:
- ModeManager::AddChannelMode(new ChannelModeParam("", modebuf[t], true));
- }
- }
-
- sep.GetToken(modebuf);
- for (size_t t = 0, end = modebuf.length(); t < end; ++t)
- {
- switch (modebuf[t])
- {
- case 'p':
- ModeManager::AddChannelMode(new ChannelMode("PRIVATE", 'p'));
- continue;
- case 's':
- ModeManager::AddChannelMode(new ChannelMode("SECRET", 's'));
- continue;
- case 'm':
- ModeManager::AddChannelMode(new ChannelMode("MODERATED", 'm'));
- continue;
- case 'n':
- ModeManager::AddChannelMode(new ChannelMode("NOEXTERNAL", 'n'));
- continue;
- case 't':
- ModeManager::AddChannelMode(new ChannelMode("TOPIC", 't'));
- continue;
- case 'i':
- ModeManager::AddChannelMode(new ChannelMode("INVITE", 'i'));
- continue;
- case 'r':
- ModeManager::AddChannelMode(new ChannelModeNoone("REGISTERED", 'r'));
- continue;
- case 'R':
- ModeManager::AddChannelMode(new ChannelMode("REGISTEREDONLY", 'R'));
- continue;
- case 'c':
- ModeManager::AddChannelMode(new ChannelMode("BLOCKCOLOR", 'c'));
- continue;
- case 'O':
- ModeManager::AddChannelMode(new ChannelModeOperOnly("OPERONLY", 'O'));
- continue;
- case 'Q':
- ModeManager::AddChannelMode(new ChannelMode("NOKICK", 'Q'));
- continue;
- case 'K':
- ModeManager::AddChannelMode(new ChannelMode("NOKNOCK", 'K'));
- continue;
- case 'V':
- ModeManager::AddChannelMode(new ChannelMode("NOINVITE", 'V'));
- continue;
- case 'C':
- ModeManager::AddChannelMode(new ChannelMode("NOCTCP", 'C'));
- continue;
- case 'z':
- ModeManager::AddChannelMode(new ChannelMode("SSL", 'z'));
- continue;
- case 'N':
- ModeManager::AddChannelMode(new ChannelMode("NONICK", 'N'));
- continue;
- case 'S':
- ModeManager::AddChannelMode(new ChannelMode("STRIPCOLOR", 'S'));
- continue;
- case 'M':
- ModeManager::AddChannelMode(new ChannelMode("REGMODERATED", 'M'));
- continue;
- case 'T':
- ModeManager::AddChannelMode(new ChannelMode("NONOTICE", 'T'));
- continue;
- case 'G':
- ModeManager::AddChannelMode(new ChannelMode("CENSOR", 'G'));
- continue;
- case 'Z':
- ModeManager::AddChannelMode(new ChannelModeUnrealSSL("", 'Z'));
- continue;
- case 'd':
- // post delayed. means that channel is -D but invisible users still exist.
- continue;
- case 'D':
- ModeManager::AddChannelMode(new ChannelMode("DELAYEDJOIN", 'D'));
- continue;
- case 'P':
- ModeManager::AddChannelMode(new ChannelMode("PERM", 'P'));
- continue;
- default:
- ModeManager::AddChannelMode(new ChannelMode("", modebuf[t]));
- }
- }
- }
- else if (!capab.find("SID="))
- {
- UplinkSID = capab.substr(4);
- }
- }
-
- Message::Capab::Run(source, params);
- }
-};
-
-struct IRCDMessageChgHost : IRCDMessage
-{
- IRCDMessageChgHost(Module *creator) : IRCDMessage(creator, "CHGHOST", 2) { }
-
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- User *u = User::Find(params[0]);
- if (u)
- u->SetDisplayedHost(params[1]);
- }
-};
-
-struct IRCDMessageChgIdent : IRCDMessage
-{
- IRCDMessageChgIdent(Module *creator) : IRCDMessage(creator, "CHGIDENT", 2) { }
-
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- User *u = User::Find(params[0]);
- if (u)
- u->SetVIdent(params[1]);
- }
-};
-
-struct IRCDMessageChgName : IRCDMessage
-{
- IRCDMessageChgName(Module *creator) : IRCDMessage(creator, "CHGNAME", 2) { }
-
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- User *u = User::Find(params[0]);
- if (u)
- u->SetRealname(params[1]);
- }
-};
-
-struct IRCDMessageMD : IRCDMessage
-{
- IRCDMessageMD(Module *creator) : IRCDMessage(creator, "MD", 3) { SetFlag(IRCDMESSAGE_SOFT_LIMIT); }
-
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- const Anope::string &mdtype = params[0],
- &obj = params[1],
- &var = params[2],
- &value = params.size() > 3 ? params[3] : "";
-
- if (mdtype == "client")
- {
- User *u = User::Find(obj);
-
- if (u == NULL)
- return;
-
- if (var == "certfp" && !value.empty())
- {
- u->Extend<bool>("ssl");
- u->fingerprint = value;
- FOREACH_MOD(OnFingerprint, (u));
- }
- }
- }
-};
-
-struct IRCDMessageMode : IRCDMessage
-{
- IRCDMessageMode(Module *creator, const Anope::string &mname) : IRCDMessage(creator, mname, 2) { SetFlag(IRCDMESSAGE_SOFT_LIMIT); }
-
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- bool server_source = source.GetServer() != NULL;
- Anope::string modes = params[1];
- for (unsigned i = 2; i < params.size() - (server_source ? 1 : 0); ++i)
- modes += " " + params[i];
-
- if (IRCD->IsChannelValid(params[0]))
- {
- Channel *c = Channel::Find(params[0]);
- time_t ts = 0;
-
- try
- {
- if (server_source)
- ts = convertTo<time_t>(params[params.size() - 1]);
- }
- catch (const ConvertException &) { }
-
- if (c)
- c->SetModesInternal(source, modes, ts);
- }
- else
- {
- User *u = User::Find(params[0]);
- if (u)
- u->SetModesInternal(source, "%s", params[1].c_str());
- }
- }
-};
-
-/* netinfo
- * argv[0] = max global count
- * argv[1] = time of end sync
- * argv[2] = unreal protocol using (numeric)
- * argv[3] = cloak-crc (> u2302)
- * argv[4] = free(**)
- * argv[5] = free(**)
- * argv[6] = free(**)
- * argv[7] = ircnet
- */
-struct IRCDMessageNetInfo : IRCDMessage
-{
- IRCDMessageNetInfo(Module *creator) : IRCDMessage(creator, "NETINFO", 8) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
-
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- UplinkSocket::Message() << "NETINFO " << MaxUserCount << " " << Anope::CurTime << " " << convertTo<int>(params[2]) << " " << params[3] << " 0 0 0 :" << params[7];
- }
-};
-
-struct IRCDMessageNick : IRCDMessage
-{
- IRCDMessageNick(Module *creator) : IRCDMessage(creator, "NICK", 2) { SetFlag(IRCDMESSAGE_SOFT_LIMIT); }
-
- /*
- ** NICK - new
- ** source = NULL
- ** parv[0] = nickname
- ** parv[1] = hopcount
- ** parv[2] = timestamp
- ** parv[3] = username
- ** parv[4] = hostname
- ** parv[5] = servername
- ** parv[6] = servicestamp
- ** parv[7] = umodes
- ** parv[8] = virthost, * if none
- ** parv[9] = ip
- ** parv[10] = info
- **
- ** NICK - change
- ** source = oldnick
- ** parv[0] = new nickname
- ** parv[1] = hopcount
- */
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (params.size() == 11)
- {
- Anope::string ip;
- if (params[9] != "*")
- {
- Anope::string decoded_ip;
- Anope::B64Decode(params[9], decoded_ip);
-
- sockaddrs ip_addr;
- ip_addr.ntop(params[9].length() == 8 ? AF_INET : AF_INET6, decoded_ip.c_str());
- ip = ip_addr.addr();
- }
-
- Anope::string vhost = params[8];
- if (vhost.equals_cs("*"))
- vhost.clear();
-
- time_t user_ts = params[2].is_pos_number_only() ? convertTo<time_t>(params[2]) : Anope::CurTime;
-
- Server *s = Server::Find(params[5]);
- if (s == NULL)
- {
- Log(LOG_DEBUG) << "User " << params[0] << " introduced from non-existent server " << params[5] << "?";
- return;
- }
-
- NickAlias *na = NULL;
-
- if (params[6] == "0")
- ;
- else if (params[6].is_pos_number_only())
- {
- if (convertTo<time_t>(params[6]) == user_ts)
- na = NickAlias::Find(params[0]);
- }
- else
- {
- na = NickAlias::Find(params[6]);
- }
-
- User::OnIntroduce(params[0], params[3], params[4], vhost, ip, s, params[10], user_ts, params[7], "", na ? *na->nc : NULL);
- }
- else
- source.GetUser()->ChangeNick(params[0]);
- }
-};
-
-/** This is here because:
- *
- * If we had three servers, A, B & C linked like so: A<->B<->C
- * If Anope is linked to A and B splits from A and then reconnects
- * B introduces itself, introduces C, sends EOS for C, introduces Bs clients
- * introduces Cs clients, sends EOS for B. This causes all of Cs clients to be introduced
- * with their server "not syncing". We now send a PING immediately when receiving a new server
- * and then finish sync once we get a pong back from that server.
- */
-struct IRCDMessagePong : IRCDMessage
-{
- IRCDMessagePong(Module *creator) : IRCDMessage(creator, "PONG", 0) { SetFlag(IRCDMESSAGE_SOFT_LIMIT); SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
-
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (!source.GetServer()->IsSynced())
- source.GetServer()->Sync(false);
- }
-};
-
-struct IRCDMessageSASL : IRCDMessage
-{
- IRCDMessageSASL(Module *creator) : IRCDMessage(creator, "SASL", 4) { SetFlag(IRCDMESSAGE_SOFT_LIMIT); SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
-
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- size_t p = params[1].find('!');
- if (!SASL::sasl || p == Anope::string::npos)
- return;
-
- SASL::Message m;
- m.source = params[1];
- m.target = params[0];
- m.type = params[2];
- m.data = params[3];
- m.ext = params.size() > 4 ? params[4] : "";
-
- SASL::sasl->ProcessMessage(m);
- }
-};
-
-struct IRCDMessageSDesc : IRCDMessage
-{
- IRCDMessageSDesc(Module *creator) : IRCDMessage(creator, "SDESC", 1) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
-
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- source.GetServer()->SetDescription(params[0]);
- }
-};
-
-struct IRCDMessageSetHost : IRCDMessage
-{
- IRCDMessageSetHost(Module *creator) : IRCDMessage(creator, "SETHOST", 1) { SetFlag(IRCDMESSAGE_REQUIRE_USER); }
-
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- User *u = source.GetUser();
-
- /* When a user sets +x we receive the new host and then the mode change */
- if (u->HasMode("CLOAK"))
- u->SetDisplayedHost(params[0]);
- else
- u->SetCloakedHost(params[0]);
- }
-};
-
-struct IRCDMessageSetIdent : IRCDMessage
-{
- IRCDMessageSetIdent(Module *creator) : IRCDMessage(creator, "SETIDENT", 1) { SetFlag(IRCDMESSAGE_REQUIRE_USER); }
-
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- User *u = source.GetUser();
- u->SetVIdent(params[0]);
- }
-};
-
-struct IRCDMessageSetName : IRCDMessage
-{
- IRCDMessageSetName(Module *creator) : IRCDMessage(creator, "SETNAME", 1) { SetFlag(IRCDMESSAGE_REQUIRE_USER); }
-
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- User *u = source.GetUser();
- u->SetRealname(params[0]);
- }
-};
-
-struct IRCDMessageServer : IRCDMessage
-{
- IRCDMessageServer(Module *creator) : IRCDMessage(creator, "SERVER", 3) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
-
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- unsigned int hops = Anope::string(params[1]).is_pos_number_only() ? convertTo<unsigned>(params[1]) : 0;
-
- if (params[1].equals_cs("1"))
- {
- Anope::string desc;
- spacesepstream(params[2]).GetTokenRemainder(desc, 1);
-
- new Server(source.GetServer() == NULL ? Me : source.GetServer(), params[0], hops, desc, UplinkSID);
- }
- else
- new Server(source.GetServer(), params[0], hops, params[2]);
-
- IRCD->SendPing(Me->GetName(), params[0]);
- }
-};
-
-struct IRCDMessageSID : IRCDMessage
-{
- IRCDMessageSID(Module *creator) : IRCDMessage(creator, "SID", 4) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
-
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- unsigned int hops = Anope::string(params[1]).is_pos_number_only() ? convertTo<unsigned>(params[1]) : 0;
-
- new Server(source.GetServer(), params[0], hops, params[3], params[2]);
-
- IRCD->SendPing(Me->GetName(), params[0]);
- }
-};
-
-struct IRCDMessageSJoin : IRCDMessage
-{
- IRCDMessageSJoin(Module *creator) : IRCDMessage(creator, "SJOIN", 3) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); SetFlag(IRCDMESSAGE_SOFT_LIMIT); }
-
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- Anope::string modes;
- if (params.size() >= 4)
- for (unsigned i = 2; i < params.size() - 1; ++i)
- modes += " " + params[i];
- if (!modes.empty())
- modes.erase(modes.begin());
-
- std::list<Anope::string> bans, excepts, invites;
- std::list<Message::Join::SJoinUser> users;
-
- spacesepstream sep(params[params.size() - 1]);
- Anope::string buf;
- while (sep.GetToken(buf))
- {
- /* Ban */
- if (buf[0] == '&')
- {
- buf.erase(buf.begin());
- bans.push_back(buf);
- }
- /* Except */
- else if (buf[0] == '"')
- {
- buf.erase(buf.begin());
- excepts.push_back(buf);
- }
- /* Invex */
- else if (buf[0] == '\'')
- {
- buf.erase(buf.begin());
- invites.push_back(buf);
- }
- else
- {
- Message::Join::SJoinUser sju;
-
- /* Get prefixes from the nick */
- for (char ch; (ch = ModeManager::GetStatusChar(buf[0]));)
- {
- sju.first.AddMode(ch);
- buf.erase(buf.begin());
- }
-
- sju.second = User::Find(buf);
- if (!sju.second)
- {
- Log(LOG_DEBUG) << "SJOIN for non-existent user " << buf << " on " << params[1];
- continue;
- }
-
- users.push_back(sju);
- }
- }
-
- time_t ts = Anope::string(params[0]).is_pos_number_only() ? convertTo<time_t>(params[0]) : Anope::CurTime;
- Message::Join::SJoin(source, params[1], ts, modes, users);
-
- if (!bans.empty() || !excepts.empty() || !invites.empty())
- {
- Channel *c = Channel::Find(params[1]);
-
- if (!c || c->creation_time != ts)
- return;
-
- ChannelMode *ban = ModeManager::FindChannelModeByName("BAN"),
- *except = ModeManager::FindChannelModeByName("EXCEPT"),
- *invex = ModeManager::FindChannelModeByName("INVITEOVERRIDE");
-
- if (ban)
- for (std::list<Anope::string>::iterator it = bans.begin(), it_end = bans.end(); it != it_end; ++it)
- c->SetModeInternal(source, ban, *it);
- if (except)
- for (std::list<Anope::string>::iterator it = excepts.begin(), it_end = excepts.end(); it != it_end; ++it)
- c->SetModeInternal(source, except, *it);
- if (invex)
- for (std::list<Anope::string>::iterator it = invites.begin(), it_end = invites.end(); it != it_end; ++it)
- c->SetModeInternal(source, invex, *it);
- }
- }
-};
-
-struct IRCDMessageTopic : IRCDMessage
-{
- IRCDMessageTopic(Module *creator) : IRCDMessage(creator, "TOPIC", 4) { }
-
- /*
- ** source = sender prefix
- ** parv[0] = channel name
- ** parv[1] = topic nickname
- ** parv[2] = topic time
- ** parv[3] = topic text
- */
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- Channel *c = Channel::Find(params[0]);
- if (c)
- c->ChangeTopicInternal(source.GetUser(), params[1], params[3], Anope::string(params[2]).is_pos_number_only() ? convertTo<time_t>(params[2]) : Anope::CurTime);
- }
-};
-
-/*
- * parv[0] = nickname
- * parv[1] = hopcount
- * parv[2] = timestamp
- * parv[3] = username
- * parv[4] = hostname
- * parv[5] = UID
- * parv[6] = servicestamp
- * parv[7] = umodes
- * parv[8] = virthost, * if none
- * parv[9] = cloaked host, * if none
- * parv[10] = ip
- * parv[11] = info
- */
-struct IRCDMessageUID : IRCDMessage
-{
- IRCDMessageUID(Module *creator) : IRCDMessage(creator, "UID", 12) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
-
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- Anope::string
- nickname = params[0],
- hopcount = params[1],
- timestamp = params[2],
- username = params[3],
- hostname = params[4],
- uid = params[5],
- account = params[6],
- umodes = params[7],
- vhost = params[8],
- chost = params[9],
- ip = params[10],
- info = params[11];
-
- if (ip != "*")
- {
- Anope::string decoded_ip;
- Anope::B64Decode(ip, decoded_ip);
-
- sockaddrs ip_addr;
- ip_addr.ntop(ip.length() == 8 ? AF_INET : AF_INET6, decoded_ip.c_str());
- ip = ip_addr.addr();
- }
-
- if (vhost == "*")
- vhost.clear();
-
- if (chost == "*")
- chost.clear();
-
- time_t user_ts;
- try
- {
- user_ts = convertTo<time_t>(timestamp);
- }
- catch (const ConvertException &)
- {
- user_ts = Anope::CurTime;
- }
-
- NickAlias *na = NULL;
-
- if (account == "0")
- {
- ;
- }
- else if (account.is_pos_number_only())
- {
- if (convertTo<time_t>(account) == user_ts)
- na = NickAlias::Find(nickname);
- }
- else
- {
- na = NickAlias::Find(account);
- }
-
- User *u = User::OnIntroduce(nickname, username, hostname, vhost, ip, source.GetServer(), info, user_ts, umodes, uid, na ? *na->nc : NULL);
-
- if (u && !chost.empty() && chost != u->GetCloakedHost())
- u->SetCloakedHost(chost);
- }
-};
-
-struct IRCDMessageUmode2 : IRCDMessage
-{
- IRCDMessageUmode2(Module *creator) : IRCDMessage(creator, "UMODE2", 1) { SetFlag(IRCDMESSAGE_REQUIRE_USER); }
-
- void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
- {
- source.GetUser()->SetModesInternal(source, "%s", params[0].c_str());
- }
-};
-
-class ProtoUnreal : public Module
-{
- UnrealIRCdProto ircd_proto;
-
- /* Core message handlers */
- Message::Away message_away;
- Message::Error message_error;
- Message::Invite message_invite;
- Message::Join message_join;
- Message::Kick message_kick;
- Message::Kill message_kill, message_svskill;
- Message::MOTD message_motd;
- Message::Notice message_notice;
- Message::Part message_part;
- Message::Ping message_ping;
- Message::Privmsg message_privmsg;
- Message::Quit message_quit;
- Message::SQuit message_squit;
- Message::Stats message_stats;
- Message::Time message_time;
- Message::Version message_version;
- Message::Whois message_whois;
-
- /* Our message handlers */
- IRCDMessageCapab message_capab;
- IRCDMessageChgHost message_chghost;
- IRCDMessageChgIdent message_chgident;
- IRCDMessageChgName message_chgname;
- IRCDMessageMD message_md;
- IRCDMessageMode message_mode, message_svsmode, message_svs2mode;
- IRCDMessageNetInfo message_netinfo;
- IRCDMessageNick message_nick;
- IRCDMessagePong message_pong;
- IRCDMessageSASL message_sasl;
- IRCDMessageSDesc message_sdesc;
- IRCDMessageSetHost message_sethost;
- IRCDMessageSetIdent message_setident;
- IRCDMessageSetName message_setname;
- IRCDMessageServer message_server;
- IRCDMessageSID message_sid;
- IRCDMessageSJoin message_sjoin;
- IRCDMessageTopic message_topic;
- IRCDMessageUID message_uid;
- IRCDMessageUmode2 message_umode2;
-
- bool use_server_side_mlock;
-
- void AddModes()
- {
- ModeManager::AddChannelMode(new ChannelModeStatus("VOICE", 'v', '+', 0));
- ModeManager::AddChannelMode(new ChannelModeStatus("HALFOP", 'h', '%', 1));
- ModeManager::AddChannelMode(new ChannelModeStatus("OP", 'o', '@', 2));
- /* Unreal sends +q as * and +a as ~ */
- ModeManager::AddChannelMode(new ChannelModeStatus("PROTECT", 'a', '~', 3));
- ModeManager::AddChannelMode(new ChannelModeStatus("OWNER", 'q', '*', 4));
-
- /* Add user modes */
- ModeManager::AddUserMode(new UserMode("BOT", 'B'));
- ModeManager::AddUserMode(new UserMode("CENSOR", 'G'));
- ModeManager::AddUserMode(new UserModeOperOnly("HIDEOPER", 'H'));
- ModeManager::AddUserMode(new UserModeOperOnly("HIDEIDLE", 'I'));
- ModeManager::AddUserMode(new UserMode("REGPRIV", 'R'));
- ModeManager::AddUserMode(new UserModeOperOnly("PROTECTED", 'S'));
- ModeManager::AddUserMode(new UserMode("NOCTCP", 'T'));
- ModeManager::AddUserMode(new UserMode("WEBTV", 'V'));
- ModeManager::AddUserMode(new UserModeOperOnly("WHOIS", 'W'));
- ModeManager::AddUserMode(new UserMode("DEAF", 'd'));
- ModeManager::AddUserMode(new UserModeOperOnly("GLOBOPS", 'g'));
- ModeManager::AddUserMode(new UserModeOperOnly("HELPOP", 'h'));
- ModeManager::AddUserMode(new UserMode("INVIS", 'i'));
- ModeManager::AddUserMode(new UserModeOperOnly("OPER", 'o'));
- ModeManager::AddUserMode(new UserMode("PRIV", 'p'));
- ModeManager::AddUserMode(new UserModeOperOnly("GOD", 'q'));
- ModeManager::AddUserMode(new UserModeNoone("REGISTERED", 'r'));
- ModeManager::AddUserMode(new UserModeOperOnly("SNOMASK", 's'));
- ModeManager::AddUserMode(new UserModeNoone("VHOST", 't'));
- ModeManager::AddUserMode(new UserMode("WALLOPS", 'w'));
- ModeManager::AddUserMode(new UserMode("CLOAK", 'x'));
- ModeManager::AddUserMode(new UserModeNoone("SSL", 'z'));
- }
-
- public:
- ProtoUnreal(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, PROTOCOL | VENDOR),
- ircd_proto(this),
- message_away(this), message_error(this), message_invite(this), message_join(this), message_kick(this),
- message_kill(this), message_svskill(this, "SVSKILL"), message_motd(this), message_notice(this), message_part(this), message_ping(this),
- message_privmsg(this), message_quit(this), message_squit(this), message_stats(this), message_time(this),
- message_version(this), message_whois(this),
-
- message_capab(this), message_chghost(this), message_chgident(this), message_chgname(this), message_md(this), message_mode(this, "MODE"),
- message_svsmode(this, "SVSMODE"), message_svs2mode(this, "SVS2MODE"), message_netinfo(this), message_nick(this), message_pong(this),
- message_sasl(this), message_sdesc(this), message_sethost(this), message_setident(this), message_setname(this), message_server(this),
- message_sid(this), message_sjoin(this), message_topic(this), message_uid(this), message_umode2(this)
- {
-
- this->AddModes();
- }
-
- void Prioritize() anope_override
- {
- ModuleManager::SetPriority(this, PRIORITY_FIRST);
- }
-
- void OnReload(Configuration::Conf *conf) anope_override
- {
- use_server_side_mlock = conf->GetModule(this)->Get<bool>("use_server_side_mlock");
- }
-
- void OnUserNickChange(User *u, const Anope::string &) anope_override
- {
- u->RemoveModeInternal(Me, ModeManager::FindUserModeByName("REGISTERED"));
- if (Servers::Capab.count("ESVID") == 0)
- IRCD->SendLogout(u);
- }
-
- void OnChannelSync(Channel *c) anope_override
- {
- if (!c->ci)
- return;
-
- ModeLocks *modelocks = c->ci->GetExt<ModeLocks>("modelocks");
- if (use_server_side_mlock && Servers::Capab.count("MLOCK") > 0 && modelocks)
- {
- Anope::string modes = modelocks->GetMLockAsString(false).replace_all_cs("+", "").replace_all_cs("-", "");
- UplinkSocket::Message(Me) << "MLOCK " << static_cast<long>(c->creation_time) << " " << c->ci->name << " " << modes;
- }
- }
-
- void OnChanRegistered(ChannelInfo *ci) anope_override
- {
- ModeLocks *modelocks = ci->GetExt<ModeLocks>("modelocks");
- if (!ci->c || !use_server_side_mlock || !modelocks || !Servers::Capab.count("MLOCK"))
- return;
- Anope::string modes = modelocks->GetMLockAsString(false).replace_all_cs("+", "").replace_all_cs("-", "");
- UplinkSocket::Message(Me) << "MLOCK " << static_cast<long>(ci->c->creation_time) << " " << ci->name << " " << modes;
- }
-
- void OnDelChan(ChannelInfo *ci) anope_override
- {
- if (!ci->c || !use_server_side_mlock || !Servers::Capab.count("MLOCK"))
- return;
- UplinkSocket::Message(Me) << "MLOCK " << static_cast<long>(ci->c->creation_time) << " " << ci->name << " :";
- }
-
- EventReturn OnMLock(ChannelInfo *ci, ModeLock *lock) anope_override
- {
- ModeLocks *modelocks = ci->GetExt<ModeLocks>("modelocks");
- ChannelMode *cm = ModeManager::FindChannelModeByName(lock->name);
- if (use_server_side_mlock && cm && modelocks && ci->c && (cm->type == MODE_REGULAR || cm->type == MODE_PARAM) && Servers::Capab.count("MLOCK") > 0)
- {
- Anope::string modes = modelocks->GetMLockAsString(false).replace_all_cs("+", "").replace_all_cs("-", "") + cm->mchar;
- UplinkSocket::Message(Me) << "MLOCK " << static_cast<long>(ci->c->creation_time) << " " << ci->name << " " << modes;
- }
-
- return EVENT_CONTINUE;
- }
-
- EventReturn OnUnMLock(ChannelInfo *ci, ModeLock *lock) anope_override
- {
- ModeLocks *modelocks = ci->GetExt<ModeLocks>("modelocks");
- ChannelMode *cm = ModeManager::FindChannelModeByName(lock->name);
- if (use_server_side_mlock && cm && modelocks && ci->c && (cm->type == MODE_REGULAR || cm->type == MODE_PARAM) && Servers::Capab.count("MLOCK") > 0)
- {
- Anope::string modes = modelocks->GetMLockAsString(false).replace_all_cs("+", "").replace_all_cs("-", "").replace_all_cs(cm->mchar, "");
- UplinkSocket::Message(Me) << "MLOCK " << static_cast<long>(ci->c->creation_time) << " " << ci->name << " " << modes;
- }
-
- return EVENT_CONTINUE;
- }
-};
-
-MODULE_INIT(ProtoUnreal)
diff --git a/modules/pseudoclients/chanserv.cpp b/modules/pseudoclients/chanserv.cpp
deleted file mode 100644
index 9c56116b7..000000000
--- a/modules/pseudoclients/chanserv.cpp
+++ /dev/null
@@ -1,479 +0,0 @@
-/* ChanServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-#include "modules/cs_mode.h"
-
-inline static Anope::string BotModes()
-{
- return Config->GetModule("botserv")->Get<Anope::string>("botmodes",
- Config->GetModule("chanserv")->Get<Anope::string>("botmodes", "o")
- );
-}
-
-class ChanServCore : public Module, public ChanServService
-{
- Reference<BotInfo> ChanServ;
- std::vector<Anope::string> defaults;
- ExtensibleItem<bool> inhabit;
- ExtensibleRef<bool> persist;
- bool always_lower;
-
- public:
- ChanServCore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, PSEUDOCLIENT | VENDOR),
- ChanServService(this), inhabit(this, "inhabit"), persist("PERSIST"), always_lower(false)
- {
- }
-
- void Hold(Channel *c) anope_override
- {
- /** A timer used to keep the BotServ bot/ChanServ in the channel
- * after kicking the last user in a channel
- */
- class ChanServTimer : public Timer
- {
- Reference<BotInfo> &ChanServ;
- ExtensibleItem<bool> &inhabit;
- Reference<Channel> c;
-
- public:
- /** Constructor
- * @param chan The channel
- */
- ChanServTimer(Reference<BotInfo> &cs, ExtensibleItem<bool> &i, Module *m, Channel *chan) : Timer(m, Config->GetModule(m)->Get<time_t>("inhabit", "15s")), ChanServ(cs), inhabit(i), c(chan)
- {
- if (!ChanServ || !c)
- return;
- inhabit.Set(c, true);
- if (!c->ci || !c->ci->bi)
- ChanServ->Join(c);
- else if (!c->FindUser(c->ci->bi))
- c->ci->bi->Join(c);
-
- /* Set +ntsi to prevent rejoin */
- c->SetMode(NULL, "NOEXTERNAL");
- c->SetMode(NULL, "TOPIC");
- c->SetMode(NULL, "SECRET");
- c->SetMode(NULL, "INVITE");
- }
-
- /** Called when the delay is up
- * @param The current time
- */
- void Tick(time_t) anope_override
- {
- if (!c)
- return;
-
- /* In the event we don't part */
- c->RemoveMode(NULL, "SECRET");
- c->RemoveMode(NULL, "INVITE");
-
- inhabit.Unset(c); /* now we're done changing modes, unset inhabit */
-
- if (!c->ci || !c->ci->bi)
- {
- if (ChanServ)
- ChanServ->Part(c);
- }
- /* If someone has rejoined this channel in the meantime, don't part the bot */
- else if (c->users.size() <= 1)
- c->ci->bi->Part(c);
- }
- };
-
- if (inhabit.HasExt(c))
- return;
-
- new ChanServTimer(ChanServ, inhabit, this->owner, c);
- }
-
- void OnReload(Configuration::Conf *conf) anope_override
- {
- const Anope::string &channick = conf->GetModule(this)->Get<const Anope::string>("client");
-
- if (channick.empty())
- throw ConfigException(Module::name + ": <client> must be defined");
-
- BotInfo *bi = BotInfo::Find(channick, true);
- if (!bi)
- throw ConfigException(Module::name + ": no bot named " + channick);
-
- ChanServ = bi;
-
- spacesepstream(conf->GetModule(this)->Get<const Anope::string>("defaults", "greet fantasy")).GetTokens(defaults);
- if (defaults.empty())
- {
- defaults.push_back("KEEPTOPIC");
- defaults.push_back("CS_SECURE");
- defaults.push_back("SECUREFOUNDER");
- defaults.push_back("SIGNKICK");
- }
- else if (defaults[0].equals_ci("none"))
- defaults.clear();
-
- always_lower = conf->GetModule(this)->Get<bool>("always_lower_ts");
- }
-
- void OnBotDelete(BotInfo *bi) anope_override
- {
- if (bi == ChanServ)
- ChanServ = NULL;
- }
-
- EventReturn OnBotPrivmsg(User *u, BotInfo *bi, Anope::string &message) anope_override
- {
- if (bi == ChanServ && Config->GetModule(this)->Get<bool>("opersonly") && !u->HasMode("OPER"))
- {
- u->SendMessage(bi, ACCESS_DENIED);
- return EVENT_STOP;
- }
-
- return EVENT_CONTINUE;
- }
-
- void OnDelCore(NickCore *nc) anope_override
- {
- std::deque<ChannelInfo *> chans;
- nc->GetChannelReferences(chans);
- int max_reg = Config->GetModule(this)->Get<int>("maxregistered");
-
- for (unsigned i = 0; i < chans.size(); ++i)
- {
- ChannelInfo *ci = chans[i];
-
- if (ci->GetFounder() == nc)
- {
- NickCore *newowner = NULL;
- if (ci->GetSuccessor() && ci->GetSuccessor() != nc && (ci->GetSuccessor()->IsServicesOper() || !max_reg || ci->GetSuccessor()->channelcount < max_reg))
- newowner = ci->GetSuccessor();
- else
- {
- const ChanAccess *highest = NULL;
- for (unsigned j = 0; j < ci->GetAccessCount(); ++j)
- {
- const ChanAccess *ca = ci->GetAccess(j);
- NickCore *anc = ca->GetAccount();
-
- if (!anc || (!anc->IsServicesOper() && max_reg && anc->channelcount >= max_reg) || (anc == nc))
- continue;
- if (!highest || *ca > *highest)
- highest = ca;
- }
- if (highest)
- newowner = highest->GetAccount();
- }
-
- if (newowner)
- {
- Log(LOG_NORMAL, "chanserv/drop", ChanServ) << "Transferring foundership of " << ci->name << " from deleted nick " << nc->display << " to " << newowner->display;
- ci->SetFounder(newowner);
- ci->SetSuccessor(NULL);
- }
- else
- {
- Log(LOG_NORMAL, "chanserv/drop", ChanServ) << "Deleting channel " << ci->name << " owned by deleted nick " << nc->display;
-
- delete ci;
- continue;
- }
- }
-
- if (ci->GetSuccessor() == nc)
- ci->SetSuccessor(NULL);
-
- for (unsigned j = 0; j < ci->GetAccessCount(); ++j)
- {
- const ChanAccess *ca = ci->GetAccess(j);
-
- if (ca->GetAccount() == nc)
- {
- delete ci->EraseAccess(j);
- break;
- }
- }
-
- for (unsigned j = 0; j < ci->GetAkickCount(); ++j)
- {
- const AutoKick *akick = ci->GetAkick(j);
- if (akick->nc == nc)
- {
- ci->EraseAkick(j);
- break;
- }
- }
- }
- }
-
- void OnDelChan(ChannelInfo *ci) anope_override
- {
- /* remove access entries that are this channel */
-
- std::deque<Anope::string> chans;
- ci->GetChannelReferences(chans);
-
- for (unsigned i = 0; i < chans.size(); ++i)
- {
- ChannelInfo *c = ChannelInfo::Find(chans[i]);
- if (!c)
- continue;
-
- for (unsigned j = 0; j < c->GetAccessCount(); ++j)
- {
- ChanAccess *a = c->GetAccess(j);
-
- if (a->Mask().equals_ci(ci->name))
- {
- delete a;
- break;
- }
- }
- }
-
- if (ci->c)
- {
- ci->c->RemoveMode(ci->WhoSends(), "REGISTERED", "", false);
-
- const Anope::string &require = Config->GetModule(this)->Get<const Anope::string>("require");
- if (!require.empty())
- ci->c->SetModes(ci->WhoSends(), false, "-%s", require.c_str());
- }
- }
-
- EventReturn OnPreHelp(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (!params.empty() || source.c || source.service != *ChanServ)
- return EVENT_CONTINUE;
- source.Reply(_("\002%s\002 allows you to register and control various\n"
- "aspects of channels. %s can often prevent\n"
- "malicious users from \"taking over\" channels by limiting\n"
- "who is allowed channel operator privileges. Available\n"
- "commands are listed below; to use them, type\n"
- "\002%s%s \037command\037\002. For more information on a\n"
- "specific command, type \002%s%s HELP \037command\037\002.\n"),
- ChanServ->nick.c_str(), ChanServ->nick.c_str(), Config->StrictPrivmsg.c_str(), ChanServ->nick.c_str(), Config->StrictPrivmsg.c_str(), ChanServ->nick.c_str(), ChanServ->nick.c_str(), source.command.c_str());
- return EVENT_CONTINUE;
- }
-
- void OnPostHelp(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (!params.empty() || source.c || source.service != *ChanServ)
- return;
- time_t chanserv_expire = Config->GetModule(this)->Get<time_t>("expire", "14d");
- if (chanserv_expire >= 86400)
- source.Reply(_(" \n"
- "Note that any channel which is not used for %d days\n"
- "(i.e. which no user on the channel's access list enters\n"
- "for that period of time) will be automatically dropped."), chanserv_expire / 86400);
- if (source.IsServicesOper())
- source.Reply(_(" \n"
- "Services Operators can also, depending on their access drop\n"
- "any channel, view (and modify) the access, levels and akick\n"
- "lists and settings for any channel."));
- }
-
- void OnCheckModes(Reference<Channel> &c) anope_override
- {
- if (!c)
- return;
-
- if (c->ci)
- c->SetMode(c->ci->WhoSends(), "REGISTERED", "", false);
- else
- c->RemoveMode(c->ci->WhoSends(), "REGISTERED", "", false);
-
- const Anope::string &require = Config->GetModule(this)->Get<const Anope::string>("require");
- if (!require.empty())
- {
- if (c->ci)
- c->SetModes(c->ci->WhoSends(), false, "+%s", require.c_str());
- else
- c->SetModes(c->ci->WhoSends(), false, "-%s", require.c_str());
- }
- }
-
- void OnCreateChan(ChannelInfo *ci) anope_override
- {
- /* Set default chan flags */
- for (unsigned i = 0; i < defaults.size(); ++i)
- ci->Extend<bool>(defaults[i].upper());
- }
-
- EventReturn OnCanSet(User *u, const ChannelMode *cm) anope_override
- {
- if (Config->GetModule(this)->Get<const Anope::string>("nomlock").find(cm->mchar) != Anope::string::npos
- || Config->GetModule(this)->Get<const Anope::string>("require").find(cm->mchar) != Anope::string::npos)
- return EVENT_STOP;
- return EVENT_CONTINUE;
- }
-
- void OnChannelSync(Channel *c) anope_override
- {
- bool perm = c->HasMode("PERM") || (c->ci && persist && persist->HasExt(c->ci));
- if (!perm && !c->botchannel && (c->users.empty() || (c->users.size() == 1 && c->users.begin()->second->user->server == Me)))
- {
- this->Hold(c);
- }
- }
-
- void OnLog(Log *l) anope_override
- {
- if (l->type == LOG_CHANNEL)
- l->bi = ChanServ;
- }
-
- void OnExpireTick() anope_override
- {
- time_t chanserv_expire = Config->GetModule(this)->Get<time_t>("expire", "14d");
-
- if (!chanserv_expire || Anope::NoExpire || Anope::ReadOnly)
- return;
-
- for (registered_channel_map::const_iterator it = RegisteredChannelList->begin(), it_end = RegisteredChannelList->end(); it != it_end; )
- {
- ChannelInfo *ci = it->second;
- ++it;
-
- bool expire = false;
-
- if (Anope::CurTime - ci->last_used >= chanserv_expire)
- {
- if (ci->c)
- {
- time_t last_used = ci->last_used;
- for (Channel::ChanUserList::const_iterator cit = ci->c->users.begin(), cit_end = ci->c->users.end(); cit != cit_end && last_used == ci->last_used; ++cit)
- ci->AccessFor(cit->second->user);
- expire = last_used == ci->last_used;
- }
- else
- expire = true;
- }
-
- FOREACH_MOD(OnPreChanExpire, (ci, expire));
-
- if (expire)
- {
- Log(LOG_NORMAL, "chanserv/expire", ChanServ) << "Expiring channel " << ci->name << " (founder: " << (ci->GetFounder() ? ci->GetFounder()->display : "(none)") << ")";
- FOREACH_MOD(OnChanExpire, (ci));
- delete ci;
- }
- }
- }
-
- EventReturn OnCheckDelete(Channel *c) anope_override
- {
- /* Do not delete this channel if ChanServ/a BotServ bot is inhabiting it */
- if (inhabit.HasExt(c))
- return EVENT_STOP;
-
- return EVENT_CONTINUE;
- }
-
- void OnPreUplinkSync(Server *serv) anope_override
- {
- if (!persist)
- return;
- /* Find all persistent channels and create them, as we are about to finish burst to our uplink */
- for (registered_channel_map::iterator it = RegisteredChannelList->begin(), it_end = RegisteredChannelList->end(); it != it_end; ++it)
- {
- ChannelInfo *ci = it->second;
- if (persist->HasExt(ci))
- {
- bool c;
- ci->c = Channel::FindOrCreate(ci->name, c, ci->time_registered);
-
- if (ModeManager::FindChannelModeByName("PERM") != NULL)
- {
- if (c)
- IRCD->SendChannel(ci->c);
- ci->c->SetMode(NULL, "PERM");
- }
- else
- {
- if (!ci->bi)
- ci->WhoSends()->Assign(NULL, ci);
- if (ci->c->FindUser(ci->bi) == NULL)
- {
- ChannelStatus status(BotModes());
- ci->bi->Join(ci->c, &status);
- }
- }
- }
- }
-
- }
-
- void OnChanRegistered(ChannelInfo *ci) anope_override
- {
- if (!persist || !ci->c)
- return;
- /* Mark the channel as persistent */
- if (ci->c->HasMode("PERM"))
- persist->Set(ci);
- /* Persist may be in def cflags, set it here */
- else if (persist->HasExt(ci))
- ci->c->SetMode(NULL, "PERM");
- }
-
- void OnJoinChannel(User *u, Channel *c) anope_override
- {
- if (always_lower && c->ci && c->creation_time > c->ci->time_registered)
- {
- Log(LOG_DEBUG) << "Changing TS of " << c->name << " from " << c->creation_time << " to " << c->ci->time_registered;
- c->creation_time = c->ci->time_registered;
- IRCD->SendChannel(c);
- c->Reset();
- }
- }
-
- EventReturn OnChannelModeSet(Channel *c, MessageSource &setter, ChannelMode *mode, const Anope::string &param) anope_override
- {
- if (!always_lower && Anope::CurTime == c->creation_time && c->ci && setter.GetUser() && !setter.GetUser()->server->IsULined())
- {
- ChanUserContainer *cu = c->FindUser(setter.GetUser());
- ChannelMode *cm = ModeManager::FindChannelModeByName("OP");
- if (cu && cm && !cu->status.HasMode(cm->mchar))
- {
- /* Our -o and their mode change crossing, bounce their mode */
- c->RemoveMode(c->ci->WhoSends(), mode, param);
- /* We don't set mlocks until after the join has finished processing, it will stack with this change,
- * so there isn't much for the user to remove except -nt etc which is likely locked anyway.
- */
- }
- }
-
- return EVENT_CONTINUE;
- }
-
- void OnChanInfo(CommandSource &source, ChannelInfo *ci, InfoFormatter &info, bool show_all) anope_override
- {
- if (!show_all)
- return;
-
- time_t chanserv_expire = Config->GetModule(this)->Get<time_t>("expire", "14d");
- if (!ci->HasExt("CS_NO_EXPIRE") && chanserv_expire && !Anope::NoExpire && ci->last_used != Anope::CurTime)
- info[_("Expires")] = Anope::strftime(ci->last_used + chanserv_expire, source.GetAccount());
- }
-
- void OnSetCorrectModes(User *user, Channel *chan, AccessGroup &access, bool &give_modes, bool &take_modes) anope_override
- {
- if (always_lower)
- // Since we always lower the TS, the other side will remove the modes if the channel ts lowers, so we don't
- // have to worry about it
- take_modes = false;
- else if (ModeManager::FindChannelModeByName("REGISTERED"))
- // Otherwise if the registered channel mode exists, we should remove modes if the channel is not +r
- take_modes = !chan->HasMode("REGISTERED");
- }
-};
-
-MODULE_INIT(ChanServCore)
-
diff --git a/modules/pseudoclients/global.cpp b/modules/pseudoclients/global.cpp
deleted file mode 100644
index 065627cb2..000000000
--- a/modules/pseudoclients/global.cpp
+++ /dev/null
@@ -1,96 +0,0 @@
-/* Global core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class GlobalCore : public Module, public GlobalService
-{
- Reference<BotInfo> Global;
-
- void ServerGlobal(BotInfo *sender, Server *s, const Anope::string &message)
- {
- if (s != Me && !s->IsJuped())
- s->Notice(sender, message);
- for (unsigned i = 0, j = s->GetLinks().size(); i < j; ++i)
- this->ServerGlobal(sender, s->GetLinks()[i], message);
- }
-
- public:
- GlobalCore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, PSEUDOCLIENT | VENDOR),
- GlobalService(this)
- {
- }
-
- void SendGlobal(BotInfo *sender, const Anope::string &source, const Anope::string &message) anope_override
- {
- if (Me->GetLinks().empty())
- return;
- if (!sender)
- sender = Global;
- if (!sender)
- return;
-
- Anope::string rmessage;
-
- if (!source.empty() && !Config->GetModule("global")->Get<bool>("anonymousglobal"))
- rmessage = "[" + source + "] " + message;
- else
- rmessage = message;
-
- this->ServerGlobal(sender, Servers::GetUplink(), rmessage);
- }
-
- void OnReload(Configuration::Conf *conf) anope_override
- {
- const Anope::string &glnick = conf->GetModule(this)->Get<const Anope::string>("client");
-
- if (glnick.empty())
- throw ConfigException(Module::name + ": <client> must be defined");
-
- BotInfo *bi = BotInfo::Find(glnick, true);
- if (!bi)
- throw ConfigException(Module::name + ": no bot named " + glnick);
-
- Global = bi;
- }
-
- void OnRestart() anope_override
- {
- const Anope::string &gl = Config->GetModule(this)->Get<const Anope::string>("globaloncycledown");
- if (!gl.empty())
- this->SendGlobal(Global, "", gl);
- }
-
- void OnShutdown() anope_override
- {
- const Anope::string &gl = Config->GetModule(this)->Get<const Anope::string>("globaloncycledown");
- if (!gl.empty())
- this->SendGlobal(Global, "", gl);
- }
-
- void OnNewServer(Server *s) anope_override
- {
- const Anope::string &gl = Config->GetModule(this)->Get<const Anope::string>("globaloncycleup");
- if (!gl.empty() && !Me->IsSynced())
- s->Notice(Global, gl);
- }
-
- EventReturn OnPreHelp(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (!params.empty() || source.c || source.service != *Global)
- return EVENT_CONTINUE;
- source.Reply(_("%s commands:"), Global->nick.c_str());
- return EVENT_CONTINUE;
- }
-};
-
-MODULE_INIT(GlobalCore)
-
diff --git a/modules/pseudoclients/hostserv.cpp b/modules/pseudoclients/hostserv.cpp
deleted file mode 100644
index bb0f7e839..000000000
--- a/modules/pseudoclients/hostserv.cpp
+++ /dev/null
@@ -1,122 +0,0 @@
-/* HostServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class HostServCore : public Module
-{
- Reference<BotInfo> HostServ;
- public:
- HostServCore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, PSEUDOCLIENT | VENDOR)
- {
- if (!IRCD || !IRCD->CanSetVHost)
- throw ModuleException("Your IRCd does not support vhosts");
- }
-
- void OnReload(Configuration::Conf *conf) anope_override
- {
- const Anope::string &hsnick = conf->GetModule(this)->Get<const Anope::string>("client");
-
- if (hsnick.empty())
- throw ConfigException(Module::name + ": <client> must be defined");
-
- BotInfo *bi = BotInfo::Find(hsnick, true);
- if (!bi)
- throw ConfigException(Module::name + ": no bot named " + hsnick);
-
- HostServ = bi;
- }
-
- void OnUserLogin(User *u) anope_override
- {
- if (!IRCD->CanSetVHost)
- return;
-
- const NickAlias *na = NickAlias::Find(u->nick);
- if (!na || na->nc != u->Account() || !na->HasVhost())
- na = NickAlias::Find(u->Account()->display);
- if (!na || !na->HasVhost())
- return;
-
- if (u->vhost.empty() || !u->vhost.equals_cs(na->GetVhostHost()) || (!na->GetVhostIdent().empty() && !u->GetVIdent().equals_cs(na->GetVhostIdent())))
- {
- IRCD->SendVhost(u, na->GetVhostIdent(), na->GetVhostHost());
-
- u->vhost = na->GetVhostHost();
- u->UpdateHost();
-
- if (IRCD->CanSetVIdent && !na->GetVhostIdent().empty())
- u->SetVIdent(na->GetVhostIdent());
-
- if (HostServ)
- {
- if (!na->GetVhostIdent().empty())
- u->SendMessage(HostServ, _("Your vhost of \002%s\002@\002%s\002 is now activated."), na->GetVhostIdent().c_str(), na->GetVhostHost().c_str());
- else
- u->SendMessage(HostServ, _("Your vhost of \002%s\002 is now activated."), na->GetVhostHost().c_str());
- }
- }
- }
-
- void OnNickUpdate(User *u) anope_override
- {
- this->OnUserLogin(u);
- }
-
- EventReturn OnPreHelp(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (!params.empty() || source.c || source.service != *HostServ)
- return EVENT_CONTINUE;
- source.Reply(_("%s commands:"), HostServ->nick.c_str());
- return EVENT_CONTINUE;
- }
-
- void OnSetVhost(NickAlias *na) anope_override
- {
- if (Config->GetModule(this)->Get<bool>("activate_on_set"))
- {
- User *u = User::Find(na->nick);
-
- if (u && u->Account() == na->nc)
- {
- IRCD->SendVhost(u, na->GetVhostIdent(), na->GetVhostHost());
-
- u->vhost = na->GetVhostHost();
- u->UpdateHost();
-
- if (IRCD->CanSetVIdent && !na->GetVhostIdent().empty())
- u->SetVIdent(na->GetVhostIdent());
-
- if (HostServ)
- {
- if (!na->GetVhostIdent().empty())
- u->SendMessage(HostServ, _("Your vhost of \002%s\002@\002%s\002 is now activated."), na->GetVhostIdent().c_str(), na->GetVhostHost().c_str());
- else
- u->SendMessage(HostServ, _("Your vhost of \002%s\002 is now activated."), na->GetVhostHost().c_str());
- }
- }
- }
- }
-
- void OnDeleteVhost(NickAlias *na) anope_override
- {
- if (Config->GetModule(this)->Get<bool>("activate_on_set"))
- {
- User *u = User::Find(na->nick);
-
- if (u && u->Account() == na->nc)
- IRCD->SendVhostDel(u);
- }
- }
-};
-
-MODULE_INIT(HostServCore)
-
diff --git a/modules/pseudoclients/memoserv.cpp b/modules/pseudoclients/memoserv.cpp
deleted file mode 100644
index 37e9545aa..000000000
--- a/modules/pseudoclients/memoserv.cpp
+++ /dev/null
@@ -1,223 +0,0 @@
-/* MemoServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class MemoServCore : public Module, public MemoServService
-{
- Reference<BotInfo> MemoServ;
-
- bool SendMemoMail(NickCore *nc, MemoInfo *mi, Memo *m)
- {
- Anope::string subject = Language::Translate(nc, Config->GetBlock("mail")->Get<const Anope::string>("memo_subject").c_str()),
- message = Language::Translate(Config->GetBlock("mail")->Get<const Anope::string>("memo_message").c_str());
-
- subject = subject.replace_all_cs("%n", nc->display);
- subject = subject.replace_all_cs("%s", m->sender);
- subject = subject.replace_all_cs("%d", stringify(mi->GetIndex(m) + 1));
- subject = subject.replace_all_cs("%t", m->text);
- subject = subject.replace_all_cs("%N", Config->GetBlock("networkinfo")->Get<const Anope::string>("networkname"));
-
- message = message.replace_all_cs("%n", nc->display);
- message = message.replace_all_cs("%s", m->sender);
- message = message.replace_all_cs("%d", stringify(mi->GetIndex(m) + 1));
- message = message.replace_all_cs("%t", m->text);
- message = message.replace_all_cs("%N", Config->GetBlock("networkinfo")->Get<const Anope::string>("networkname"));
-
- return Mail::Send(nc, subject, message);
- }
-
- public:
- MemoServCore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, PSEUDOCLIENT | VENDOR),
- MemoServService(this)
- {
- }
-
- MemoResult Send(const Anope::string &source, const Anope::string &target, const Anope::string &message, bool force) anope_override
- {
- bool ischan;
- MemoInfo *mi = MemoInfo::GetMemoInfo(target, ischan);
-
- if (mi == NULL)
- return MEMO_INVALID_TARGET;
-
- User *sender = User::Find(source, true);
- if (sender != NULL && !sender->HasPriv("memoserv/no-limit") && !force)
- {
- time_t send_delay = Config->GetModule("memoserv")->Get<time_t>("senddelay");
- if (send_delay > 0 && sender->lastmemosend + send_delay > Anope::CurTime)
- return MEMO_TOO_FAST;
- else if (!mi->memomax)
- return MEMO_TARGET_FULL;
- else if (mi->memomax > 0 && mi->memos->size() >= static_cast<unsigned>(mi->memomax))
- return MEMO_TARGET_FULL;
- else if (mi->HasIgnore(sender))
- return MEMO_SUCCESS;
- }
-
- if (sender != NULL)
- sender->lastmemosend = Anope::CurTime;
-
- Memo *m = new Memo();
- m->mi = mi;
- mi->memos->push_back(m);
- m->owner = target;
- m->sender = source;
- m->time = Anope::CurTime;
- m->text = message;
- m->unread = true;
-
- FOREACH_MOD(OnMemoSend, (source, target, mi, m));
-
- if (ischan)
- {
- ChannelInfo *ci = ChannelInfo::Find(target);
-
- if (ci->c)
- {
- for (Channel::ChanUserList::iterator it = ci->c->users.begin(), it_end = ci->c->users.end(); it != it_end; ++it)
- {
- ChanUserContainer *cu = it->second;
-
- if (ci->AccessFor(cu->user).HasPriv("MEMO"))
- {
- if (cu->user->Account() && cu->user->Account()->HasExt("MEMO_RECEIVE"))
- cu->user->SendMessage(MemoServ, MEMO_NEW_X_MEMO_ARRIVED, ci->name.c_str(), Config->StrictPrivmsg.c_str(), MemoServ->nick.c_str(), ci->name.c_str(), mi->memos->size());
- }
- }
- }
- }
- else
- {
- NickCore *nc = NickAlias::Find(target)->nc;
-
- if (nc->HasExt("MEMO_RECEIVE"))
- {
- for (unsigned i = 0; i < nc->aliases->size(); ++i)
- {
- const NickAlias *na = nc->aliases->at(i);
- User *user = User::Find(na->nick, true);
- if (user && user->IsIdentified())
- user->SendMessage(MemoServ, MEMO_NEW_MEMO_ARRIVED, source.c_str(), Config->StrictPrivmsg.c_str(), MemoServ->nick.c_str(), mi->memos->size());
- }
- }
-
- /* let's get out the mail if set in the nickcore - certus */
- if (nc->HasExt("MEMO_MAIL"))
- SendMemoMail(nc, mi, m);
- }
-
- return MEMO_SUCCESS;
- }
-
- void Check(User *u)
- {
- const NickCore *nc = u->Account();
- if (!nc)
- return;
-
- unsigned i = 0, end = nc->memos.memos->size(), newcnt = 0;
- for (; i < end; ++i)
- if (nc->memos.GetMemo(i)->unread)
- ++newcnt;
- if (newcnt > 0)
- u->SendMessage(MemoServ, newcnt == 1 ? _("You have 1 new memo.") : _("You have %d new memos."), newcnt);
- if (nc->memos.memomax > 0 && nc->memos.memos->size() >= static_cast<unsigned>(nc->memos.memomax))
- {
- if (nc->memos.memos->size() > static_cast<unsigned>(nc->memos.memomax))
- u->SendMessage(MemoServ, _("You are over your maximum number of memos (%d). You will be unable to receive any new memos until you delete some of your current ones."), nc->memos.memomax);
- else
- u->SendMessage(MemoServ, _("You have reached your maximum number of memos (%d). You will be unable to receive any new memos until you delete some of your current ones."), nc->memos.memomax);
- }
- }
-
- void OnReload(Configuration::Conf *conf) anope_override
- {
- const Anope::string &msnick = conf->GetModule(this)->Get<const Anope::string>("client");
-
- if (msnick.empty())
- throw ConfigException(Module::name + ": <client> must be defined");
-
- BotInfo *bi = BotInfo::Find(msnick, true);
- if (!bi)
- throw ConfigException(Module::name + ": no bot named " + msnick);
-
- MemoServ = bi;
- }
-
- void OnNickCoreCreate(NickCore *nc) anope_override
- {
- nc->memos.memomax = Config->GetModule(this)->Get<int>("maxmemos");
- }
-
- void OnCreateChan(ChannelInfo *ci) anope_override
- {
- ci->memos.memomax = Config->GetModule(this)->Get<int>("maxmemos");
- }
-
- void OnBotDelete(BotInfo *bi) anope_override
- {
- if (bi == MemoServ)
- MemoServ = NULL;
- }
-
- void OnNickIdentify(User *u) anope_override
- {
- this->Check(u);
- }
-
- void OnJoinChannel(User *u, Channel *c) anope_override
- {
- if (c->ci && !c->ci->memos.memos->empty() && c->ci->AccessFor(u).HasPriv("MEMO"))
- {
- if (c->ci->memos.memos->size() == 1)
- u->SendMessage(MemoServ, _("There is \002%d\002 memo on channel %s."), c->ci->memos.memos->size(), c->ci->name.c_str());
- else
- u->SendMessage(MemoServ, _("There are \002%d\002 memos on channel %s."), c->ci->memos.memos->size(), c->ci->name.c_str());
- }
- }
-
- void OnUserAway(User *u, const Anope::string &message) anope_override
- {
- if (message.empty())
- this->Check(u);
- }
-
- void OnNickUpdate(User *u) anope_override
- {
- this->Check(u);
- }
-
- EventReturn OnPreHelp(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (!params.empty() || source.c || source.service != *MemoServ)
- return EVENT_CONTINUE;
- source.Reply(_("\002%s\002 is a utility allowing IRC users to send short\n"
- "messages to other IRC users, whether they are online at\n"
- "the time or not, or to channels(*). Both the sender's\n"
- "nickname and the target nickname or channel must be\n"
- "registered in order to send a memo.\n"
- "%s's commands include:"), MemoServ->nick.c_str(), MemoServ->nick.c_str());
- return EVENT_CONTINUE;
- }
-
- void OnPostHelp(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (!params.empty() || source.c || source.service != *MemoServ)
- return;
- source.Reply(_(" \n"
- "Type \002%s%s HELP \037command\037\002 for help on any of the\n"
- "above commands."), Config->StrictPrivmsg.c_str(), MemoServ->nick.c_str());
- }
-};
-
-MODULE_INIT(MemoServCore)
-
diff --git a/modules/pseudoclients/nickserv.cpp b/modules/pseudoclients/nickserv.cpp
deleted file mode 100644
index 9049a0fe1..000000000
--- a/modules/pseudoclients/nickserv.cpp
+++ /dev/null
@@ -1,575 +0,0 @@
-/* NickServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class NickServCollide;
-static std::set<NickServCollide *> collides;
-
-/** Timer for colliding nicks to force people off of nicknames
- */
-class NickServCollide : public Timer
-{
- NickServService *service;
- Reference<User> u;
- time_t ts;
- Reference<NickAlias> na;
-
- public:
- NickServCollide(Module *me, NickServService *nss, User *user, NickAlias *nick, time_t delay) : Timer(me, delay), service(nss), u(user), ts(user->timestamp), na(nick)
- {
- collides.insert(this);
- }
-
- ~NickServCollide()
- {
- collides.erase(this);
- }
-
- User *GetUser()
- {
- return u;
- }
-
- NickAlias *GetNick()
- {
- return na;
- }
-
- void Tick(time_t t) anope_override
- {
- if (!u || !na)
- return;
- /* If they identified or don't exist anymore, don't kill them. */
- if (u->Account() == na->nc || u->timestamp > ts)
- return;
-
- service->Collide(u, na);
- }
-};
-
-/** Timer for removing HELD status from nicks.
- */
-class NickServHeld : public Timer
-{
- Reference<NickAlias> na;
- Anope::string nick;
- public:
- NickServHeld(Module *me, NickAlias *n, long l) : Timer(me, l), na(n), nick(na->nick)
- {
- n->Extend<bool>("HELD");
- }
-
- void Tick(time_t)
- {
- if (na)
- na->Shrink<bool>("HELD");
- }
-};
-
-class NickServRelease;
-static Anope::map<NickServRelease *> NickServReleases;
-
-/** Timer for releasing nicks to be available for use
- */
-class NickServRelease : public User, public Timer
-{
- Anope::string nick;
-
- public:
- NickServRelease(Module *me, NickAlias *na, time_t delay) : User(na->nick, Config->GetModule("nickserv")->Get<const Anope::string>("enforceruser", "user"),
- Config->GetModule("nickserv")->Get<const Anope::string>("enforcerhost", "services.localhost.net"), "", "", Me, "Services Enforcer", Anope::CurTime, "", IRCD->UID_Retrieve(), NULL), Timer(me, delay), nick(na->nick)
- {
- /* Erase the current release timer and use the new one */
- Anope::map<NickServRelease *>::iterator nit = NickServReleases.find(this->nick);
- if (nit != NickServReleases.end())
- {
- IRCD->SendQuit(nit->second, "");
- delete nit->second;
- }
-
- NickServReleases.insert(std::make_pair(this->nick, this));
-
- IRCD->SendClientIntroduction(this);
- }
-
- ~NickServRelease()
- {
- IRCD->SendQuit(this, "");
- NickServReleases.erase(this->nick);
- }
-
- void Tick(time_t t) anope_override { }
-};
-
-class NickServCore : public Module, public NickServService
-{
- Reference<BotInfo> NickServ;
- std::vector<Anope::string> defaults;
- ExtensibleItem<bool> held, collided;
-
- void OnCancel(User *u, NickAlias *na)
- {
- if (collided.HasExt(na))
- {
- collided.Unset(na);
-
- new NickServHeld(this, na, Config->GetModule("nickserv")->Get<time_t>("releasetimeout", "1m"));
-
- if (IRCD->CanSVSHold)
- IRCD->SendSVSHold(na->nick, Config->GetModule("nickserv")->Get<time_t>("releasetimeout", "1m"));
- else
- new NickServRelease(this, na, Config->GetModule("nickserv")->Get<time_t>("releasetimeout", "1m"));
- }
- }
-
- public:
- NickServCore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, PSEUDOCLIENT | VENDOR),
- NickServService(this), held(this, "HELD"), collided(this, "COLLIDED")
- {
- }
-
- ~NickServCore()
- {
- OnShutdown();
- }
-
- void OnShutdown() anope_override
- {
- /* On shutdown, restart, or mod unload, remove all of our holds for nicks (svshold or qlines)
- * because some IRCds do not allow us to have these automatically expire
- */
- for (nickalias_map::const_iterator it = NickAliasList->begin(); it != NickAliasList->end(); ++it)
- this->Release(it->second);
- }
-
- void OnRestart() anope_override
- {
- OnShutdown();
- }
-
- void Validate(User *u) anope_override
- {
- NickAlias *na = NickAlias::Find(u->nick);
- if (!na)
- return;
-
- EventReturn MOD_RESULT;
- FOREACH_RESULT(OnNickValidate, MOD_RESULT, (u, na));
- if (MOD_RESULT == EVENT_STOP)
- {
- this->Collide(u, na);
- return;
- }
- else if (MOD_RESULT == EVENT_ALLOW)
- return;
-
- if (!na->nc->HasExt("NS_SECURE") && u->IsRecognized())
- {
- na->last_seen = Anope::CurTime;
- na->last_usermask = u->GetIdent() + "@" + u->GetDisplayedHost();
- na->last_realname = u->realname;
- return;
- }
-
- if (Config->GetModule("nickserv")->Get<bool>("nonicknameownership"))
- return;
-
- bool on_access = u->IsRecognized(false);
-
- if (on_access || !na->nc->HasExt("KILL_IMMED"))
- {
- if (na->nc->HasExt("NS_SECURE"))
- u->SendMessage(NickServ, NICK_IS_SECURE, Config->StrictPrivmsg.c_str(), NickServ->nick.c_str());
- else
- u->SendMessage(NickServ, NICK_IS_REGISTERED, Config->StrictPrivmsg.c_str(), NickServ->nick.c_str());
- }
- if (na->nc->HasExt("KILLPROTECT") && !on_access)
- {
- if (na->nc->HasExt("KILL_IMMED"))
- {
- u->SendMessage(NickServ, FORCENICKCHANGE_NOW);
- this->Collide(u, na);
- }
- else if (na->nc->HasExt("KILL_QUICK"))
- {
- time_t killquick = Config->GetModule("nickserv")->Get<time_t>("killquick", "20s");
- u->SendMessage(NickServ, _("If you do not change within %s, I will change your nick."), Anope::Duration(killquick, u->Account()).c_str());
- new NickServCollide(this, this, u, na, killquick);
- }
- else
- {
- time_t kill = Config->GetModule("nickserv")->Get<time_t>("kill", "60s");
- u->SendMessage(NickServ, _("If you do not change within %s, I will change your nick."), Anope::Duration(kill, u->Account()).c_str());
- new NickServCollide(this, this, u, na, kill);
- }
- }
-
- }
-
- void OnUserLogin(User *u) anope_override
- {
- NickAlias *na = NickAlias::Find(u->nick);
- if (na && *na->nc == u->Account() && !Config->GetModule("nickserv")->Get<bool>("nonicknameownership") && !na->nc->HasExt("UNCONFIRMED"))
- u->SetMode(NickServ, "REGISTERED");
-
- const Anope::string &modesonid = Config->GetModule(this)->Get<Anope::string>("modesonid");
- if (!modesonid.empty())
- u->SetModes(NickServ, "%s", modesonid.c_str());
- }
-
- void Collide(User *u, NickAlias *na) anope_override
- {
- if (na)
- collided.Set(na);
-
- if (IRCD->CanSVSNick)
- {
- unsigned nicklen = Config->GetBlock("networkinfo")->Get<unsigned>("nicklen");
- const Anope::string &guestprefix = Config->GetModule("nickserv")->Get<const Anope::string>("guestnickprefix", "Guest");
-
- Anope::string guestnick;
-
- int i = 0;
- do
- {
- guestnick = guestprefix + stringify(static_cast<uint16_t>(rand()));
- if (guestnick.length() > nicklen)
- guestnick = guestnick.substr(0, nicklen);
- }
- while (User::Find(guestnick) && i++ < 10);
-
- if (i == 11)
- u->Kill(*NickServ, "Services nickname-enforcer kill");
- else
- {
- u->SendMessage(*NickServ, _("Your nickname is now being changed to \002%s\002"), guestnick.c_str());
- IRCD->SendForceNickChange(u, guestnick, Anope::CurTime);
- }
- }
- else
- u->Kill(*NickServ, "Services nickname-enforcer kill");
- }
-
- void Release(NickAlias *na) anope_override
- {
- if (held.HasExt(na))
- {
- if (IRCD->CanSVSHold)
- IRCD->SendSVSHoldDel(na->nick);
- else
- {
- User *u = User::Find(na->nick);
- if (u && u->server == Me)
- {
- u->Quit();
- }
- }
-
- held.Unset(na);
- }
- collided.Unset(na); /* clear pending collide */
- }
-
- void OnReload(Configuration::Conf *conf) anope_override
- {
- const Anope::string &nsnick = conf->GetModule(this)->Get<const Anope::string>("client");
-
- if (nsnick.empty())
- throw ConfigException(Module::name + ": <client> must be defined");
-
- BotInfo *bi = BotInfo::Find(nsnick, true);
- if (!bi)
- throw ConfigException(Module::name + ": no bot named " + nsnick);
-
- NickServ = bi;
-
- spacesepstream(conf->GetModule(this)->Get<const Anope::string>("defaults", "ns_secure memo_signon memo_receive")).GetTokens(defaults);
- if (defaults.empty())
- {
- defaults.push_back("NS_SECURE");
- defaults.push_back("MEMO_SIGNON");
- defaults.push_back("MEMO_RECEIVE");
- }
- else if (defaults[0].equals_ci("none"))
- defaults.clear();
- }
-
- void OnDelNick(NickAlias *na) anope_override
- {
- User *u = User::Find(na->nick);
- if (u && u->Account() == na->nc)
- {
- IRCD->SendLogout(u);
- u->RemoveMode(NickServ, "REGISTERED");
- u->Logout();
- }
- }
-
- void OnDelCore(NickCore *nc) anope_override
- {
- Log(NickServ, "nick") << "Deleting nickname group " << nc->display;
-
- /* Clean up this nick core from any users online */
- for (std::list<User *>::iterator it = nc->users.begin(); it != nc->users.end();)
- {
- User *user = *it++;
- IRCD->SendLogout(user);
- user->RemoveMode(NickServ, "REGISTERED");
- user->Logout();
- FOREACH_MOD(OnNickLogout, (user));
- }
- nc->users.clear();
- }
-
- void OnChangeCoreDisplay(NickCore *nc, const Anope::string &newdisplay) anope_override
- {
- Log(LOG_NORMAL, "nick", NickServ) << "Changing " << nc->display << " nickname group display to " << newdisplay;
- }
-
- void OnNickIdentify(User *u) anope_override
- {
- Configuration::Block *block = Config->GetModule(this);
-
- if (block->Get<bool>("modeonid", "yes"))
-
- for (User::ChanUserList::iterator it = u->chans.begin(), it_end = u->chans.end(); it != it_end; ++it)
- {
- ChanUserContainer *cc = it->second;
- Channel *c = cc->chan;
- if (c)
- c->SetCorrectModes(u, true);
- }
-
- const Anope::string &modesonid = block->Get<const Anope::string>("modesonid");
- if (!modesonid.empty())
- u->SetModes(NickServ, "%s", modesonid.c_str());
-
- if (block->Get<bool>("forceemail", "yes") && u->Account()->email.empty())
- {
- u->SendMessage(NickServ, _("You must now supply an e-mail for your nick.\n"
- "This e-mail will allow you to retrieve your password in\n"
- "case you forget it."));
- u->SendMessage(NickServ, _("Type \002%s%s SET EMAIL \037e-mail\037\002 in order to set your e-mail.\n"
- "Your privacy is respected; this e-mail won't be given to\n"
- "any third-party person."), Config->StrictPrivmsg.c_str(), NickServ->nick.c_str());
- }
-
- for (std::set<NickServCollide *>::iterator it = collides.begin(); it != collides.end(); ++it)
- {
- NickServCollide *c = *it;
- if (c->GetUser() == u && c->GetNick() && c->GetNick()->nc == u->Account())
- {
- delete c;
- break;
- }
- }
- }
-
- void OnNickGroup(User *u, NickAlias *target) anope_override
- {
- if (!target->nc->HasExt("UNCONFIRMED"))
- u->SetMode(NickServ, "REGISTERED");
- }
-
- void OnNickUpdate(User *u) anope_override
- {
- for (User::ChanUserList::iterator it = u->chans.begin(), it_end = u->chans.end(); it != it_end; ++it)
- {
- ChanUserContainer *cc = it->second;
- Channel *c = cc->chan;
- if (c)
- c->SetCorrectModes(u, true);
- }
- }
-
- void OnUserConnect(User *u, bool &exempt) anope_override
- {
- if (u->Quitting() || !u->server->IsSynced() || u->server->IsULined())
- return;
-
- const NickAlias *na = NickAlias::Find(u->nick);
-
- const Anope::string &unregistered_notice = Config->GetModule(this)->Get<const Anope::string>("unregistered_notice");
- if (!Config->GetModule("nickserv")->Get<bool>("nonicknameownership") && !unregistered_notice.empty() && !na && !u->Account())
- u->SendMessage(NickServ, unregistered_notice.replace_all_cs("%n", u->nick));
- else if (na && !u->IsIdentified(true))
- this->Validate(u);
- }
-
- void OnPostUserLogoff(User *u) anope_override
- {
- NickAlias *na = NickAlias::Find(u->nick);
- if (na)
- OnCancel(u, na);
- }
-
- void OnServerSync(Server *s) anope_override
- {
- for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it)
- {
- User *u = it->second;
-
- if (u->server == s)
- {
- if (u->HasMode("REGISTERED") && !u->IsIdentified(true))
- u->RemoveMode(NickServ, "REGISTERED");
- if (!u->IsIdentified())
- this->Validate(u);
- }
- }
- }
-
- void OnUserNickChange(User *u, const Anope::string &oldnick) anope_override
- {
- NickAlias *old_na = NickAlias::Find(oldnick), *na = NickAlias::Find(u->nick);
- /* If the new nick isn't registered or it's registered and not yours */
- if (!na || na->nc != u->Account())
- {
- /* Remove +r, but keep an account associated with the user */
- u->RemoveMode(NickServ, "REGISTERED");
-
- this->Validate(u);
- }
- else
- {
- /* Reset +r and re-send account (even though it really should be set at this point) */
- IRCD->SendLogin(u, na);
- if (!Config->GetModule("nickserv")->Get<bool>("nonicknameownership") && na->nc == u->Account() && !na->nc->HasExt("UNCONFIRMED"))
- u->SetMode(NickServ, "REGISTERED");
- Log(u, "", NickServ) << u->GetMask() << " automatically identified for group " << u->Account()->display;
- }
-
- if (!u->nick.equals_ci(oldnick) && old_na)
- OnCancel(u, old_na);
- }
-
- void OnUserModeSet(const MessageSource &setter, User *u, const Anope::string &mname) anope_override
- {
- if (u->server->IsSynced() && mname == "REGISTERED" && !u->IsIdentified(true))
- u->RemoveMode(NickServ, mname);
- }
-
- EventReturn OnPreHelp(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (!params.empty() || source.c || source.service != *NickServ)
- return EVENT_CONTINUE;
- if (!Config->GetModule("nickserv")->Get<bool>("nonicknameownership"))
- source.Reply(_("\002%s\002 allows you to register a nickname and\n"
- "prevent others from using it. The following\n"
- "commands allow for registration and maintenance of\n"
- "nicknames; to use them, type \002%s%s \037command\037\002.\n"
- "For more information on a specific command, type\n"
- "\002%s%s %s \037command\037\002.\n"), NickServ->nick.c_str(), Config->StrictPrivmsg.c_str(), NickServ->nick.c_str(), Config->StrictPrivmsg.c_str(), NickServ->nick.c_str(), source.command.c_str());
- else
- source.Reply(_("\002%s\002 allows you to register an account.\n"
- "The following commands allow for registration and maintenance of\n"
- "accounts; to use them, type \002%s%s \037command\037\002.\n"
- "For more information on a specific command, type\n"
- "\002%s%s %s \037command\037\002.\n"), NickServ->nick.c_str(), Config->StrictPrivmsg.c_str(), NickServ->nick.c_str(), Config->StrictPrivmsg.c_str(), NickServ->nick.c_str(), source.command.c_str());
- return EVENT_CONTINUE;
- }
-
- void OnPostHelp(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (!params.empty() || source.c || source.service != *NickServ)
- return;
- if (source.IsServicesOper())
- source.Reply(_(" \n"
- "Services Operators can also drop any nickname without needing\n"
- "to identify for the nick, and may view the access list for\n"
- "any nickname."));
- time_t nickserv_expire = Config->GetModule(this)->Get<time_t>("expire", "21d");
- if (nickserv_expire >= 86400)
- source.Reply(_(" \n"
- "Accounts that are not used anymore are subject to\n"
- "the automatic expiration, i.e. they will be deleted\n"
- "after %d days if not used."), nickserv_expire / 86400);
- source.Reply(_(" \n"
- "\002NOTICE:\002 This service is intended to provide a way for\n"
- "IRC users to ensure their identity is not compromised.\n"
- "It is \002NOT\002 intended to facilitate \"stealing\" of\n"
- "nicknames or other malicious actions. Abuse of %s\n"
- "will result in, at minimum, loss of the abused\n"
- "nickname(s)."), NickServ->nick.c_str());
- }
-
- void OnNickCoreCreate(NickCore *nc)
- {
- /* Set default flags */
- for (unsigned i = 0; i < defaults.size(); ++i)
- nc->Extend<bool>(defaults[i].upper());
- }
-
- void OnUserQuit(User *u, const Anope::string &msg)
- {
- if (u->server && !u->server->GetQuitReason().empty() && Config->GetModule(this)->Get<bool>("hidenetsplitquit"))
- return;
-
- /* Update last quit and last seen for the user */
- NickAlias *na = NickAlias::Find(u->nick);
- if (na && !na->nc->HasExt("NS_SUSPENDED") && (u->IsRecognized() || u->IsIdentified(true)))
- {
- na->last_seen = Anope::CurTime;
- na->last_quit = msg;
- }
- }
-
- void OnExpireTick() anope_override
- {
- if (Anope::NoExpire || Anope::ReadOnly)
- return;
-
- time_t nickserv_expire = Config->GetModule(this)->Get<time_t>("expire", "21d");
-
- for (nickalias_map::const_iterator it = NickAliasList->begin(), it_end = NickAliasList->end(); it != it_end; )
- {
- NickAlias *na = it->second;
- ++it;
-
- User *u = User::Find(na->nick, true);
- if (u && (u->IsIdentified(true) || u->IsRecognized()))
- na->last_seen = Anope::CurTime;
-
- bool expire = false;
-
- if (nickserv_expire && Anope::CurTime - na->last_seen >= nickserv_expire)
- expire = true;
-
- FOREACH_MOD(OnPreNickExpire, (na, expire));
-
- if (expire)
- {
- Log(LOG_NORMAL, "nickserv/expire", NickServ) << "Expiring nickname " << na->nick << " (group: " << na->nc->display << ") (e-mail: " << (na->nc->email.empty() ? "none" : na->nc->email) << ")";
- FOREACH_MOD(OnNickExpire, (na));
- delete na;
- }
- }
- }
-
- void OnNickInfo(CommandSource &source, NickAlias *na, InfoFormatter &info, bool show_hidden) anope_override
- {
- if (!na->nc->HasExt("UNCONFIRMED"))
- {
- time_t nickserv_expire = Config->GetModule(this)->Get<time_t>("expire", "21d");
- if (!na->HasExt("NS_NO_EXPIRE") && nickserv_expire && !Anope::NoExpire && (source.HasPriv("nickserv/auspex") || na->last_seen != Anope::CurTime))
- info[_("Expires")] = Anope::strftime(na->last_seen + nickserv_expire, source.GetAccount());
- }
- else
- {
- time_t unconfirmed_expire = Config->GetModule(this)->Get<time_t>("unconfirmedexpire", "1d");
- info[_("Expires")] = Anope::strftime(na->time_registered + unconfirmed_expire, source.GetAccount());
- }
- }
-};
-
-MODULE_INIT(NickServCore)
-
diff --git a/modules/pseudoclients/operserv.cpp b/modules/pseudoclients/operserv.cpp
deleted file mode 100644
index f443aed95..000000000
--- a/modules/pseudoclients/operserv.cpp
+++ /dev/null
@@ -1,299 +0,0 @@
-/* OperServ core functions
- *
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
- *
- * Please read COPYING and README for further details.
- *
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
- */
-
-#include "module.h"
-
-class SGLineManager : public XLineManager
-{
- public:
- SGLineManager(Module *creator) : XLineManager(creator, "xlinemanager/sgline", 'G') { }
-
- void OnMatch(User *u, XLine *x) anope_override
- {
- this->Send(u, x);
- }
-
- void OnExpire(const XLine *x) anope_override
- {
- Log(Config->GetClient("OperServ"), "expire/akill") << "AKILL on \002" << x->mask << "\002 has expired";
- }
-
- void Send(User *u, XLine *x) anope_override
- {
- IRCD->SendAkill(u, x);
- }
-
- void SendDel(XLine *x) anope_override
- {
- IRCD->SendAkillDel(x);
- }
-
- bool Check(User *u, const XLine *x) anope_override
- {
- if (x->regex)
- {
- Anope::string uh = u->GetIdent() + "@" + u->host, nuhr = u->nick + "!" + uh + "#" + u->realname;
- return x->regex->Matches(uh) || x->regex->Matches(nuhr);
- }
-
- if (!x->GetNick().empty() && !Anope::Match(u->nick, x->GetNick()))
- return false;
-
- if (!x->GetUser().empty() && !Anope::Match(u->GetIdent(), x->GetUser()))
- return false;
-
- if (!x->GetReal().empty() && !Anope::Match(u->realname, x->GetReal()))
- return false;
-
- if (x->c && x->c->match(u->ip))
- return true;
-
- if (x->GetHost().empty() || Anope::Match(u->host, x->GetHost()) || Anope::Match(u->ip.addr(), x->GetHost()))
- return true;
-
- return false;
- }
-};
-
-class SQLineManager : public XLineManager
-{
- ServiceReference<NickServService> nickserv;
-
- public:
- SQLineManager(Module *creator) : XLineManager(creator, "xlinemanager/sqline", 'Q'), nickserv("NickServService", "NickServ") { }
-
- void OnMatch(User *u, XLine *x) anope_override
- {
- this->Send(u, x);
- }
-
- void OnExpire(const XLine *x) anope_override
- {
- Log(Config->GetClient("OperServ"), "expire/sqline") << "SQLINE on \002" << x->mask << "\002 has expired";
- }
-
- void Send(User *u, XLine *x) anope_override
- {
- if (!IRCD->CanSQLine)
- {
- if (!u)
- ;
- else if (nickserv)
- nickserv->Collide(u, NULL);
- else
- u->Kill(Config->GetClient("OperServ"), "Q-Lined: " + x->reason);
- }
- else if (x->IsRegex())
- {
- if (u)
- u->Kill(Config->GetClient("OperServ"), "Q-Lined: " + x->reason);
- }
- else if (x->mask[0] != '#' || IRCD->CanSQLineChannel)
- {
- IRCD->SendSQLine(u, x);
- /* If it is an oper, assume they're walking it, otherwise kill for good measure */
- if (u && !u->HasMode("OPER"))
- u->Kill(Config->GetClient("OperServ"), "Q-Lined: " + x->reason);
- }
- }
-
- void SendDel(XLine *x) anope_override
- {
- if (!IRCD->CanSQLine || x->IsRegex())
- ;
- else if (x->mask[0] != '#' || IRCD->CanSQLineChannel)
- IRCD->SendSQLineDel(x);
- }
-
- bool Check(User *u, const XLine *x) anope_override
- {
- if (x->regex)
- return x->regex->Matches(u->nick);
- return Anope::Match(u->nick, x->mask);
- }
-
- XLine *CheckChannel(Channel *c)
- {
- for (std::vector<XLine *>::const_iterator it = this->GetList().begin(), it_end = this->GetList().end(); it != it_end; ++it)
- {
- XLine *x = *it;
-
- if (x->regex)
- {
- if (x->regex->Matches(c->name))
- return x;
- }
- else
- {
- if (x->mask.empty() || x->mask[0] != '#')
- continue;
-
- if (Anope::Match(c->name, x->mask, false, true))
- return x;
- }
- }
- return NULL;
- }
-};
-
-class SNLineManager : public XLineManager
-{
- public:
- SNLineManager(Module *creator) : XLineManager(creator, "xlinemanager/snline", 'N') { }
-
- void OnMatch(User *u, XLine *x) anope_override
- {
- this->Send(u, x);
- }
-
- void OnExpire(const XLine *x) anope_override
- {
- Log(Config->GetClient("OperServ"), "expire/snline") << "SNLINE on \002" << x->mask << "\002 has expired";
- }
-
- void Send(User *u, XLine *x) anope_override
- {
- if (IRCD->CanSNLine && !x->IsRegex())
- IRCD->SendSGLine(u, x);
-
- if (u)
- u->Kill(Config->GetClient("OperServ"), "SNLined: " + x->reason);
- }
-
- void SendDel(XLine *x) anope_override
- {
- if (IRCD->CanSNLine && !x->IsRegex())
- IRCD->SendSGLineDel(x);
- }
-
- bool Check(User *u, const XLine *x) anope_override
- {
- if (x->regex)
- return x->regex->Matches(u->realname);
- return Anope::Match(u->realname, x->mask, false, true);
- }
-};
-
-class OperServCore : public Module
-{
- Reference<BotInfo> OperServ;
- SGLineManager sglines;
- SQLineManager sqlines;
- SNLineManager snlines;
-
- public:
- OperServCore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, PSEUDOCLIENT | VENDOR),
- sglines(this), sqlines(this), snlines(this)
- {
-
- /* Yes, these are in this order for a reason. Most violent->least violent. */
- XLineManager::RegisterXLineManager(&sglines);
- XLineManager::RegisterXLineManager(&sqlines);
- XLineManager::RegisterXLineManager(&snlines);
- }
-
- ~OperServCore()
- {
- this->sglines.Clear();
- this->sqlines.Clear();
- this->snlines.Clear();
-
- XLineManager::UnregisterXLineManager(&sglines);
- XLineManager::UnregisterXLineManager(&sqlines);
- XLineManager::UnregisterXLineManager(&snlines);
- }
-
- void OnReload(Configuration::Conf *conf) anope_override
- {
- const Anope::string &osnick = conf->GetModule(this)->Get<const Anope::string>("client");
-
- if (osnick.empty())
- throw ConfigException(this->name + ": <client> must be defined");
-
- BotInfo *bi = BotInfo::Find(osnick, true);
- if (!bi)
- throw ConfigException(this->name + ": no bot named " + osnick);
-
- OperServ = bi;
- }
-
- EventReturn OnBotPrivmsg(User *u, BotInfo *bi, Anope::string &message) anope_override
- {
- if (bi == OperServ && !u->HasMode("OPER") && Config->GetModule(this)->Get<bool>("opersonly"))
- {
- u->SendMessage(bi, ACCESS_DENIED);
- Log(bi, "bados") << "Denied access to " << bi->nick << " from " << u->GetMask() << " (non-oper)";
- return EVENT_STOP;
- }
-
- return EVENT_CONTINUE;
- }
-
- void OnServerQuit(Server *server) anope_override
- {
- if (server->IsJuped())
- Log(server, "squit", OperServ) << "Received SQUIT for juped server " << server->GetName();
- }
-
- void OnUserModeSet(const MessageSource &setter, User *u, const Anope::string &mname) anope_override
- {
- if (mname == "OPER")
- Log(u, "oper", OperServ) << "is now an IRC operator.";
- }
-
- void OnUserModeUnset(const MessageSource &setter, User *u, const Anope::string &mname) anope_override
- {
- if (mname == "OPER")
- Log(u, "oper", OperServ) << "is no longer an IRC operator";
- }
-
- void OnUserConnect(User *u, bool &exempt) anope_override
- {
- if (!u->Quitting() && !exempt)
- XLineManager::CheckAll(u);
- }
-
- void OnUserNickChange(User *u, const Anope::string &oldnick) anope_override
- {
- if (!u->HasMode("OPER"))
- this->sqlines.CheckAllXLines(u);
- }
-
- EventReturn OnCheckKick(User *u, Channel *c, Anope::string &mask, Anope::string &reason) anope_override
- {
- XLine *x = this->sqlines.CheckChannel(c);
- if (x)
- {
- this->sqlines.OnMatch(u, x);
- reason = x->reason;
- return EVENT_STOP;
- }
-
- return EVENT_CONTINUE;
- }
-
- EventReturn OnPreHelp(CommandSource &source, const std::vector<Anope::string> &params) anope_override
- {
- if (!params.empty() || source.c || source.service != *OperServ)
- return EVENT_CONTINUE;
- source.Reply(_("%s commands:"), OperServ->nick.c_str());
- return EVENT_CONTINUE;
- }
-
- void OnLog(Log *l) anope_override
- {
- if (l->type == LOG_SERVER)
- l->bi = OperServ;
- }
-};
-
-MODULE_INIT(OperServCore)
-
diff --git a/modules/m_redis.cpp b/modules/redis.cpp
index d8398c1b5..7a519a4e7 100644
--- a/modules/m_redis.cpp
+++ b/modules/redis.cpp
@@ -1,9 +1,20 @@
/*
+ * Anope IRC Services
*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2013-2016 Anope Team <team@anope.org>
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "module.h"
@@ -25,10 +36,10 @@ class RedisSocket : public BinarySocket, public ConnectionSocket
~RedisSocket();
- void OnConnect() anope_override;
- void OnError(const Anope::string &error) anope_override;
+ void OnConnect() override;
+ void OnError(const Anope::string &error) override;
- bool Read(const char *buffer, size_t l) anope_override;
+ bool Read(const char *buffer, size_t l) override;
};
class Transaction : public Interface
@@ -51,7 +62,7 @@ class Transaction : public Interface
}
}
- void OnResult(const Reply &r) anope_override
+ void OnResult(const Reply &r) override
{
/* This is a multi bulk reply of the results of the queued commands
* in this transaction
@@ -165,13 +176,6 @@ class MyRedisService : public Provider
this->Send(s, i, args);
}
- void SendCommand(RedisSocket *s, Interface *i, const Anope::string &str)
- {
- std::vector<Anope::string> args;
- spacesepstream(str).GetTokens(args);
- this->SendCommand(s, i, args);
- }
-
void Send(Interface *i, const std::vector<std::pair<const char *, size_t> > &args)
{
if (!sock)
@@ -183,7 +187,7 @@ class MyRedisService : public Provider
this->Send(sock, i, args);
}
- void SendCommand(Interface *i, const std::vector<Anope::string> &cmds) anope_override
+ void SendCommand(Interface *i, const std::vector<Anope::string> &cmds) override
{
std::vector<std::pair<const char *, size_t> > args;
for (unsigned j = 0; j < cmds.size(); ++j)
@@ -191,15 +195,14 @@ class MyRedisService : public Provider
this->Send(i, args);
}
- void SendCommand(Interface *i, const Anope::string &str) anope_override
+ void SendCommand(Interface *i, const Anope::string &str) override
{
std::vector<Anope::string> args;
spacesepstream(str).GetTokens(args);
this->SendCommand(i, args);
}
- public:
- bool BlockAndProcess() anope_override
+ bool BlockAndProcess() override
{
this->sock->ProcessWrite();
this->sock->SetBlocking(true);
@@ -208,7 +211,7 @@ class MyRedisService : public Provider
return !this->sock->interfaces.empty();
}
- void Subscribe(Interface *i, const Anope::string &pattern) anope_override
+ void Subscribe(Interface *i, const Anope::string &ch) override
{
if (sub == NULL)
{
@@ -216,21 +219,19 @@ class MyRedisService : public Provider
sub->Connect(host, port);
}
- std::vector<Anope::string> args;
- args.push_back("PSUBSCRIBE");
- args.push_back(pattern);
+ std::vector<Anope::string> args = { "SUBSCRIBE", ch };
this->SendCommand(sub, NULL, args);
- sub->subinterfaces[pattern] = i;
+ sub->subinterfaces[ch] = i;
}
- void Unsubscribe(const Anope::string &pattern) anope_override
+ void Unsubscribe(const Anope::string &pattern) override
{
if (sub)
sub->subinterfaces.erase(pattern);
}
- void StartTransaction() anope_override
+ void StartTransaction() override
{
if (in_transaction)
throw CoreException();
@@ -239,7 +240,7 @@ class MyRedisService : public Provider
in_transaction = true;
}
- void CommitTransaction() anope_override
+ void CommitTransaction() override
{
/* The result of the transaction comes back to the reply of EXEC as a multi bulk.
* The reply to the individual commands that make up the transaction when executed
@@ -273,20 +274,15 @@ RedisSocket::~RedisSocket()
void RedisSocket::OnConnect()
{
- Log() << "redis: Successfully connected to " << provider->name << (this == this->provider->sub ? " (sub)" : "");
+ Log() << "redis: Successfully connected to " << provider->GetName() << (this == this->provider->sub ? " (sub)" : "");
this->provider->SendCommand(NULL, "CLIENT SETNAME Anope");
this->provider->SendCommand(NULL, "SELECT " + stringify(provider->db));
-
- if (this != this->provider->sub)
- {
- this->provider->SendCommand(this, NULL, "CONFIG SET notify-keyspace-events KA");
- }
}
void RedisSocket::OnError(const Anope::string &error)
{
- Log() << "redis: Error on " << provider->name << (this == this->provider->sub ? " (sub)" : "") << ": " << error;
+ Log() << "redis: Error on " << provider->GetName() << (this == this->provider->sub ? " (sub)" : "") << ": " << error;
}
size_t RedisSocket::ParseReply(Reply &r, const char *buffer, size_t l)
@@ -467,17 +463,9 @@ bool RedisSocket::Read(const char *buffer, size_t l)
if (this == provider->sub)
{
- if (r.multi_bulk.size() == 4)
- {
- /* pmessage
- * pattern subscribed to
- * __keyevent@0__:set
- * key
- */
- std::map<Anope::string, Interface *>::iterator it = this->subinterfaces.find(r.multi_bulk[1]->bulk);
- if (it != this->subinterfaces.end())
- it->second->OnResult(r);
- }
+ std::map<Anope::string, Interface *>::iterator it = this->subinterfaces.find(r.multi_bulk[1]->bulk);
+ if (it != this->subinterfaces.end())
+ it->second->OnResult(r);
}
else
{
@@ -519,11 +507,13 @@ bool RedisSocket::Read(const char *buffer, size_t l)
class ModuleRedis : public Module
+ , public EventHook<Event::ModuleUnload>
{
std::map<Anope::string, MyRedisService *> services;
public:
ModuleRedis(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, EXTRA | VENDOR)
+ , EventHook<Event::ModuleUnload>(this)
{
}
@@ -542,7 +532,7 @@ class ModuleRedis : public Module
}
}
- void OnReload(Configuration::Conf *conf) anope_override
+ void OnReload(Configuration::Conf *conf) override
{
Configuration::Block *block = conf->GetModule(this);
std::vector<Anope::string> new_services;
@@ -551,8 +541,8 @@ class ModuleRedis : public Module
{
Configuration::Block *redis = block->GetBlock("redis", i);
- const Anope::string &n = redis->Get<const Anope::string>("name"),
- &ip = redis->Get<const Anope::string>("ip");
+ const Anope::string &n = redis->Get<Anope::string>("name"),
+ &ip = redis->Get<Anope::string>("ip");
int port = redis->Get<int>("port");
unsigned db = redis->Get<unsigned>("db");
@@ -566,12 +556,12 @@ class ModuleRedis : public Module
Provider *p = it->second;
++it;
- if (std::find(new_services.begin(), new_services.end(), p->name) == new_services.end())
+ if (std::find(new_services.begin(), new_services.end(), p->GetName()) == new_services.end())
delete it->second;
}
}
- void OnModuleUnload(User *, Module *m) anope_override
+ void OnModuleUnload(User *, Module *m) override
{
for (std::map<Anope::string, MyRedisService *>::iterator it = services.begin(); it != services.end(); ++it)
{
diff --git a/modules/m_rewrite.cpp b/modules/rewrite.cpp
index e4ce46c3a..c4c5575e8 100644
--- a/modules/m_rewrite.cpp
+++ b/modules/rewrite.cpp
@@ -1,9 +1,20 @@
/*
+ * Anope IRC Services
*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2011-2016 Anope Team <team@anope.org>
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "module.h"
@@ -20,7 +31,7 @@ struct Rewrite
for (unsigned i = 0; i < sm.size(); ++i)
if (i >= message.size() || (sm[i] != "$" && !sm[i].equals_ci(message[i])))
return false;
-
+
return true;
}
@@ -71,7 +82,7 @@ struct Rewrite
}
static std::vector<Rewrite> rewrites;
-
+
static Rewrite *Find(const Anope::string &client, const Anope::string &cmd)
{
for (unsigned i = 0; i < rewrites.size(); ++i)
@@ -106,7 +117,7 @@ class RewriteCommand : public Command
public:
RewriteCommand(Module *creator) : Command(creator, "rewrite", 0, 0) { }
- void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
+ void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
{
std::vector<Anope::string> full_params = params;
full_params.insert(full_params.begin(), source.command);
@@ -116,7 +127,7 @@ class RewriteCommand : public Command
{
Anope::string new_message = r->Process(source, full_params);
Log(LOG_DEBUG) << "m_rewrite: Rewrote '" << source.command << (!params.empty() ? " " + params[0] : "") << "' to '" << new_message << "' using '" << r->source_message << "'";
- source.service = BotInfo::Find(r->client, true);
+ source.service = ServiceBot::Find(r->client, true);
if (!source.service)
return;
Command::Run(source, new_message);
@@ -125,7 +136,7 @@ class RewriteCommand : public Command
Log() << "m_rewrite: Unable to rewrite '" << source.command << (!params.empty() ? " " + params[0] : "") << "'";
}
- void OnServHelp(CommandSource &source) anope_override
+ void OnServHelp(CommandSource &source) override
{
Rewrite *r = Rewrite::Find(!source.c ? source.service->nick : "", source.command);
if (r != NULL && !r->desc.empty())
@@ -135,7 +146,7 @@ class RewriteCommand : public Command
}
}
- bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
+ bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
{
Rewrite *r = Rewrite::Find(!source.c ? source.service->nick : "", source.command);
if (r != NULL && !r->desc.empty())
@@ -154,11 +165,12 @@ class ModuleRewrite : public Module
RewriteCommand cmdrewrite;
public:
- ModuleRewrite(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR | EXTRA), cmdrewrite(this)
+ ModuleRewrite(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR | EXTRA)
+ , cmdrewrite(this)
{
}
- void OnReload(Configuration::Conf *conf) anope_override
+ void OnReload(Configuration::Conf *conf) override
{
Rewrite::rewrites.clear();
@@ -171,10 +183,10 @@ class ModuleRewrite : public Module
Rewrite rw;
- rw.client = block->Get<const Anope::string>("service");
- rw.source_message = block->Get<const Anope::string>("rewrite_source");
- rw.target_message = block->Get<const Anope::string>("rewrite_target");
- rw.desc = block->Get<const Anope::string>("rewrite_description");
+ rw.client = block->Get<Anope::string>("service");
+ rw.source_message = block->Get<Anope::string>("rewrite_source");
+ rw.target_message = block->Get<Anope::string>("rewrite_target");
+ rw.desc = block->Get<Anope::string>("rewrite_description");
if (rw.client.empty() || rw.source_message.empty() || rw.target_message.empty())
continue;
diff --git a/modules/sasl.cpp b/modules/sasl.cpp
new file mode 100644
index 000000000..f07026b9e
--- /dev/null
+++ b/modules/sasl.cpp
@@ -0,0 +1,391 @@
+/*
+ * Anope IRC Services
+ *
+ * Copyright (C) 2014-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
+ */
+
+#include "module.h"
+#include "modules/sasl.h"
+#include "modules/nickserv/cert.h"
+
+using namespace SASL;
+
+class Plain : public Mechanism
+{
+ public:
+ Plain(SASL::Service *s, Module *o) : Mechanism(s, o, "PLAIN") { }
+
+ void ProcessMessage(Session *sess, const SASL::Message &m) override
+ {
+ if (m.type == "S")
+ {
+ GetService()->SendMessage(sess, "C", "+");
+ }
+ else if (m.type == "C")
+ {
+ Anope::string decoded;
+ Anope::B64Decode(m.data, decoded);
+
+ size_t p = decoded.find('\0');
+ if (p == Anope::string::npos)
+ {
+ GetService()->Fail(sess);
+ delete sess;
+ return;
+ }
+ decoded = decoded.substr(p + 1);
+
+ p = decoded.find('\0');
+ if (p == Anope::string::npos)
+ {
+ GetService()->Fail(sess);
+ delete sess;
+ return;
+ }
+
+ Anope::string acc = decoded.substr(0, p),
+ pass = decoded.substr(p + 1);
+
+ if (!NickServ::service || acc.empty() || pass.empty() || !IRCD->IsNickValid(acc) || pass.find_first_of("\r\n") != Anope::string::npos)
+ {
+ GetService()->Fail(sess);
+ delete sess;
+ return;
+ }
+
+ NickServ::IdentifyRequest *req = NickServ::service->CreateIdentifyRequest(new IdentifyRequestListener(GetService(), m.source), this->GetOwner(), acc, pass);
+ EventManager::Get()->Dispatch(&Event::CheckAuthentication::OnCheckAuthentication, nullptr, req);
+ req->Dispatch();
+ }
+ }
+};
+
+class External : public Mechanism
+{
+ ServiceReference<CertService> certs;
+
+ struct Session : SASL::Session
+ {
+ Anope::string cert;
+
+ Session(SASL::Service *s, Mechanism *m, const Anope::string &u) : SASL::Session(s, m, u) { }
+ };
+
+ public:
+ External(SASL::Service *s, Module *o) : Mechanism(s, o, "EXTERNAL")
+ , certs("certs")
+ {
+ if (!IRCD || !IRCD->CanCertFP)
+ throw ModuleException("No CertFP");
+ }
+
+ Session* CreateSession(const Anope::string &uid) override
+ {
+ return new Session(this->GetService(), this, uid);
+ }
+
+ void ProcessMessage(SASL::Session *sess, const SASL::Message &m) override
+ {
+ Session *mysess = anope_dynamic_static_cast<Session *>(sess);
+
+ if (m.type == "S")
+ {
+ mysess->cert = m.ext;
+
+ GetService()->SendMessage(sess, "C", "+");
+ }
+ else if (m.type == "C")
+ {
+ if (!certs || mysess->cert.empty())
+ {
+ GetService()->Fail(sess);
+ delete sess;
+ return;
+ }
+
+ NickServ::Account *nc = certs->FindAccountFromCert(mysess->cert);
+ if (!nc || nc->HasFieldS("NS_SUSPENDED"))
+ {
+ Log(Config->GetClient("NickServ"), "sasl") << "A user failed to identify using certificate " << mysess->cert << " using SASL EXTERNAL";
+ GetService()->Fail(sess);
+ delete sess;
+ return;
+ }
+
+ Log(Config->GetClient("NickServ"), "sasl") << "A user identified to account " << nc->GetDisplay() << " using SASL EXTERNAL";
+ GetService()->Succeed(sess, nc);
+ delete sess;
+ }
+ }
+};
+
+class SASLService : public SASL::Service, public Timer
+{
+ std::map<Anope::string, SASL::Session *> sessions;
+
+ public:
+ SASLService(Module *o) : SASL::Service(o), Timer(o, 60, Anope::CurTime, true) { }
+
+ ~SASLService()
+ {
+ for (std::map<Anope::string, Session *>::iterator it = sessions.begin(); it != sessions.end(); it++)
+ delete it->second;
+ }
+
+ void ProcessMessage(const SASL::Message &m) override
+ {
+ if (m.target != "*")
+ {
+ Server *s = Server::Find(m.target);
+ if (s != Me)
+ {
+ User *u = User::Find(m.target);
+ if (!u || u->server != Me)
+ return;
+ }
+ }
+
+ Session* &session = sessions[m.source];
+
+ if (m.type == "S")
+ {
+ ServiceReference<Mechanism> mech(m.data);
+ if (!mech)
+ {
+ Session tmp(this, NULL, m.source);
+
+ this->SendMechs(&tmp);
+ this->Fail(&tmp);
+ return;
+ }
+
+ if (!session)
+ session = mech->CreateSession(m.source);
+ }
+ else if (m.type == "D")
+ {
+ delete session;
+ sessions.erase(m.source);
+ return;
+ }
+
+ if (session && session->mech)
+ session->mech->ProcessMessage(session, m);
+ }
+
+ Anope::string GetAgent() override
+ {
+ Anope::string agent = Config->GetModule(Service::GetOwner())->Get<Anope::string>("agent", "NickServ");
+ ServiceBot *bi = Config->GetClient(agent);
+ if (bi)
+ agent = bi->GetUID();
+ return agent;
+ }
+
+ Session* GetSession(const Anope::string &uid) override
+ {
+ std::map<Anope::string, Session *>::iterator it = sessions.find(uid);
+ if (it != sessions.end())
+ return it->second;
+ return NULL;
+ }
+
+ void RemoveSession(Session *sess) override
+ {
+ sessions.erase(sess->uid);
+ }
+
+ void DeleteSessions(Mechanism *mech, bool da) override
+ {
+ for (std::map<Anope::string, Session *>::iterator it = sessions.begin(); it != sessions.end();)
+ {
+ std::map<Anope::string, Session *>::iterator del = it++;
+ if (*del->second->mech == mech)
+ {
+ if (da)
+ this->SendMessage(del->second, "D", "A");
+ delete del->second;
+ }
+ }
+ }
+
+ void SendMessage(Session *session, const Anope::string &mtype, const Anope::string &data) override
+ {
+ SASL::Message msg;
+ msg.source = this->GetAgent();
+ msg.target = session->uid;
+ msg.type = mtype;
+ msg.data = data;
+
+ IRCD->SendSASLMessage(msg);
+ }
+
+ void Succeed(Session *session, NickServ::Account *nc) override
+ {
+ // If the user is already introduced then we log them in now.
+ // Otherwise, we send an SVSLOGIN to log them in later.
+ User *user = User::Find(session->uid);
+ NickServ::Nick *na = NickServ::FindNick(nc->GetDisplay());
+ if (user)
+ {
+ user->Identify(na);
+ }
+ else
+ {
+ IRCD->SendSVSLogin(session->uid, nc->GetDisplay(), na->GetVhostIdent(), na->GetVhostHost());
+ }
+ this->SendMessage(session, "D", "S");
+ }
+
+ void Fail(Session *session) override
+ {
+ this->SendMessage(session, "D", "F");
+ }
+
+ void SendMechs(Session *session) override
+ {
+ std::vector<Mechanism *> mechs = ServiceManager::Get()->FindServices<Mechanism *>();
+
+ Anope::string buf;
+ for (unsigned j = 0; j < mechs.size(); ++j)
+ buf += "," + mechs[j]->GetName();
+
+ this->SendMessage(session, "M", buf.empty() ? "" : buf.substr(1));
+ }
+
+ void Tick(time_t) override
+ {
+ for (std::map<Anope::string, Session *>::iterator it = sessions.begin(); it != sessions.end();)
+ {
+ Anope::string key = it->first;
+ Session *s = it->second;
+ ++it;
+
+ if (!s || !s->mech || s->created + 60 < Anope::CurTime)
+ {
+ delete s;
+ sessions.erase(key);
+ }
+ }
+ }
+};
+
+void IdentifyRequestListener::OnSuccess(NickServ::IdentifyRequest *req)
+{
+ NickServ::Nick *na = NickServ::FindNick(req->GetAccount());
+ if (!na || na->GetAccount()->HasFieldS("NS_SUSPENDED"))
+ return OnFail(req);
+
+ unsigned int maxlogins = Config->GetModule("ns_identify")->Get<unsigned int>("maxlogins");
+ if (maxlogins && na->GetAccount()->users.size() >= maxlogins)
+ return OnFail(req);
+
+ Session *s = service->GetSession(uid);
+ if (s)
+ {
+ Log(Config->GetClient("NickServ"), "sasl") << "A user identified to account " << req->GetAccount() << " using SASL";
+ service->Succeed(s, na->GetAccount());
+ delete s;
+ }
+}
+
+void IdentifyRequestListener::OnFail(NickServ::IdentifyRequest *req)
+{
+ Session *s = service->GetSession(uid);
+ if (s)
+ {
+ service->Fail(s);
+ delete s;
+ }
+
+ Anope::string accountstatus;
+ NickServ::Nick *na = NickServ::FindNick(req->GetAccount());
+ if (!na)
+ accountstatus = "nonexistent ";
+ else if (na->GetAccount()->HasFieldS("NS_SUSPENDED"))
+ accountstatus = "suspended ";
+
+ Log(Config->GetClient("NickServ"), "sasl") << "A user failed to identify for " << accountstatus << "account " << req->GetAccount() << " using SASL";
+}
+
+class ModuleSASL : public Module
+ , public EventHook<Event::ModuleLoad>
+ , public EventHook<Event::ModuleUnload>
+ , public EventHook<Event::PreUplinkSync>
+{
+ SASLService sasl;
+
+ Plain plain;
+ External *external = nullptr;
+
+ std::vector<Anope::string> mechs;
+
+ void CheckMechs()
+ {
+ std::vector<Anope::string> names;
+ for (Mechanism *mech : ServiceManager::Get()->FindServices<Mechanism *>())
+ names.push_back(mech->GetName());
+
+ if (mechs == names)
+ return;
+
+ mechs = names;
+
+ // If we are connected to the network then broadcast the mechlist.
+ if (Me && Me->IsSynced())
+ IRCD->SendSASLMechanisms(mechs);
+ }
+
+ public:
+ ModuleSASL(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
+ , EventHook<Event::ModuleLoad>(this)
+ , EventHook<Event::ModuleUnload>(this)
+ , EventHook<Event::PreUplinkSync>(this)
+ , sasl(this)
+ , plain(&sasl, this)
+ {
+ try
+ {
+ external = new External(&sasl, this);
+ }
+ catch (ModuleException &) { }
+
+ CheckMechs();
+ }
+
+ ~ModuleSASL()
+ {
+ delete external;
+ }
+
+ void OnModuleLoad(User *, Module *) override
+ {
+ CheckMechs();
+ }
+
+ void OnModuleUnload(User *, Module *) override
+ {
+ CheckMechs();
+ }
+
+ void OnPreUplinkSync(Server *) override
+ {
+ // We have not yet sent a mechanism list so always do it here.
+ IRCD->SendSASLMechanisms(mechs);
+ }
+};
+
+MODULE_INIT(ModuleSASL)
diff --git a/modules/third/language/CMakeLists.txt b/modules/third/language/CMakeLists.txt
index 85278f5be..6a7891475 100644
--- a/modules/third/language/CMakeLists.txt
+++ b/modules/third/language/CMakeLists.txt
@@ -2,7 +2,7 @@
if(GETTEXT_FOUND)
# Get all of the .po files
file(GLOB LANG_SRCS_PO RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.*.po")
- sort_list(LANG_SRCS_PO)
+ list(SORT LANG_SRCS_PO)
foreach(LANG_PO ${LANG_SRCS_PO})
# Get the domain for this language file
@@ -26,13 +26,13 @@ if(GETTEXT_FOUND)
# Add to cpack ignored files if not on Windows.
if(NOT WIN32)
add_to_cpack_ignored_files("${LANG_MO}")
- endif(NOT WIN32)
+ endif()
# Install the new language file
- install(CODE "FILE(MAKE_DIRECTORY ${LOCALE_DIR}/${LANG_LANG}/LC_MESSAGES/)")
+ install(CODE "file(MAKE_DIRECTORY ${LOCALE_DIR}/${LANG_LANG}/LC_MESSAGES/)")
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${LANG_MO} DESTINATION ${LOCALE_DIR}/${LANG_LANG}/LC_MESSAGES RENAME ${LANG_DOMAIN}.mo PERMISSIONS ${PERMS})
- endforeach(LANG_PO)
+ endforeach()
# Generate languages, depends on the mo files
add_custom_target(module_language DEPENDS ${LANG_SRCS_MO})
-endif(GETTEXT_FOUND)
+endif()
diff --git a/modules/webcpanel/pages/chanserv/access.cpp b/modules/webcpanel/pages/chanserv/access.cpp
index 93f8b082a..c98ae45c8 100644
--- a/modules/webcpanel/pages/chanserv/access.cpp
+++ b/modules/webcpanel/pages/chanserv/access.cpp
@@ -1,8 +1,20 @@
/*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Anope IRC Services
*
- * Please read COPYING and README for further details.
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "../../webcpanel.h"
@@ -12,7 +24,7 @@ WebCPanel::ChanServ::Access::Access(const Anope::string &cat, const Anope::strin
{
}
-bool WebCPanel::ChanServ::Access::OnRequest(HTTPProvider *server, const Anope::string &page_name, HTTPClient *client, HTTPMessage &message, HTTPReply &reply, NickAlias *na, TemplateFileServer::Replacements &replacements)
+bool WebCPanel::ChanServ::Access::OnRequest(HTTPProvider *server, const Anope::string &page_name, HTTPClient *client, HTTPMessage &message, HTTPReply &reply, ::NickServ::Nick *na, TemplateFileServer::Replacements &replacements)
{
TemplateFileServer Page("chanserv/access.html");
const Anope::string &chname = message.get_data["channel"];
@@ -25,7 +37,7 @@ bool WebCPanel::ChanServ::Access::OnRequest(HTTPProvider *server, const Anope::s
return true;
}
- ChannelInfo *ci = ChannelInfo::Find(chname);
+ ::ChanServ::Channel *ci = ::ChanServ::Find(chname);
if (!ci)
{
@@ -34,8 +46,8 @@ bool WebCPanel::ChanServ::Access::OnRequest(HTTPProvider *server, const Anope::s
return true;
}
- AccessGroup u_access = ci->AccessFor(na->nc);
- bool has_priv = na->nc->IsServicesOper() && na->nc->o->ot->HasPriv("chanserv/access/modify");
+ ::ChanServ::AccessGroup u_access = ci->AccessFor(na->GetAccount());
+ bool has_priv = na->GetAccount()->IsServicesOper() && na->GetAccount()->o->GetType()->HasPriv("chanserv/access/modify");
if (!u_access.HasPriv("ACCESS_LIST") && !has_priv)
{
@@ -51,11 +63,11 @@ bool WebCPanel::ChanServ::Access::OnRequest(HTTPProvider *server, const Anope::s
if (message.get_data["del"].empty() == false && message.get_data["mask"].empty() == false)
{
std::vector<Anope::string> params;
- params.push_back(ci->name);
+ params.push_back(ci->GetName());
params.push_back("DEL");
params.push_back(message.get_data["mask"]);
- WebPanel::RunCommand(na->nc->display, na->nc, "ChanServ", "chanserv/access", params, replacements);
+ WebPanel::RunCommand(na->GetAccount()->GetDisplay(), na->GetAccount(), "ChanServ", "chanserv/access", params, replacements);
}
else if (message.post_data["mask"].empty() == false && message.post_data["access"].empty() == false && message.post_data["provider"].empty() == false)
{
@@ -64,55 +76,55 @@ bool WebCPanel::ChanServ::Access::OnRequest(HTTPProvider *server, const Anope::s
if (provider == "chanserv/access")
{
std::vector<Anope::string> params;
- params.push_back(ci->name);
+ params.push_back(ci->GetName());
params.push_back("ADD");
params.push_back(message.post_data["mask"]);
params.push_back(message.post_data["access"]);
- WebPanel::RunCommand(na->nc->display, na->nc, "ChanServ", "chanserv/access", params, replacements);
+ WebPanel::RunCommand(na->GetAccount()->GetDisplay(), na->GetAccount(), "ChanServ", "chanserv/access", params, replacements);
}
else if (provider == "chanserv/xop")
{
std::vector<Anope::string> params;
- params.push_back(ci->name);
+ params.push_back(ci->GetName());
params.push_back("ADD");
params.push_back(message.post_data["mask"]);
- WebPanel::RunCommandWithName(na->nc, "ChanServ", "chanserv/xop", message.post_data["access"], params, replacements);
+ WebPanel::RunCommandWithName(na->GetAccount(), "ChanServ", "chanserv/xop", message.post_data["access"], params, replacements);
}
else if (provider == "chanserv/flags")
{
std::vector<Anope::string> params;
- params.push_back(ci->name);
+ params.push_back(ci->GetName());
params.push_back("MODIFY");
params.push_back(message.post_data["mask"]);
params.push_back(message.post_data["access"]);
- WebPanel::RunCommand(na->nc->display, na->nc, "ChanServ", "chanserv/flags", params, replacements);
+ WebPanel::RunCommand(na->GetAccount()->GetDisplay(), na->GetAccount(), "ChanServ", "chanserv/flags", params, replacements);
}
}
}
/* command might have invalidated u_access */
- u_access = ci->AccessFor(na->nc);
+ u_access = ci->AccessFor(na->GetAccount());
replacements["ESCAPED_CHANNEL"] = HTTPUtils::URLEncode(chname);
replacements["ACCESS_CHANGE"] = u_access.HasPriv("ACCESS_CHANGE") ? "YES" : "NO";
for (unsigned i = 0; i < ci->GetAccessCount(); ++i)
{
- ChanAccess *access = ci->GetAccess(i);
+ ::ChanServ::ChanAccess *access = ci->GetAccess(i);
replacements["MASKS"] = HTTPUtils::Escape(access->Mask());
replacements["ACCESSES"] = HTTPUtils::Escape(access->AccessSerialize());
- replacements["CREATORS"] = HTTPUtils::Escape(access->creator);
+ replacements["CREATORS"] = HTTPUtils::Escape(access->GetCreator());
}
- if (Service::FindService("Command", "chanserv/access"))
+ if (ServiceManager::Get()->FindService("Command", "chanserv/access"))
replacements["PROVIDERS"] = "chanserv/access";
- if (Service::FindService("Command", "chanserv/xop"))
+ if (ServiceManager::Get()->FindService("Command", "chanserv/xop"))
replacements["PROVIDERS"] = "chanserv/xop";
- if (Service::FindService("Command", "chanserv/flags"))
+ if (ServiceManager::Get()->FindService("Command", "chanserv/flags"))
replacements["PROVIDERS"] = "chanserv/flags";
Page.Serve(server, page_name, client, message, reply, replacements);
diff --git a/modules/webcpanel/pages/chanserv/access.h b/modules/webcpanel/pages/chanserv/access.h
index fac203eee..288d976ae 100644
--- a/modules/webcpanel/pages/chanserv/access.h
+++ b/modules/webcpanel/pages/chanserv/access.h
@@ -1,8 +1,20 @@
/*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Anope IRC Services
*
- * Please read COPYING and README for further details.
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
namespace WebCPanel
@@ -16,9 +28,9 @@ class Access : public WebPanelProtectedPage
public:
Access(const Anope::string &cat, const Anope::string &u);
- bool OnRequest(HTTPProvider *, const Anope::string &, HTTPClient *, HTTPMessage &, HTTPReply &, NickAlias *, TemplateFileServer::Replacements &) anope_override;
+ bool OnRequest(HTTPProvider *, const Anope::string &, HTTPClient *, HTTPMessage &, HTTPReply &, ::NickServ::Nick *, TemplateFileServer::Replacements &) override;
- std::set<Anope::string> GetData() anope_override;
+ std::set<Anope::string> GetData() override;
};
}
diff --git a/modules/webcpanel/pages/chanserv/akick.cpp b/modules/webcpanel/pages/chanserv/akick.cpp
index 8a6a09d4d..eb1c81f5a 100644
--- a/modules/webcpanel/pages/chanserv/akick.cpp
+++ b/modules/webcpanel/pages/chanserv/akick.cpp
@@ -1,18 +1,31 @@
/*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Anope IRC Services
*
- * Please read COPYING and README for further details.
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "../../webcpanel.h"
#include "utils.h"
+#include "modules/chanserv/akick.h"
WebCPanel::ChanServ::Akick::Akick(const Anope::string &cat, const Anope::string &u) : WebPanelProtectedPage(cat, u)
{
}
-bool WebCPanel::ChanServ::Akick::OnRequest(HTTPProvider *server, const Anope::string &page_name, HTTPClient *client, HTTPMessage &message, HTTPReply &reply, NickAlias *na, TemplateFileServer::Replacements &replacements)
+bool WebCPanel::ChanServ::Akick::OnRequest(HTTPProvider *server, const Anope::string &page_name, HTTPClient *client, HTTPMessage &message, HTTPReply &reply, ::NickServ::Nick *na, TemplateFileServer::Replacements &replacements)
{
const Anope::string &chname = message.get_data["channel"];
TemplateFileServer Page("chanserv/akick.html");
@@ -25,7 +38,7 @@ bool WebCPanel::ChanServ::Akick::OnRequest(HTTPProvider *server, const Anope::st
return true;
}
- ChannelInfo *ci = ChannelInfo::Find(chname);
+ ::ChanServ::Channel *ci = ::ChanServ::Find(chname);
if (!ci)
{
@@ -34,8 +47,8 @@ bool WebCPanel::ChanServ::Akick::OnRequest(HTTPProvider *server, const Anope::st
return true;
}
- AccessGroup u_access = ci->AccessFor(na->nc);
- bool has_priv = na->nc->IsServicesOper() && na->nc->o->ot->HasPriv("chanserv/access/modify");
+ ::ChanServ::AccessGroup u_access = ci->AccessFor(na->GetAccount());
+ bool has_priv = na->GetAccount()->IsServicesOper() && na->GetAccount()->o->GetType()->HasPriv("chanserv/access/modify");
if (!u_access.HasPriv("AKICK") && !has_priv)
{
@@ -49,36 +62,36 @@ bool WebCPanel::ChanServ::Akick::OnRequest(HTTPProvider *server, const Anope::st
if (message.get_data["del"].empty() == false && message.get_data["mask"].empty() == false)
{
std::vector<Anope::string> params;
- params.push_back(ci->name);
+ params.push_back(ci->GetName());
params.push_back("DEL");
params.push_back(message.get_data["mask"]);
- WebPanel::RunCommand(na->nc->display, na->nc, "ChanServ", "chanserv/akick", params, replacements);
+ WebPanel::RunCommand(na->GetAccount()->GetDisplay(), na->GetAccount(), "ChanServ", "chanserv/akick", params, replacements);
}
else if (message.post_data["mask"].empty() == false)
{
std::vector<Anope::string> params;
- params.push_back(ci->name);
+ params.push_back(ci->GetName());
params.push_back("ADD");
params.push_back(message.post_data["mask"]);
if (message.post_data["reason"].empty() == false)
params.push_back(message.post_data["reason"]);
- WebPanel::RunCommand(na->nc->display, na->nc, "ChanServ", "chanserv/akick", params, replacements);
+ WebPanel::RunCommand(na->GetAccount()->GetDisplay(), na->GetAccount(), "ChanServ", "chanserv/akick", params, replacements);
}
replacements["ESCAPED_CHANNEL"] = HTTPUtils::URLEncode(chname);
for (unsigned i = 0; i < ci->GetAkickCount(); ++i)
{
- AutoKick *akick = ci->GetAkick(i);
+ AutoKick *ak = ci->GetAkick(i);
- if (akick->nc)
- replacements["MASKS"] = HTTPUtils::Escape(akick->nc->display);
+ if (ak->GetAccount())
+ replacements["MASKS"] = HTTPUtils::Escape(ak->GetAccount()->GetDisplay());
else
- replacements["MASKS"] = HTTPUtils::Escape(akick->mask);
- replacements["CREATORS"] = HTTPUtils::Escape(akick->creator);
- replacements["REASONS"] = HTTPUtils::Escape(akick->reason);
+ replacements["MASKS"] = HTTPUtils::Escape(ak->GetMask());
+ replacements["CREATORS"] = HTTPUtils::Escape(ak->GetCreator());
+ replacements["REASONS"] = HTTPUtils::Escape(ak->GetReason());
}
Page.Serve(server, page_name, client, message, reply, replacements);
diff --git a/modules/webcpanel/pages/chanserv/akick.h b/modules/webcpanel/pages/chanserv/akick.h
index 393f79853..ff1730bc7 100644
--- a/modules/webcpanel/pages/chanserv/akick.h
+++ b/modules/webcpanel/pages/chanserv/akick.h
@@ -1,8 +1,20 @@
/*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Anope IRC Services
*
- * Please read COPYING and README for further details.
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
namespace WebCPanel
@@ -16,9 +28,9 @@ class Akick : public WebPanelProtectedPage
public:
Akick(const Anope::string &cat, const Anope::string &u);
- bool OnRequest(HTTPProvider *, const Anope::string &, HTTPClient *, HTTPMessage &, HTTPReply &, NickAlias *, TemplateFileServer::Replacements &) anope_override;
+ bool OnRequest(HTTPProvider *, const Anope::string &, HTTPClient *, HTTPMessage &, HTTPReply &, ::NickServ::Nick *, TemplateFileServer::Replacements &) override;
- std::set<Anope::string> GetData() anope_override;
+ std::set<Anope::string> GetData() override;
};
}
diff --git a/modules/webcpanel/pages/chanserv/drop.cpp b/modules/webcpanel/pages/chanserv/drop.cpp
index e2c131f7a..cc5fa4026 100644
--- a/modules/webcpanel/pages/chanserv/drop.cpp
+++ b/modules/webcpanel/pages/chanserv/drop.cpp
@@ -1,8 +1,20 @@
/*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Anope IRC Services
*
- * Please read COPYING and README for further details.
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "../../webcpanel.h"
@@ -13,7 +25,7 @@ WebCPanel::ChanServ::Drop::Drop(const Anope::string &cat, const Anope::string &u
}
-bool WebCPanel::ChanServ::Drop::OnRequest(HTTPProvider *server, const Anope::string &page_name, HTTPClient *client, HTTPMessage &message, HTTPReply &reply, NickAlias *na, TemplateFileServer::Replacements &replacements)
+bool WebCPanel::ChanServ::Drop::OnRequest(HTTPProvider *server, const Anope::string &page_name, HTTPClient *client, HTTPMessage &message, HTTPReply &reply, ::NickServ::Nick *na, TemplateFileServer::Replacements &replacements)
{
if (message.post_data.count("channel") > 0 && message.post_data.count("confChan") > 0)
@@ -25,23 +37,18 @@ bool WebCPanel::ChanServ::Drop::OnRequest(HTTPProvider *server, const Anope::str
params.push_back(channel);
params.push_back(channel);
- WebPanel::RunCommand(na->nc->display, na->nc, "ChanServ", "chanserv/drop", params, replacements);
+ WebPanel::RunCommand(na->GetAccount()->GetDisplay(), na->GetAccount(), "ChanServ", "chanserv/drop", params, replacements);
}
else
replacements["MESSAGES"] = "Invalid Confirmation";
}
- std::deque<ChannelInfo *> queue;
- na->nc->GetChannelReferences(queue);
- for (unsigned i = 0; i < queue.size(); ++i)
- {
- ChannelInfo *ci = queue[i];
- if ((ci->HasExt("SECUREFOUNDER") ? ci->AccessFor(na->nc).founder : ci->AccessFor(na->nc).HasPriv("FOUNDER")) || (na->nc->IsServicesOper() && na->nc->o->ot->HasCommand("chanserv/drop")))
+ for (::ChanServ::Channel *ci : na->GetAccount()->GetRefs<::ChanServ::Channel *>())
+ if ((ci->HasFieldS("SECUREFOUNDER") ? ci->AccessFor(na->GetAccount()).founder : ci->AccessFor(na->GetAccount()).HasPriv("FOUNDER")) || (na->GetAccount()->IsServicesOper() && na->GetAccount()->o->GetType()->HasCommand("chanserv/drop")))
{
- replacements["CHANNEL_NAMES"] = ci->name;
- replacements["ESCAPED_CHANNEL_NAMES"] = HTTPUtils::URLEncode(ci->name);
+ replacements["CHANNEL_NAMES"] = ci->GetName();
+ replacements["ESCAPED_CHANNEL_NAMES"] = HTTPUtils::URLEncode(ci->GetName());
}
- }
if (message.get_data.count("channel") > 0)
{
diff --git a/modules/webcpanel/pages/chanserv/drop.h b/modules/webcpanel/pages/chanserv/drop.h
index f50d9428c..1a4f49a25 100644
--- a/modules/webcpanel/pages/chanserv/drop.h
+++ b/modules/webcpanel/pages/chanserv/drop.h
@@ -1,8 +1,20 @@
/*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Anope IRC Services
*
- * Please read COPYING and README for further details.
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
namespace WebCPanel
@@ -16,7 +28,7 @@ namespace WebCPanel
public:
Drop(const Anope::string &cat, const Anope::string &u);
- bool OnRequest(HTTPProvider *, const Anope::string &, HTTPClient *, HTTPMessage &, HTTPReply &, NickAlias *, TemplateFileServer::Replacements &) anope_override;
+ bool OnRequest(HTTPProvider *, const Anope::string &, HTTPClient *, HTTPMessage &, HTTPReply &, ::NickServ::Nick *, TemplateFileServer::Replacements &) override;
};
diff --git a/modules/webcpanel/pages/chanserv/info.cpp b/modules/webcpanel/pages/chanserv/info.cpp
index 856cc9b50..b7c1a5cef 100644
--- a/modules/webcpanel/pages/chanserv/info.cpp
+++ b/modules/webcpanel/pages/chanserv/info.cpp
@@ -1,8 +1,20 @@
/*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Anope IRC Services
*
- * Please read COPYING and README for further details.
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "../../webcpanel.h"
@@ -12,7 +24,7 @@ WebCPanel::ChanServ::Info::Info(const Anope::string &cat, const Anope::string &u
{
}
-bool WebCPanel::ChanServ::Info::OnRequest(HTTPProvider *server, const Anope::string &page_name, HTTPClient *client, HTTPMessage &message, HTTPReply &reply, NickAlias *na, TemplateFileServer::Replacements &replacements)
+bool WebCPanel::ChanServ::Info::OnRequest(HTTPProvider *server, const Anope::string &page_name, HTTPClient *client, HTTPMessage &message, HTTPReply &reply, ::NickServ::Nick *na, TemplateFileServer::Replacements &replacements)
{
const Anope::string &chname = message.get_data["channel"];
diff --git a/modules/webcpanel/pages/chanserv/info.h b/modules/webcpanel/pages/chanserv/info.h
index b1108b11c..91974a707 100644
--- a/modules/webcpanel/pages/chanserv/info.h
+++ b/modules/webcpanel/pages/chanserv/info.h
@@ -1,8 +1,20 @@
/*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Anope IRC Services
*
- * Please read COPYING and README for further details.
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
namespace WebCPanel
@@ -16,7 +28,7 @@ class Info : public WebPanelProtectedPage
public:
Info(const Anope::string &cat, const Anope::string &u);
- bool OnRequest(HTTPProvider *, const Anope::string &, HTTPClient *, HTTPMessage &, HTTPReply &, NickAlias *, TemplateFileServer::Replacements &) anope_override;
+ bool OnRequest(HTTPProvider *, const Anope::string &, HTTPClient *, HTTPMessage &, HTTPReply &, ::NickServ::Nick *, TemplateFileServer::Replacements &) override;
};
}
diff --git a/modules/webcpanel/pages/chanserv/modes.cpp b/modules/webcpanel/pages/chanserv/modes.cpp
index 8e77c0287..da7ad0803 100644
--- a/modules/webcpanel/pages/chanserv/modes.cpp
+++ b/modules/webcpanel/pages/chanserv/modes.cpp
@@ -1,8 +1,20 @@
/*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Anope IRC Services
*
- * Please read COPYING and README for further details.
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "../../webcpanel.h"
@@ -12,7 +24,7 @@ WebCPanel::ChanServ::Modes::Modes(const Anope::string &cat, const Anope::string
{
}
-bool WebCPanel::ChanServ::Modes::OnRequest(HTTPProvider *server, const Anope::string &page_name, HTTPClient *client, HTTPMessage &message, HTTPReply &reply, NickAlias *na, TemplateFileServer::Replacements &replacements)
+bool WebCPanel::ChanServ::Modes::OnRequest(HTTPProvider *server, const Anope::string &page_name, HTTPClient *client, HTTPMessage &message, HTTPReply &reply, ::NickServ::Nick *na, TemplateFileServer::Replacements &replacements)
{
const Anope::string &chname = message.get_data["channel"];
const Anope::string &mode = message.get_data["m"];
@@ -27,7 +39,7 @@ bool WebCPanel::ChanServ::Modes::OnRequest(HTTPProvider *server, const Anope::st
}
replacements["ESCAPED_CHANNEL"] = HTTPUtils::URLEncode(chname);
- ChannelInfo *ci = ChannelInfo::Find(chname);
+ ::ChanServ::Channel *ci = ::ChanServ::Find(chname);
if (!ci)
{
@@ -39,13 +51,13 @@ bool WebCPanel::ChanServ::Modes::OnRequest(HTTPProvider *server, const Anope::st
if (!c)
{
- replacements["MESSAGES"] = Anope::printf(CHAN_X_NOT_IN_USE, chname.c_str());
+ replacements["MESSAGES"] = Anope::printf(_("Channel \002%s\002 doesn't exist."), chname.c_str());
Page.Serve(server, page_name, client, message, reply, replacements);
return true;
}
- AccessGroup u_access = ci->AccessFor(na->nc);
- bool has_priv = na->nc->IsServicesOper() && na->nc->o->ot->HasPriv("chanserv/administration");
+ ::ChanServ::AccessGroup u_access = ci->AccessFor(na->GetAccount());
+ bool has_priv = na->GetAccount()->IsServicesOper() && na->GetAccount()->o->GetType()->HasPriv("chanserv/administration");
if (!u_access.HasPriv("MODE") && !has_priv)
{
@@ -78,20 +90,20 @@ bool WebCPanel::ChanServ::Modes::OnRequest(HTTPProvider *server, const Anope::st
if (message.get_data["del"].empty() == false && message.get_data["mask"].empty() == false)
{
std::vector<Anope::string> params;
- params.push_back(ci->name);
+ params.push_back(ci->GetName());
params.push_back("SET");
params.push_back("-" + Anope::string(cm->mchar));
params.push_back(message.get_data["mask"]);
- WebPanel::RunCommand(na->nc->display, na->nc, "ChanServ", "chanserv/mode", params, replacements);
+ WebPanel::RunCommand(na->GetAccount()->GetDisplay(), na->GetAccount(), "ChanServ", "chanserv/mode", params, replacements);
}
else if (message.post_data["mask"].empty() == false)
{
std::vector<Anope::string> params;
- params.push_back(ci->name);
+ params.push_back(ci->GetName());
params.push_back("SET");
params.push_back("+" + Anope::string(cm->mchar));
params.push_back(message.post_data["mask"]);
- WebPanel::RunCommand(na->nc->display, na->nc, "ChanServ", "chanserv/mode", params, replacements);
+ WebPanel::RunCommand(na->GetAccount()->GetDisplay(), na->GetAccount(), "ChanServ", "chanserv/mode", params, replacements);
}
std::vector<Anope::string> v = c->GetModeList(cm->name);
diff --git a/modules/webcpanel/pages/chanserv/modes.h b/modules/webcpanel/pages/chanserv/modes.h
index 5f752d203..2b4985b30 100644
--- a/modules/webcpanel/pages/chanserv/modes.h
+++ b/modules/webcpanel/pages/chanserv/modes.h
@@ -1,8 +1,20 @@
/*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Anope IRC Services
*
- * Please read COPYING and README for further details.
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
namespace WebCPanel
@@ -16,9 +28,9 @@ class Modes : public WebPanelProtectedPage
public:
Modes(const Anope::string &cat, const Anope::string &u);
- bool OnRequest(HTTPProvider *, const Anope::string &, HTTPClient *, HTTPMessage &, HTTPReply &, NickAlias *, TemplateFileServer::Replacements &) anope_override;
+ bool OnRequest(HTTPProvider *, const Anope::string &, HTTPClient *, HTTPMessage &, HTTPReply &, ::NickServ::Nick *, TemplateFileServer::Replacements &) override;
- std::set<Anope::string> GetData() anope_override;
+ std::set<Anope::string> GetData() override;
};
}
diff --git a/modules/webcpanel/pages/chanserv/set.cpp b/modules/webcpanel/pages/chanserv/set.cpp
index 84b31d50e..d3cca72cc 100644
--- a/modules/webcpanel/pages/chanserv/set.cpp
+++ b/modules/webcpanel/pages/chanserv/set.cpp
@@ -1,8 +1,20 @@
/*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Anope IRC Services
*
- * Please read COPYING and README for further details.
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "../../webcpanel.h"
@@ -12,7 +24,7 @@ WebCPanel::ChanServ::Set::Set(const Anope::string &cat, const Anope::string &u)
{
}
-bool WebCPanel::ChanServ::Set::OnRequest(HTTPProvider *server, const Anope::string &page_name, HTTPClient *client, HTTPMessage &message, HTTPReply &reply, NickAlias *na, TemplateFileServer::Replacements &replacements)
+bool WebCPanel::ChanServ::Set::OnRequest(HTTPProvider *server, const Anope::string &page_name, HTTPClient *client, HTTPMessage &message, HTTPReply &reply, ::NickServ::Nick *na, TemplateFileServer::Replacements &replacements)
{
const Anope::string &chname = message.get_data["channel"];
bool can_set = false;
@@ -26,7 +38,7 @@ bool WebCPanel::ChanServ::Set::OnRequest(HTTPProvider *server, const Anope::stri
return true;
}
- ChannelInfo *ci = ChannelInfo::Find(chname);
+ ::ChanServ::Channel *ci = ::ChanServ::Find(chname);
if (!ci)
{
@@ -36,7 +48,7 @@ bool WebCPanel::ChanServ::Set::OnRequest(HTTPProvider *server, const Anope::stri
replacements["OKAY"];
- if (ci->AccessFor(na->nc).HasPriv("SET"))
+ if (ci->AccessFor(na->GetAccount()).HasPriv("SET"))
{
replacements["CAN_SET"];
can_set = true;
@@ -44,101 +56,101 @@ bool WebCPanel::ChanServ::Set::OnRequest(HTTPProvider *server, const Anope::stri
if (can_set && message.post_data.empty() == false)
{
- if (ci->HasExt("KEEPTOPIC") != message.post_data.count("keeptopic"))
+ if (ci->HasFieldS("KEEPTOPIC") != message.post_data.count("keeptopic"))
{
- if (!ci->HasExt("KEEPTOPIC"))
- ci->Extend<bool>("KEEPTOPIC");
+ if (!ci->HasFieldS("KEEPTOPIC"))
+ ci->SetS<bool>("KEEPTOPIC", true);
else
- ci->Shrink<bool>("KEEPTOPIC");
+ ci->UnsetS<bool>("KEEPTOPIC");
replacements["MESSAGES"] = "Secure updated";
}
- if (ci->HasExt("PEACE") != message.post_data.count("peace"))
+ if (ci->HasFieldS("PEACE") != message.post_data.count("peace"))
{
- if (!ci->HasExt("PEACE"))
- ci->Extend<bool>("PEACE");
+ if (!ci->HasFieldS("PEACE"))
+ ci->SetS<bool>("PEACE", true);
else
- ci->Shrink<bool>("PEACE");
+ ci->UnsetS<bool>("PEACE");
replacements["MESSAGES"] = "Peace updated";
}
- if (ci->HasExt("CS_PRIVATE") != message.post_data.count("private"))
+ if (ci->HasFieldS("CS_PRIVATE") != message.post_data.count("private"))
{
- if (!ci->HasExt("CS_PRIVATE"))
- ci->Extend<bool>("CS_PRIVATE");
+ if (!ci->HasFieldS("CS_PRIVATE"))
+ ci->SetS<bool>("CS_PRIVATE", true);
else
- ci->Shrink<bool>("CS_PRIVATE");
+ ci->UnsetS<bool>("CS_PRIVATE");
replacements["MESSAGES"] = "Private updated";
}
- if (ci->HasExt("RESTRICTED") != message.post_data.count("restricted"))
+ if (ci->HasFieldS("RESTRICTED") != message.post_data.count("restricted"))
{
- if (!ci->HasExt("RESTRICTED"))
- ci->Extend<bool>("RESTRICTED");
+ if (!ci->HasFieldS("RESTRICTED"))
+ ci->SetS<bool>("RESTRICTED", true);
else
- ci->Shrink<bool>("RESTRICTED");
+ ci->UnsetS<bool>("RESTRICTED");
replacements["MESSAGES"] = "Restricted updated";
}
- if (ci->HasExt("CS_SECURE") != message.post_data.count("secure"))
+ if (ci->HasFieldS("CS_SECURE") != message.post_data.count("secure"))
{
- if (!ci->HasExt("CS_SECURE"))
- ci->Extend<bool>("CS_SECURE");
+ if (!ci->HasFieldS("CS_SECURE"))
+ ci->SetS<bool>("CS_SECURE", true);
else
- ci->Shrink<bool>("CS_SECURE");
+ ci->UnsetS<bool>("CS_SECURE");
replacements["MESSAGES"] = "Secure updated";
}
- if (ci->HasExt("SECUREOPS") != message.post_data.count("secureops"))
+ if (ci->HasFieldS("SECUREOPS") != message.post_data.count("secureops"))
{
- if (!ci->HasExt("SECUREOPS"))
- ci->Extend<bool>("SECUREOPS");
+ if (!ci->HasFieldS("SECUREOPS"))
+ ci->SetS<bool>("SECUREOPS", true);
else
- ci->Shrink<bool>("SECUREOPS");
+ ci->UnsetS<bool>("SECUREOPS");
replacements["MESSAGES"] = "Secureops updated";
}
- if (ci->HasExt("TOPICLOCK") != message.post_data.count("topiclock"))
+ if (ci->HasFieldS("TOPICLOCK") != message.post_data.count("topiclock"))
{
- if (!ci->HasExt("TOPICLOCK"))
- ci->Extend<bool>("TOPICLOCK");
+ if (!ci->HasFieldS("TOPICLOCK"))
+ ci->SetS<bool>("TOPICLOCK", true);
else
- ci->Shrink<bool>("TOPICLOCK");
+ ci->UnsetS<bool>("TOPICLOCK");
replacements["MESSAGES"] = "Topiclock updated";
}
}
- replacements["CHANNEL"] = HTTPUtils::Escape(ci->name);
- replacements["CHANNEL_ESCAPED"] = HTTPUtils::URLEncode(ci->name);
+ replacements["CHANNEL"] = HTTPUtils::Escape(ci->GetName());
+ replacements["CHANNEL_ESCAPED"] = HTTPUtils::URLEncode(ci->GetName());
if (ci->GetFounder())
- replacements["FOUNDER"] = ci->GetFounder()->display;
+ replacements["FOUNDER"] = ci->GetFounder()->GetDisplay();
if (ci->GetSuccessor())
- replacements["SUCCESSOR"] = ci->GetSuccessor()->display;
- replacements["TIME_REGISTERED"] = Anope::strftime(ci->time_registered, na->nc);
- replacements["LAST_USED"] = Anope::strftime(ci->last_used, na->nc);
+ replacements["SUCCESSOR"] = ci->GetSuccessor()->GetDisplay();
+ replacements["TIME_REGISTERED"] = Anope::strftime(ci->GetTimeRegistered(), na->GetAccount());
+ replacements["LAST_USED"] = Anope::strftime(ci->GetLastUsed(), na->GetAccount());
replacements["ESCAPED_CHANNEL"] = HTTPUtils::URLEncode(chname);
- if (!ci->last_topic.empty())
+ if (!ci->GetLastTopic().empty())
{
- replacements["LAST_TOPIC"] = HTTPUtils::Escape(ci->last_topic);
- replacements["LAST_TOPIC_SETTER"] = HTTPUtils::Escape(ci->last_topic_setter);
+ replacements["LAST_TOPIC"] = HTTPUtils::Escape(ci->GetLastTopic());
+ replacements["LAST_TOPIC_SETTER"] = HTTPUtils::Escape(ci->GetLastTopicSetter());
}
if (can_set)
{
- if (ci->HasExt("KEEPTOPIC"))
+ if (ci->HasFieldS("KEEPTOPIC"))
replacements["KEEPTOPIC"];
- if (ci->HasExt("PEACE"))
+ if (ci->HasFieldS("PEACE"))
replacements["PEACE"];
- if (ci->HasExt("CS_PRIVATE"))
+ if (ci->HasFieldS("CS_PRIVATE"))
replacements["PRIVATE"];
- if (ci->HasExt("RESTRICTED"))
+ if (ci->HasFieldS("RESTRICTED"))
replacements["RESTRICTED"];
- if (ci->HasExt("CS_SECURE"))
+ if (ci->HasFieldS("CS_SECURE"))
replacements["SECURE"];
- if (ci->HasExt("SECUREOPS"))
+ if (ci->HasFieldS("SECUREOPS"))
replacements["SECUREOPS"];
- if (ci->HasExt("TOPICLOCK"))
+ if (ci->HasFieldS("TOPICLOCK"))
replacements["TOPICLOCK"];
}
diff --git a/modules/webcpanel/pages/chanserv/set.h b/modules/webcpanel/pages/chanserv/set.h
index bc88d13c6..5a1b05346 100644
--- a/modules/webcpanel/pages/chanserv/set.h
+++ b/modules/webcpanel/pages/chanserv/set.h
@@ -1,8 +1,20 @@
/*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Anope IRC Services
*
- * Please read COPYING and README for further details.
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
namespace WebCPanel
@@ -16,9 +28,9 @@ class Set : public WebPanelProtectedPage
public:
Set(const Anope::string &cat, const Anope::string &u);
- bool OnRequest(HTTPProvider *, const Anope::string &, HTTPClient *, HTTPMessage &, HTTPReply &, NickAlias *, TemplateFileServer::Replacements &) anope_override;
+ bool OnRequest(HTTPProvider *, const Anope::string &, HTTPClient *, HTTPMessage &, HTTPReply &, ::NickServ::Nick *, TemplateFileServer::Replacements &) override;
- std::set<Anope::string> GetData() anope_override;
+ std::set<Anope::string> GetData() override;
};
}
diff --git a/modules/webcpanel/pages/chanserv/utils.cpp b/modules/webcpanel/pages/chanserv/utils.cpp
index 5c055f722..bdf0b92ec 100644
--- a/modules/webcpanel/pages/chanserv/utils.cpp
+++ b/modules/webcpanel/pages/chanserv/utils.cpp
@@ -1,17 +1,29 @@
/*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Anope IRC Services
*
- * Please read COPYING and README for further details.
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "../../webcpanel.h"
namespace
{
- bool ChannelSort(ChannelInfo *ci1, ChannelInfo *ci2)
+ bool ChannelSort(ChanServ::Channel *ci1, ChanServ::Channel *ci2)
{
- return ci::less()(ci1->name, ci2->name);
+ return ci::less()(ci1->GetName(), ci2->GetName());
}
}
@@ -21,21 +33,18 @@ namespace WebCPanel
namespace ChanServ
{
-void BuildChanList(NickAlias *na, TemplateFileServer::Replacements &replacements)
+void BuildChanList(::NickServ::Nick *na, TemplateFileServer::Replacements &replacements)
{
- std::deque<ChannelInfo *> queue;
- na->nc->GetChannelReferences(queue);
- std::sort(queue.begin(), queue.end(), ChannelSort);
+ std::vector<::ChanServ::Channel *> chans = na->GetAccount()->GetRefs<::ChanServ::Channel *>();
+ std::sort(chans.begin(), chans.end(), ChannelSort);
- for (unsigned i = 0; i < queue.size(); ++i)
+ for (::ChanServ::Channel *ci : chans)
{
- ChannelInfo *ci = queue[i];
-
- if (na->nc != ci->GetFounder() && ci->AccessFor(na->nc).empty())
+ if (na->GetAccount() != ci->GetFounder() && ci->AccessFor(na->GetAccount()).empty())
continue;
- replacements["CHANNEL_NAMES"] = ci->name;
- replacements["ESCAPED_CHANNEL_NAMES"] = HTTPUtils::URLEncode(ci->name);
+ replacements["CHANNEL_NAMES"] = ci->GetName();
+ replacements["ESCAPED_CHANNEL_NAMES"] = HTTPUtils::URLEncode(ci->GetName());
}
}
diff --git a/modules/webcpanel/pages/chanserv/utils.h b/modules/webcpanel/pages/chanserv/utils.h
index d5a794c61..8ad1f3c9e 100644
--- a/modules/webcpanel/pages/chanserv/utils.h
+++ b/modules/webcpanel/pages/chanserv/utils.h
@@ -1,8 +1,20 @@
/*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Anope IRC Services
*
- * Please read COPYING and README for further details.
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
namespace WebCPanel
@@ -11,7 +23,7 @@ namespace WebCPanel
namespace ChanServ
{
-extern void BuildChanList(NickAlias *, TemplateFileServer::Replacements &);
+extern void BuildChanList(::NickServ::Nick *, TemplateFileServer::Replacements &);
}
diff --git a/modules/webcpanel/pages/confirm.cpp b/modules/webcpanel/pages/confirm.cpp
index bb0412f7e..c8c4e0bbd 100644
--- a/modules/webcpanel/pages/confirm.cpp
+++ b/modules/webcpanel/pages/confirm.cpp
@@ -1,8 +1,20 @@
/*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Anope IRC Services
*
- * Please read COPYING and README for further details.
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "../webcpanel.h"
diff --git a/modules/webcpanel/pages/confirm.h b/modules/webcpanel/pages/confirm.h
index f1e95062d..ba0c367dd 100644
--- a/modules/webcpanel/pages/confirm.h
+++ b/modules/webcpanel/pages/confirm.h
@@ -1,8 +1,20 @@
/*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Anope IRC Services
*
- * Please read COPYING and README for further details.
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "modules/httpd.h"
@@ -15,7 +27,7 @@ class Confirm : public WebPanelPage
public:
Confirm(const Anope::string &u) : WebPanelPage(u) { }
- bool OnRequest(HTTPProvider *, const Anope::string &, HTTPClient *, HTTPMessage &, HTTPReply &) anope_override;
+ bool OnRequest(HTTPProvider *, const Anope::string &, HTTPClient *, HTTPMessage &, HTTPReply &) override;
};
}
diff --git a/modules/webcpanel/pages/hostserv/request.cpp b/modules/webcpanel/pages/hostserv/request.cpp
index 5ceb178d2..0230ba8fa 100644
--- a/modules/webcpanel/pages/hostserv/request.cpp
+++ b/modules/webcpanel/pages/hostserv/request.cpp
@@ -1,24 +1,36 @@
/*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Anope IRC Services
*
- * Please read COPYING and README for further details.
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "../../webcpanel.h"
-WebCPanel::HostServ::Request::Request(const Anope::string &cat, const Anope::string &u) : WebPanelProtectedPage (cat, u)
+WebCPanel::HostServ::Request::Request(const Anope::string &cat, const Anope::string &u) : WebPanelProtectedPage (cat, u)
{
}
-bool WebCPanel::HostServ::Request::OnRequest(HTTPProvider *server, const Anope::string &page_name, HTTPClient *client, HTTPMessage &message, HTTPReply &reply, NickAlias *na, TemplateFileServer::Replacements &replacements)
+bool WebCPanel::HostServ::Request::OnRequest(HTTPProvider *server, const Anope::string &page_name, HTTPClient *client, HTTPMessage &message, HTTPReply &reply, ::NickServ::Nick *na, TemplateFileServer::Replacements &replacements)
{
if (message.post_data.count("req") > 0)
{
std::vector<Anope::string> params;
params.push_back(HTTPUtils::URLDecode(message.post_data["req"]));
- WebPanel::RunCommand(na->nc->display, na->nc, "HostServ", "hostserv/request", params, replacements, "CMDR");
+ WebPanel::RunCommand(na->GetAccount()->GetDisplay(), na->GetAccount(), "HostServ", "hostserv/request", params, replacements, "CMDR");
}
if (na->HasVhost())
@@ -28,7 +40,7 @@ bool WebCPanel::HostServ::Request::OnRequest(HTTPProvider *server, const Anope::
else
replacements["VHOST"] = na->GetVhostHost();
}
- if (ServiceReference<Command>("Command", "hostserv/request"))
+ if (ServiceReference<Command>("hostserv/request"))
replacements["CAN_REQUEST"] = "YES";
TemplateFileServer page("hostserv/request.html");
page.Serve(server, page_name, client, message, reply, replacements);
diff --git a/modules/webcpanel/pages/hostserv/request.h b/modules/webcpanel/pages/hostserv/request.h
index 5dafd15fd..e9bf6b23b 100644
--- a/modules/webcpanel/pages/hostserv/request.h
+++ b/modules/webcpanel/pages/hostserv/request.h
@@ -1,8 +1,20 @@
/*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Anope IRC Services
*
- * Please read COPYING and README for further details.
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
namespace WebCPanel
@@ -16,7 +28,7 @@ class Request : public WebPanelProtectedPage
public:
Request(const Anope::string &cat, const Anope::string &u);
- bool OnRequest(HTTPProvider *, const Anope::string &, HTTPClient *, HTTPMessage &, HTTPReply &, NickAlias *, TemplateFileServer::Replacements &) anope_override;
+ bool OnRequest(HTTPProvider *, const Anope::string &, HTTPClient *, HTTPMessage &, HTTPReply &, ::NickServ::Nick *, TemplateFileServer::Replacements &) override;
};
}
diff --git a/modules/webcpanel/pages/index.cpp b/modules/webcpanel/pages/index.cpp
index 1375c3c13..0436f73af 100644
--- a/modules/webcpanel/pages/index.cpp
+++ b/modules/webcpanel/pages/index.cpp
@@ -1,13 +1,26 @@
/*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Anope IRC Services
*
- * Please read COPYING and README for further details.
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "../webcpanel.h"
+#include "modules/nickserv.h"
-class WebpanelRequest : public IdentifyRequest
+class WebpanelRequest : public NickServ::IdentifyRequestListener
{
HTTPReply reply;
HTTPMessage message;
@@ -17,22 +30,22 @@ class WebpanelRequest : public IdentifyRequest
TemplateFileServer::Replacements replacements;
public:
- WebpanelRequest(Module *o, HTTPReply &r, HTTPMessage &m, HTTPProvider *s, const Anope::string &p_n, HTTPClient *c, TemplateFileServer::Replacements &re, const Anope::string &user, const Anope::string &pass) : IdentifyRequest(o, user, pass), reply(r), message(m), server(s), page_name(p_n), client(c), replacements(re) { }
+ WebpanelRequest(HTTPReply &r, HTTPMessage &m, HTTPProvider *s, const Anope::string &p_n, HTTPClient *c, TemplateFileServer::Replacements &re) : reply(r), message(m), server(s), page_name(p_n), client(c), replacements(re) { }
- void OnSuccess() anope_override
+ void OnSuccess(NickServ::IdentifyRequest *req) override
{
if (!client || !server)
return;
- NickAlias *na = NickAlias::Find(this->GetAccount());
+ ::NickServ::Nick *na = ::NickServ::FindNick(req->GetAccount());
if (!na)
{
- this->OnFail();
+ this->OnFail(req);
return;
}
- if (na->nc->HasExt("NS_SUSPENDED"))
+ if (na->GetAccount()->HasFieldS("NS_SUSPENDED"))
{
- this->OnFail();
+ this->OnFail(req);
return;
}
@@ -51,12 +64,12 @@ class WebpanelRequest : public IdentifyRequest
{
HTTPReply::cookie c;
- c.push_back(std::make_pair("account", na->nick));
+ c.push_back(std::make_pair("account", na->GetNick()));
c.push_back(std::make_pair("Path", "/"));
reply.cookies.push_back(c);
}
- {
+ {
HTTPReply::cookie c;
c.push_back(std::make_pair("id", id));
c.push_back(std::make_pair("Path", "/"));
@@ -69,7 +82,7 @@ class WebpanelRequest : public IdentifyRequest
client->SendReply(&reply);
}
- void OnFail() anope_override
+ void OnFail(NickServ::IdentifyRequest *req) override
{
if (!client || !server)
return;
@@ -88,12 +101,12 @@ bool WebCPanel::Index::OnRequest(HTTPProvider *server, const Anope::string &page
replacements["TITLE"] = page_title;
- if (!user.empty() && !pass.empty())
+ if (!user.empty() && !pass.empty() && ::NickServ::service)
{
- // Rate limit check.
+ // XXX Rate limit check.
- WebpanelRequest *req = new WebpanelRequest(me, reply, message, server, page_name, client, replacements, user, pass);
- FOREACH_MOD(OnCheckAuthentication, (NULL, req));
+ ::NickServ::IdentifyRequest *req = ::NickServ::service->CreateIdentifyRequest(new WebpanelRequest(reply, message, server, page_name, client, replacements), me, user, pass);
+ EventManager::Get()->Dispatch(&Event::CheckAuthentication::OnCheckAuthentication, nullptr, req);
req->Dispatch();
return false;
}
diff --git a/modules/webcpanel/pages/index.h b/modules/webcpanel/pages/index.h
index 99f6da7b9..2e296a81e 100644
--- a/modules/webcpanel/pages/index.h
+++ b/modules/webcpanel/pages/index.h
@@ -1,8 +1,20 @@
/*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Anope IRC Services
*
- * Please read COPYING and README for further details.
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "modules/httpd.h"
@@ -15,7 +27,7 @@ class Index : public WebPanelPage
public:
Index(const Anope::string &u) : WebPanelPage(u) { }
- bool OnRequest(HTTPProvider *, const Anope::string &, HTTPClient *, HTTPMessage &, HTTPReply &) anope_override;
+ bool OnRequest(HTTPProvider *, const Anope::string &, HTTPClient *, HTTPMessage &, HTTPReply &) override;
};
}
diff --git a/modules/webcpanel/pages/logout.cpp b/modules/webcpanel/pages/logout.cpp
index 2b98e3431..5d32c3c22 100644
--- a/modules/webcpanel/pages/logout.cpp
+++ b/modules/webcpanel/pages/logout.cpp
@@ -1,8 +1,20 @@
/*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Anope IRC Services
*
- * Please read COPYING and README for further details.
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "../webcpanel.h"
@@ -11,7 +23,7 @@ WebCPanel::Logout::Logout(const Anope::string &u) : WebPanelProtectedPage("", u)
{
}
-bool WebCPanel::Logout::OnRequest(HTTPProvider *server, const Anope::string &page_name, HTTPClient *client, HTTPMessage &message, HTTPReply &reply, NickAlias *na, TemplateFileServer::Replacements &replacements)
+bool WebCPanel::Logout::OnRequest(HTTPProvider *server, const Anope::string &page_name, HTTPClient *client, HTTPMessage &message, HTTPReply &reply, ::NickServ::Nick *na, TemplateFileServer::Replacements &replacements)
{
na->Shrink<Anope::string>("webcpanel_id");
na->Shrink<Anope::string>("webcpanel_ip");
diff --git a/modules/webcpanel/pages/logout.h b/modules/webcpanel/pages/logout.h
index 8492d7a26..4fb70d2b2 100644
--- a/modules/webcpanel/pages/logout.h
+++ b/modules/webcpanel/pages/logout.h
@@ -1,8 +1,20 @@
/*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Anope IRC Services
*
- * Please read COPYING and README for further details.
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
namespace WebCPanel
@@ -13,7 +25,7 @@ class Logout : public WebPanelProtectedPage
public:
Logout(const Anope::string &u);
- bool OnRequest(HTTPProvider *, const Anope::string &, HTTPClient *, HTTPMessage &, HTTPReply &, NickAlias *, TemplateFileServer::Replacements &) anope_override;
+ bool OnRequest(HTTPProvider *, const Anope::string &, HTTPClient *, HTTPMessage &, HTTPReply &, NickServ::Nick *, TemplateFileServer::Replacements &) override;
};
}
diff --git a/modules/webcpanel/pages/memoserv/memos.cpp b/modules/webcpanel/pages/memoserv/memos.cpp
index d4335f749..76f4d676e 100644
--- a/modules/webcpanel/pages/memoserv/memos.cpp
+++ b/modules/webcpanel/pages/memoserv/memos.cpp
@@ -1,8 +1,20 @@
/*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Anope IRC Services
*
- * Please read COPYING and README for further details.
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "../../webcpanel.h"
@@ -11,45 +23,46 @@ WebCPanel::MemoServ::Memos::Memos(const Anope::string &cat, const Anope::string
{
}
-bool WebCPanel::MemoServ::Memos::OnRequest(HTTPProvider *server, const Anope::string &page_name, HTTPClient *client, HTTPMessage &message, HTTPReply &reply, NickAlias *na, TemplateFileServer::Replacements &replacements)
+bool WebCPanel::MemoServ::Memos::OnRequest(HTTPProvider *server, const Anope::string &page_name, HTTPClient *client, HTTPMessage &message, HTTPReply &reply, ::NickServ::Nick *na, TemplateFileServer::Replacements &replacements)
{
const Anope::string &chname = message.get_data["channel"];
- ChannelInfo *ci;
- const MemoInfo *mi;
- Memo *m;
+ ::ChanServ::Channel *ci;
+ ::MemoServ::MemoInfo *mi;
+ ::MemoServ::Memo *m;
- for (registered_channel_map::const_iterator it = RegisteredChannelList->begin(), it_end = RegisteredChannelList->end(); it != it_end; ++it)
- {
- ci = it->second;
-
- if (ci->AccessFor(na->nc).HasPriv("MEMO"))
+ if (::ChanServ::service)
+ for (auto& it : ::ChanServ::service->GetChannels())
{
- replacements["CHANNEL_NAMES"] = ci->name;
- replacements["ESCAPED_CHANNEL_NAMES"] = HTTPUtils::URLEncode(ci->name);
+ ci = it.second;
+
+ if (ci->AccessFor(na->GetAccount()).HasPriv("MEMO"))
+ {
+ replacements["CHANNEL_NAMES"] = ci->GetName();
+ replacements["ESCAPED_CHANNEL_NAMES"] = HTTPUtils::URLEncode(ci->GetName());
+ }
}
- }
if (chname.empty())
{
replacements["MESSAGES"] = "No Channel specified, displaying the memos for your Nick";
- mi = &na->nc->memos;
+ mi = na->GetAccount()->GetMemos();
}
else
{
- ci = ChannelInfo::Find(chname);
+ ci = ::ChanServ::Find(chname);
if (ci)
{
replacements["MESSAGES"] = "Displaying the memos for " + chname + ".";
- mi = &ci->memos;
-
- replacements["CHANNEL_NAME"] = ci->name;
- replacements["ESCAPED_CHANNEL_NAME"] = HTTPUtils::URLEncode(ci->name);
+ mi = ci->GetMemos();
}
else
{
replacements["MESSAGES"] = "Channel " + chname + " not found, displaying the memos for your nick";
- mi = &na->nc->memos;
+ mi = na->GetAccount()->GetMemos();
}
+
+ replacements["CHANNEL_NAME"] = ci->GetName();
+ replacements["ESCAPED_CHANNEL_NAME"] = HTTPUtils::URLEncode(ci->GetName());
}
if (message.post_data.count("receiver") > 0 && message.post_data.count("message") > 0)
{
@@ -57,7 +70,7 @@ bool WebCPanel::MemoServ::Memos::OnRequest(HTTPProvider *server, const Anope::st
params.push_back(HTTPUtils::URLDecode(message.post_data["receiver"]));
params.push_back(HTTPUtils::URLDecode(message.post_data["message"]));
- WebPanel::RunCommand(na->nc->display, na->nc, "MemoServ", "memoserv/send", params, replacements, "CMDR");
+ WebPanel::RunCommand(na->GetAccount()->GetDisplay(), na->GetAccount(), "MemoServ", "memoserv/send", params, replacements, "CMDR");
}
if (message.get_data.count("del") > 0 && message.get_data.count("number") > 0)
{
@@ -66,7 +79,7 @@ bool WebCPanel::MemoServ::Memos::OnRequest(HTTPProvider *server, const Anope::st
params.push_back(chname);
params.push_back(message.get_data["number"]);
- WebPanel::RunCommand(na->nc->display, na->nc, "MemoServ", "memoserv/del", params, replacements, "CMDR");
+ WebPanel::RunCommand(na->GetAccount()->GetDisplay(), na->GetAccount(), "MemoServ", "memoserv/del", params, replacements, "CMDR");
}
if (message.get_data.count("read") > 0 && message.get_data.count("number") > 0)
{
@@ -84,29 +97,30 @@ bool WebCPanel::MemoServ::Memos::OnRequest(HTTPProvider *server, const Anope::st
if (number > 0)
{
- m = mi->GetMemo(number-1);
+ m = mi ? mi->GetMemo(number-1) : nullptr;
if (!m)
replacements["MESSAGES"] = "ERROR - invalid memo number.";
else if (message.get_data["read"] == "1")
- m->unread = false;
+ m->SetUnread(false);
else if (message.get_data["read"] == "2")
- m->unread = true;
+ m->SetUnread(true);
}
}
- for (unsigned i = 0; i < mi->memos->size(); ++i)
- {
- m = mi->GetMemo(i);
- replacements["NUMBER"] = stringify(i+1);
- replacements["SENDER"] = m->sender;
- replacements["TIME"] = Anope::strftime(m->time);
- replacements["TEXT"] = HTTPUtils::Escape(m->text);
- if (m->unread)
- replacements["UNREAD"] = "YES";
- else
- replacements["UNREAD"] = "NO";
- }
+ if (mi)
+ for (unsigned i = 0; i < mi->GetMemos().size(); ++i)
+ {
+ m = mi->GetMemo(i);
+ replacements["NUMBER"] = stringify(i+1);
+ replacements["SENDER"] = m->GetSender();
+ replacements["TIME"] = Anope::strftime(m->GetTime());
+ replacements["TEXT"] = HTTPUtils::Escape(m->GetText());
+ if (m->GetUnread())
+ replacements["UNREAD"] = "YES";
+ else
+ replacements["UNREAD"] = "NO";
+ }
TemplateFileServer page("memoserv/memos.html");
page.Serve(server, page_name, client, message, reply, replacements);
diff --git a/modules/webcpanel/pages/memoserv/memos.h b/modules/webcpanel/pages/memoserv/memos.h
index edab4fa20..68da44ca9 100644
--- a/modules/webcpanel/pages/memoserv/memos.h
+++ b/modules/webcpanel/pages/memoserv/memos.h
@@ -1,8 +1,20 @@
/*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Anope IRC Services
*
- * Please read COPYING and README for further details.
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
namespace WebCPanel
@@ -16,7 +28,7 @@ class Memos : public WebPanelProtectedPage
public:
Memos(const Anope::string &cat, const Anope::string &u);
- bool OnRequest(HTTPProvider *, const Anope::string &, HTTPClient *, HTTPMessage &, HTTPReply &, NickAlias *, TemplateFileServer::Replacements &) anope_override;
+ bool OnRequest(HTTPProvider *, const Anope::string &, HTTPClient *, HTTPMessage &, HTTPReply &, ::NickServ::Nick *, TemplateFileServer::Replacements &) override;
};
}
diff --git a/modules/webcpanel/pages/nickserv/access.cpp b/modules/webcpanel/pages/nickserv/access.cpp
index 9a0add123..fa9c650f2 100644
--- a/modules/webcpanel/pages/nickserv/access.cpp
+++ b/modules/webcpanel/pages/nickserv/access.cpp
@@ -1,17 +1,30 @@
/*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Anope IRC Services
*
- * Please read COPYING and README for further details.
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "../../webcpanel.h"
+#include "modules/nickserv/access.h"
WebCPanel::NickServ::Access::Access(const Anope::string &cat, const Anope::string &u) : WebPanelProtectedPage(cat, u)
{
}
-bool WebCPanel::NickServ::Access::OnRequest(HTTPProvider *server, const Anope::string &page_name, HTTPClient *client, HTTPMessage &message, HTTPReply &reply, NickAlias *na, TemplateFileServer::Replacements &replacements)
+bool WebCPanel::NickServ::Access::OnRequest(HTTPProvider *server, const Anope::string &page_name, HTTPClient *client, HTTPMessage &message, HTTPReply &reply, ::NickServ::Nick *na, TemplateFileServer::Replacements &replacements)
{
if (message.post_data.count("access") > 0)
{
@@ -19,7 +32,7 @@ bool WebCPanel::NickServ::Access::OnRequest(HTTPProvider *server, const Anope::s
params.push_back("ADD");
params.push_back(message.post_data["access"]);
- WebPanel::RunCommand(na->nc->display, na->nc, "NickServ", "nickserv/access", params, replacements);
+ WebPanel::RunCommand(na->GetAccount()->GetDisplay(), na->GetAccount(), "NickServ", "nickserv/access", params, replacements);
}
else if (message.get_data.count("del") > 0 && message.get_data.count("mask") > 0)
{
@@ -27,11 +40,11 @@ bool WebCPanel::NickServ::Access::OnRequest(HTTPProvider *server, const Anope::s
params.push_back("DEL");
params.push_back(message.get_data["mask"]);
- WebPanel::RunCommand(na->nc->display, na->nc, "NickServ", "nickserv/access", params, replacements);
+ WebPanel::RunCommand(na->GetAccount()->GetDisplay(), na->GetAccount(), "NickServ", "nickserv/access", params, replacements);
}
- for (unsigned i = 0; i < na->nc->access.size(); ++i)
- replacements["ACCESS"] = na->nc->access[i];
+ for (NickAccess *a : na->GetAccount()->GetRefs<NickAccess *>())
+ replacements["ACCESS"] = a->GetMask();
TemplateFileServer page("nickserv/access.html");
page.Serve(server, page_name, client, message, reply, replacements);
diff --git a/modules/webcpanel/pages/nickserv/access.h b/modules/webcpanel/pages/nickserv/access.h
index 3bc2ff6ec..885671600 100644
--- a/modules/webcpanel/pages/nickserv/access.h
+++ b/modules/webcpanel/pages/nickserv/access.h
@@ -1,8 +1,20 @@
/*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Anope IRC Services
*
- * Please read COPYING and README for further details.
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
namespace WebCPanel
@@ -16,7 +28,7 @@ class Access : public WebPanelProtectedPage
public:
Access(const Anope::string &cat, const Anope::string &u);
- bool OnRequest(HTTPProvider *, const Anope::string &, HTTPClient *, HTTPMessage &, HTTPReply &, NickAlias *, TemplateFileServer::Replacements &) anope_override;
+ bool OnRequest(HTTPProvider *, const Anope::string &, HTTPClient *, HTTPMessage &, HTTPReply &, ::NickServ::Nick *, TemplateFileServer::Replacements &) override;
};
}
diff --git a/modules/webcpanel/pages/nickserv/alist.cpp b/modules/webcpanel/pages/nickserv/alist.cpp
index d5b331f4f..f950cf63a 100644
--- a/modules/webcpanel/pages/nickserv/alist.cpp
+++ b/modules/webcpanel/pages/nickserv/alist.cpp
@@ -1,54 +1,64 @@
/*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Anope IRC Services
*
- * Please read COPYING and README for further details.
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "../../webcpanel.h"
-static bool ChannelSort(ChannelInfo *ci1, ChannelInfo *ci2)
+static bool ChannelSort(ChanServ::Channel *ci1, ChanServ::Channel *ci2)
{
- return ci::less()(ci1->name, ci2->name);
+ return ci::less()(ci1->GetName(), ci2->GetName());
}
WebCPanel::NickServ::Alist::Alist(const Anope::string &cat, const Anope::string &u) : WebPanelProtectedPage(cat, u)
{
}
-bool WebCPanel::NickServ::Alist::OnRequest(HTTPProvider *server, const Anope::string &page_name, HTTPClient *client, HTTPMessage &message, HTTPReply &reply, NickAlias *na, TemplateFileServer::Replacements &replacements)
+bool WebCPanel::NickServ::Alist::OnRequest(HTTPProvider *server, const Anope::string &page_name, HTTPClient *client, HTTPMessage &message, HTTPReply &reply, ::NickServ::Nick *na, TemplateFileServer::Replacements &replacements)
{
- std::deque<ChannelInfo *> queue;
- na->nc->GetChannelReferences(queue);
- std::sort(queue.begin(), queue.end(), ChannelSort);
+ std::vector<::ChanServ::Channel *> chans = na->GetAccount()->GetRefs<::ChanServ::Channel *>();
+ std::sort(chans.begin(), chans.end(), ChannelSort);
int chan_count = 0;
- for (unsigned q = 0; q < queue.size(); ++q)
+ for (::ChanServ::Channel *ci : chans)
{
- ChannelInfo *ci = queue[q];
-
- if (ci->GetFounder() == na->nc)
+ if (ci->GetFounder() == na->GetAccount())
{
++chan_count;
replacements["NUMBERS"] = stringify(chan_count);
- replacements["CHANNELS"] = (ci->HasExt("CS_NO_EXPIRE") ? "!" : "") + ci->name;
+ replacements["CHANNELS"] = (ci->HasFieldS("CS_NO_EXPIRE") ? "!" : "") + ci->GetName();
replacements["ACCESSES"] = "Founder";
continue;
}
- AccessGroup access = ci->AccessFor(na->nc);
+ ::ChanServ::AccessGroup access = ci->AccessFor(na->GetAccount());
if (access.empty())
continue;
-
+
++chan_count;
replacements["NUMBERS"] = stringify(chan_count);
- replacements["CHANNELS"] = (ci->HasExt("CS_NO_EXPIRE") ? "!" : "") + ci->name;
-
- const ChanAccess *highest = access.Highest();
- replacements["ACCESSES"] = highest ? highest->AccessSerialize() : "";
+ replacements["CHANNELS"] = (ci->HasFieldS("CS_NO_EXPIRE") ? "!" : "") + ci->GetName();
+ Anope::string access_str;
+ for (unsigned i = 0; i < access.size(); ++i)
+ access_str += ", " + access[i]->AccessSerialize();
+ replacements["ACCESSES"] = access_str.substr(2);
}
TemplateFileServer page("nickserv/alist.html");
diff --git a/modules/webcpanel/pages/nickserv/alist.h b/modules/webcpanel/pages/nickserv/alist.h
index fa0d7ea0b..096c27be1 100644
--- a/modules/webcpanel/pages/nickserv/alist.h
+++ b/modules/webcpanel/pages/nickserv/alist.h
@@ -1,8 +1,20 @@
/*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Anope IRC Services
*
- * Please read COPYING and README for further details.
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
namespace WebCPanel
@@ -16,7 +28,7 @@ class Alist : public WebPanelProtectedPage
public:
Alist(const Anope::string &cat, const Anope::string &u);
- bool OnRequest(HTTPProvider *, const Anope::string &, HTTPClient *, HTTPMessage &, HTTPReply &, NickAlias *, TemplateFileServer::Replacements &) anope_override;
+ bool OnRequest(HTTPProvider *, const Anope::string &, HTTPClient *, HTTPMessage &, HTTPReply &, ::NickServ::Nick *, TemplateFileServer::Replacements &) override;
};
}
diff --git a/modules/webcpanel/pages/nickserv/cert.cpp b/modules/webcpanel/pages/nickserv/cert.cpp
index 94b93c69a..16659e660 100644
--- a/modules/webcpanel/pages/nickserv/cert.cpp
+++ b/modules/webcpanel/pages/nickserv/cert.cpp
@@ -1,18 +1,30 @@
/*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Anope IRC Services
*
- * Please read COPYING and README for further details.
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "../../webcpanel.h"
-#include "modules/ns_cert.h"
+#include "modules/nickserv/cert.h"
WebCPanel::NickServ::Cert::Cert(const Anope::string &cat, const Anope::string &u) : WebPanelProtectedPage(cat, u)
{
}
-bool WebCPanel::NickServ::Cert::OnRequest(HTTPProvider *server, const Anope::string &page_name, HTTPClient *client, HTTPMessage &message, HTTPReply &reply, NickAlias *na, TemplateFileServer::Replacements &replacements)
+bool WebCPanel::NickServ::Cert::OnRequest(HTTPProvider *server, const Anope::string &page_name, HTTPClient *client, HTTPMessage &message, HTTPReply &reply, ::NickServ::Nick *na, TemplateFileServer::Replacements &replacements)
{
if (message.post_data.count("certfp") > 0)
{
@@ -20,7 +32,7 @@ bool WebCPanel::NickServ::Cert::OnRequest(HTTPProvider *server, const Anope::str
params.push_back("ADD");
params.push_back(message.post_data["certfp"]);
- WebPanel::RunCommand(na->nc->display, na->nc, "NickServ", "nickserv/cert", params, replacements);
+ WebPanel::RunCommand(na->GetAccount()->GetDisplay(), na->GetAccount(), "NickServ", "nickserv/cert", params, replacements);
}
else if (message.get_data.count("del") > 0 && message.get_data.count("mask") > 0)
{
@@ -28,13 +40,12 @@ bool WebCPanel::NickServ::Cert::OnRequest(HTTPProvider *server, const Anope::str
params.push_back("DEL");
params.push_back(message.get_data["mask"]);
- WebPanel::RunCommand(na->nc->display, na->nc, "NickServ", "nickserv/cert", params, replacements);
+ WebPanel::RunCommand(na->GetAccount()->GetDisplay(), na->GetAccount(), "NickServ", "nickserv/cert", params, replacements);
}
- NSCertList *cl = na->nc->GetExt<NSCertList>("certificates");
- if (cl)
- for (unsigned i = 0; i < cl->GetCertCount(); ++i)
- replacements["CERTS"] = cl->GetCert(i);
+ std::vector<NSCertEntry *> cl = na->GetAccount()->GetRefs<NSCertEntry *>();
+ for (NSCertEntry *e : cl)
+ replacements["CERTS"] = e->GetCert();
TemplateFileServer page("nickserv/cert.html");
page.Serve(server, page_name, client, message, reply, replacements);
diff --git a/modules/webcpanel/pages/nickserv/cert.h b/modules/webcpanel/pages/nickserv/cert.h
index 7fbd54e20..8dc5adad1 100644
--- a/modules/webcpanel/pages/nickserv/cert.h
+++ b/modules/webcpanel/pages/nickserv/cert.h
@@ -1,8 +1,20 @@
/*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Anope IRC Services
*
- * Please read COPYING and README for further details.
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
namespace WebCPanel
@@ -16,7 +28,7 @@ class Cert : public WebPanelProtectedPage
public:
Cert(const Anope::string &cat, const Anope::string &u);
- bool OnRequest(HTTPProvider *, const Anope::string &, HTTPClient *, HTTPMessage &, HTTPReply &, NickAlias *, TemplateFileServer::Replacements &) anope_override;
+ bool OnRequest(HTTPProvider *, const Anope::string &, HTTPClient *, HTTPMessage &, HTTPReply &, ::NickServ::Nick *, TemplateFileServer::Replacements &) override;
};
}
diff --git a/modules/webcpanel/pages/nickserv/info.cpp b/modules/webcpanel/pages/nickserv/info.cpp
index b1c1e2afa..f3c353044 100644
--- a/modules/webcpanel/pages/nickserv/info.cpp
+++ b/modules/webcpanel/pages/nickserv/info.cpp
@@ -1,8 +1,20 @@
/*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Anope IRC Services
*
- * Please read COPYING and README for further details.
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "../../webcpanel.h"
@@ -11,83 +23,83 @@ WebCPanel::NickServ::Info::Info(const Anope::string &cat, const Anope::string &u
{
}
-bool WebCPanel::NickServ::Info::OnRequest(HTTPProvider *server, const Anope::string &page_name, HTTPClient *client, HTTPMessage &message, HTTPReply &reply, NickAlias *na, TemplateFileServer::Replacements &replacements)
+bool WebCPanel::NickServ::Info::OnRequest(HTTPProvider *server, const Anope::string &page_name, HTTPClient *client, HTTPMessage &message, HTTPReply &reply, ::NickServ::Nick *na, TemplateFileServer::Replacements &replacements)
{
if (message.post_data.empty() == false)
{
if (message.post_data.count("email") > 0)
{
- if (message.post_data["email"] != na->nc->email)
+ if (message.post_data["email"] != na->GetAccount()->GetEmail())
{
if (!message.post_data["email"].empty() && !Mail::Validate(message.post_data["email"]))
replacements["ERRORS"] = "Invalid email";
else
{
- na->nc->email = message.post_data["email"];
+ na->GetAccount()->SetEmail(message.post_data["email"]);
replacements["MESSAGES"] = "Email updated";
}
}
}
if (message.post_data.count("greet") > 0)
{
- Anope::string *greet = na->nc->GetExt<Anope::string>("greet");
+ Anope::string *greet = na->GetAccount()->GetExt<Anope::string>("greet");
const Anope::string &post_greet = HTTPUtils::URLDecode(message.post_data["greet"].replace_all_cs("+", " "));
if (post_greet.empty())
- na->nc->Shrink<Anope::string>("greet");
+ na->GetAccount()->Shrink<Anope::string>("greet");
else if (!greet || post_greet != *greet)
- na->nc->Extend<Anope::string>("greet", post_greet);
+ na->GetAccount()->Extend<Anope::string>("greet", post_greet);
replacements["MESSAGES"] = "Greet updated";
}
- if (na->nc->HasExt("AUTOOP") != message.post_data.count("autoop"))
+ if (na->GetAccount()->HasFieldS("AUTOOP") != message.post_data.count("autoop"))
{
- if (!na->nc->HasExt("AUTOOP"))
- na->nc->Extend<bool>("AUTOOP");
+ if (!na->GetAccount()->HasFieldS("AUTOOP"))
+ na->GetAccount()->SetS<bool>("AUTOOP", true);
else
- na->nc->Shrink<bool>("AUTOOP");
+ na->GetAccount()->UnsetS<bool>("AUTOOP");
replacements["MESSAGES"] = "Autoop updated";
}
- if (na->nc->HasExt("NS_PRIVATE") != message.post_data.count("private"))
+ if (na->GetAccount()->HasFieldS("NS_PRIVATE") != message.post_data.count("private"))
{
- if (!na->nc->HasExt("NS_PRIVATE"))
- na->nc->Extend<bool>("NS_PRIVATE");
+ if (!na->GetAccount()->HasFieldS("NS_PRIVATE"))
+ na->GetAccount()->SetS<bool>("NS_PRIVATE", true);
else
- na->nc->Shrink<bool>("NS_PRIVATE");
+ na->GetAccount()->UnsetS<bool>("NS_PRIVATE");
replacements["MESSAGES"] = "Private updated";
}
- if (na->nc->HasExt("NS_SECURE") != message.post_data.count("secure"))
+ if (na->GetAccount()->HasFieldS("NS_SECURE") != message.post_data.count("secure"))
{
- if (!na->nc->HasExt("NS_SECURE"))
- na->nc->Extend<bool>("NS_SECURE");
+ if (!na->GetAccount()->HasFieldS("NS_SECURE"))
+ na->GetAccount()->SetS<bool>("NS_SECURE", true);
else
- na->nc->Shrink<bool>("NS_SECURE");
+ na->GetAccount()->UnsetS<bool>("NS_SECURE");
replacements["MESSAGES"] = "Secure updated";
}
- if (message.post_data["kill"] == "on" && !na->nc->HasExt("KILLPROTECT"))
+ if (message.post_data["kill"] == "on" && !na->GetAccount()->HasFieldS("KILLPROTECT"))
{
- na->nc->Extend<bool>("KILLPROTECT");
- na->nc->Shrink<bool>("KILL_QUICK");
+ na->GetAccount()->SetS<bool>("KILLPROTECT", true);
+ na->GetAccount()->UnsetS<bool>("KILL_QUICK");
replacements["MESSAGES"] = "Kill updated";
}
- else if (message.post_data["kill"] == "quick" && !na->nc->HasExt("KILL_QUICK"))
+ else if (message.post_data["kill"] == "quick" && !na->GetAccount()->HasFieldS("KILL_QUICK"))
{
- na->nc->Shrink<bool>("KILLPROTECT");
- na->nc->Extend<bool>("KILL_QUICK");
+ na->GetAccount()->UnsetS<bool>("KILLPROTECT");
+ na->GetAccount()->SetS<bool>("KILL_QUICK", true);
replacements["MESSAGES"] = "Kill updated";
}
- else if (message.post_data["kill"] == "off" && (na->nc->HasExt("KILLPROTECT") || na->nc->HasExt("KILL_QUICK")))
+ else if (message.post_data["kill"] == "off" && (na->GetAccount()->HasFieldS("KILLPROTECT") || na->GetAccount()->HasFieldS("KILL_QUICK")))
{
- na->nc->Shrink<bool>("KILLPROTECT");
- na->nc->Shrink<bool>("KILL_QUICK");
+ na->GetAccount()->UnsetS<bool>("KILLPROTECT");
+ na->GetAccount()->UnsetS<bool>("KILL_QUICK");
replacements["MESSAGES"] = "Kill updated";
}
}
- replacements["DISPLAY"] = HTTPUtils::Escape(na->nc->display);
- if (na->nc->email.empty() == false)
- replacements["EMAIL"] = HTTPUtils::Escape(na->nc->email);
- replacements["TIME_REGISTERED"] = Anope::strftime(na->time_registered, na->nc);
+ replacements["DISPLAY"] = HTTPUtils::Escape(na->GetAccount()->GetDisplay());
+ if (na->GetAccount()->GetEmail().empty() == false)
+ replacements["EMAIL"] = HTTPUtils::Escape(na->GetAccount()->GetEmail());
+ replacements["TIME_REGISTERED"] = Anope::strftime(na->GetTimeRegistered(), na->GetAccount());
if (na->HasVhost())
{
if (na->GetVhostIdent().empty() == false)
@@ -95,22 +107,22 @@ bool WebCPanel::NickServ::Info::OnRequest(HTTPProvider *server, const Anope::str
else
replacements["VHOST"] = na->GetVhostHost();
}
- Anope::string *greet = na->nc->GetExt<Anope::string>("greet");
+ Anope::string *greet = na->GetAccount()->GetExt<Anope::string>("greet");
if (greet)
replacements["GREET"] = HTTPUtils::Escape(*greet);
- if (na->nc->HasExt("AUTOOP"))
+ if (na->GetAccount()->HasFieldS("AUTOOP"))
replacements["AUTOOP"];
- if (na->nc->HasExt("NS_PRIVATE"))
+ if (na->GetAccount()->HasFieldS("NS_PRIVATE"))
replacements["PRIVATE"];
- if (na->nc->HasExt("NS_SECURE"))
+ if (na->GetAccount()->HasFieldS("NS_SECURE"))
replacements["SECURE"];
- if (na->nc->HasExt("KILLPROTECT"))
+ if (na->GetAccount()->HasFieldS("KILLPROTECT"))
replacements["KILL_ON"];
- if (na->nc->HasExt("KILL_QUICK"))
+ if (na->GetAccount()->HasFieldS("KILL_QUICK"))
replacements["KILL_QUICK"];
- if (!na->nc->HasExt("KILLPROTECT") && !na->nc->HasExt("KILL_QUICK"))
+ if (!na->GetAccount()->HasFieldS("KILLPROTECT") && !na->GetAccount()->HasFieldS("KILL_QUICK"))
replacements["KILL_OFF"];
-
+
TemplateFileServer page("nickserv/info.html");
page.Serve(server, page_name, client, message, reply, replacements);
return true;
diff --git a/modules/webcpanel/pages/nickserv/info.h b/modules/webcpanel/pages/nickserv/info.h
index fd4cb2a3c..09ae4d3ee 100644
--- a/modules/webcpanel/pages/nickserv/info.h
+++ b/modules/webcpanel/pages/nickserv/info.h
@@ -1,8 +1,20 @@
/*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Anope IRC Services
*
- * Please read COPYING and README for further details.
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
namespace WebCPanel
@@ -16,7 +28,7 @@ class Info : public WebPanelProtectedPage
public:
Info(const Anope::string &cat, const Anope::string &u);
- bool OnRequest(HTTPProvider *, const Anope::string &, HTTPClient *, HTTPMessage &, HTTPReply &, NickAlias *, TemplateFileServer::Replacements &) anope_override;
+ bool OnRequest(HTTPProvider *, const Anope::string &, HTTPClient *, HTTPMessage &, HTTPReply &, ::NickServ::Nick *, TemplateFileServer::Replacements &) override;
};
}
diff --git a/modules/webcpanel/pages/operserv/akill.cpp b/modules/webcpanel/pages/operserv/akill.cpp
index bc09c4216..a4cdb26be 100644
--- a/modules/webcpanel/pages/operserv/akill.cpp
+++ b/modules/webcpanel/pages/operserv/akill.cpp
@@ -1,28 +1,38 @@
/*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Anope IRC Services
*
- * Please read COPYING and README for further details.
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "../../webcpanel.h"
-WebCPanel::OperServ::Akill::Akill(const Anope::string &cat, const Anope::string &u) : WebPanelProtectedPage(cat, u)
+WebCPanel::OperServ::Akill::Akill(const Anope::string &cat, const Anope::string &u) : WebPanelProtectedPage(cat, u), akills("xlinemanager/sgline")
{
}
-bool WebCPanel::OperServ::Akill::OnRequest(HTTPProvider *server, const Anope::string &page_name, HTTPClient *client, HTTPMessage &message, HTTPReply &reply, NickAlias *na, TemplateFileServer::Replacements &replacements)
+bool WebCPanel::OperServ::Akill::OnRequest(HTTPProvider *server, const Anope::string &page_name, HTTPClient *client, HTTPMessage &message, HTTPReply &reply, ::NickServ::Nick *na, TemplateFileServer::Replacements &replacements)
{
-
- static ServiceReference<XLineManager> akills("XLineManager","xlinemanager/sgline");
-
- if (!na->nc->o || !na->nc->o->ot->HasCommand("operserv/akill"))
+ if (!na->GetAccount()->o || !na->GetAccount()->o->GetType()->HasCommand("operserv/akill"))
{
replacements["NOACCESS"];
}
else
{
- if (akills->GetCount() == 0)
+ std::vector<XLine *> xlines = akills->GetXLines();
+ if (xlines.empty())
replacements["AKILLS"] = "No Akills to display.";
if (message.post_data.count("mask") > 0 && message.post_data.count("expiry") > 0 && message.post_data.count("reason") > 0)
@@ -34,7 +44,7 @@ bool WebCPanel::OperServ::Akill::OnRequest(HTTPProvider *server, const Anope::st
cmdstr << " " << HTTPUtils::URLDecode(message.post_data["mask"]);
cmdstr << " " << HTTPUtils::URLDecode(message.post_data["reason"]);
params.push_back(cmdstr.str());
- WebPanel::RunCommand(na->nc->display, na->nc, "OperServ", "operserv/akill", params, replacements);
+ WebPanel::RunCommand(na->GetAccount()->GetDisplay(), na->GetAccount(), "OperServ", "operserv/akill", params, replacements);
}
if (message.get_data["del"] == "1" && message.get_data.count("number") > 0)
@@ -42,18 +52,18 @@ bool WebCPanel::OperServ::Akill::OnRequest(HTTPProvider *server, const Anope::st
std::vector<Anope::string> params;
params.push_back("DEL");
params.push_back(HTTPUtils::URLDecode(message.get_data["number"]));
- WebPanel::RunCommand(na->nc->display, na->nc, "OperServ", "operserv/akill", params, replacements);
+ WebPanel::RunCommand(na->GetAccount()->GetDisplay(), na->GetAccount(), "OperServ", "operserv/akill", params, replacements);
}
- for (unsigned i = 0, end = akills->GetCount(); i < end; ++i)
+ unsigned int i = 0;
+ for (XLine *x : xlines)
{
- const XLine *x = akills->GetEntry(i);
- replacements["NUMBER"] = stringify(i + 1);
- replacements["HOST"] = x->mask;
- replacements["SETTER"] = x->by;
- replacements["TIME"] = Anope::strftime(x->created, NULL, true);
- replacements["EXPIRE"] = Anope::Expires(x->expires, na->nc);
- replacements["REASON"] = x->reason;
+ replacements["NUMBER"] = stringify(++i);
+ replacements["HOST"] = x->GetMask();
+ replacements["SETTER"] = x->GetBy();
+ replacements["TIME"] = Anope::strftime(x->GetCreated(), NULL, true);
+ replacements["EXPIRE"] = Anope::Expires(x->GetExpires(), na->GetAccount());
+ replacements["REASON"] = x->GetReason();
}
}
diff --git a/modules/webcpanel/pages/operserv/akill.h b/modules/webcpanel/pages/operserv/akill.h
index 1801a4fbd..76435c450 100644
--- a/modules/webcpanel/pages/operserv/akill.h
+++ b/modules/webcpanel/pages/operserv/akill.h
@@ -1,8 +1,20 @@
/*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Anope IRC Services
*
- * Please read COPYING and README for further details.
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
namespace WebCPanel
@@ -13,10 +25,12 @@ namespace OperServ
class Akill : public WebPanelProtectedPage
{
+ ServiceReference<XLineManager> akills;
+
public:
Akill(const Anope::string &cat, const Anope::string &u);
- bool OnRequest(HTTPProvider *, const Anope::string &, HTTPClient *, HTTPMessage &, HTTPReply &, NickAlias *, TemplateFileServer::Replacements &) anope_override;
+ bool OnRequest(HTTPProvider *, const Anope::string &, HTTPClient *, HTTPMessage &, HTTPReply &, ::NickServ::Nick *, TemplateFileServer::Replacements &) override;
};
}
diff --git a/modules/webcpanel/pages/register.cpp b/modules/webcpanel/pages/register.cpp
index 7da8b37c3..c662397ab 100644
--- a/modules/webcpanel/pages/register.cpp
+++ b/modules/webcpanel/pages/register.cpp
@@ -1,8 +1,20 @@
/*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Anope IRC Services
*
- * Please read COPYING and README for further details.
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "../webcpanel.h"
diff --git a/modules/webcpanel/pages/register.h b/modules/webcpanel/pages/register.h
index 2daff1b74..fec14058f 100644
--- a/modules/webcpanel/pages/register.h
+++ b/modules/webcpanel/pages/register.h
@@ -1,8 +1,20 @@
/*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Anope IRC Services
*
- * Please read COPYING and README for further details.
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "modules/httpd.h"
@@ -15,7 +27,7 @@ class Register : public WebPanelPage
public:
Register(const Anope::string &u) : WebPanelPage(u) { }
- bool OnRequest(HTTPProvider *, const Anope::string &, HTTPClient *, HTTPMessage &, HTTPReply &) anope_override;
+ bool OnRequest(HTTPProvider *, const Anope::string &, HTTPClient *, HTTPMessage &, HTTPReply &) override;
};
}
diff --git a/modules/webcpanel/static_fileserver.cpp b/modules/webcpanel/static_fileserver.cpp
index 47f191b46..858336c6c 100644
--- a/modules/webcpanel/static_fileserver.cpp
+++ b/modules/webcpanel/static_fileserver.cpp
@@ -1,8 +1,20 @@
/*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Anope IRC Services
*
- * Please read COPYING and README for further details.
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "webcpanel.h"
@@ -35,7 +47,7 @@ bool StaticFileServer::OnRequest(HTTPProvider *server, const Anope::string &page
char buffer[BUFSIZE];
while ((i = read(fd, buffer, sizeof(buffer))) > 0)
reply.Write(buffer, i);
-
+
close(fd);
return true;
}
diff --git a/modules/webcpanel/static_fileserver.h b/modules/webcpanel/static_fileserver.h
index 2e2ec3f5c..63d041301 100644
--- a/modules/webcpanel/static_fileserver.h
+++ b/modules/webcpanel/static_fileserver.h
@@ -1,8 +1,20 @@
/*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Anope IRC Services
*
- * Please read COPYING and README for further details.
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "modules/httpd.h"
@@ -14,6 +26,6 @@ class StaticFileServer : public HTTPPage
public:
StaticFileServer(const Anope::string &f_n, const Anope::string &u, const Anope::string &c_t);
- bool OnRequest(HTTPProvider *, const Anope::string &, HTTPClient *, HTTPMessage &, HTTPReply &) anope_override;
+ bool OnRequest(HTTPProvider *, const Anope::string &, HTTPClient *, HTTPMessage &, HTTPReply &) override;
};
diff --git a/modules/webcpanel/template_fileserver.cpp b/modules/webcpanel/template_fileserver.cpp
index 7436265d1..81c4c4a5f 100644
--- a/modules/webcpanel/template_fileserver.cpp
+++ b/modules/webcpanel/template_fileserver.cpp
@@ -1,8 +1,20 @@
/*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Anope IRC Services
*
- * Please read COPYING and README for further details.
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "webcpanel.h"
@@ -79,7 +91,7 @@ static Anope::string FindReplacement(const TemplateFileServer::Replacements &r,
}
}
}
-
+
TemplateFileServer::Replacements::const_iterator it = r.find(key);
if (it != r.end())
return it->second;
@@ -110,7 +122,7 @@ void TemplateFileServer::Serve(HTTPProvider *server, const Anope::string &page_n
buffer[i] = 0;
buf += buffer;
}
-
+
close(fd);
Anope::string finished;
@@ -235,7 +247,7 @@ void TemplateFileServer::Serve(HTTPProvider *server, const Anope::string &page_n
// If the if stack is empty or we are in a true statement
bool ifok = IfStack.empty() || IfStack.top();
bool forok = ForLoop::Stack.empty() || !ForLoop::Stack.back().finished(r);
-
+
if (ifok && forok)
{
const Anope::string &replacement = FindReplacement(r, content.substr(0, f - 1));
@@ -252,7 +264,7 @@ void TemplateFileServer::Serve(HTTPProvider *server, const Anope::string &page_n
// If the if stack is empty or we are in a true statement
bool ifok = IfStack.empty() || IfStack.top();
bool forok = ForLoop::Stack.empty() || !ForLoop::Stack.back().finished(r);
-
+
if (ifok && forok)
finished += buf[j];
}
diff --git a/modules/webcpanel/template_fileserver.h b/modules/webcpanel/template_fileserver.h
index 84be3c71f..5f381d70a 100644
--- a/modules/webcpanel/template_fileserver.h
+++ b/modules/webcpanel/template_fileserver.h
@@ -1,8 +1,20 @@
/*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Anope IRC Services
*
- * Please read COPYING and README for further details.
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "modules/httpd.h"
diff --git a/modules/webcpanel/templates/default/confirm.html b/modules/webcpanel/templates/default/confirm.html
index 652dbd116..871f42118 100644
--- a/modules/webcpanel/templates/default/confirm.html
+++ b/modules/webcpanel/templates/default/confirm.html
@@ -39,7 +39,7 @@
<div class="row">
<div class="col-lg-offset-3 col-lg-6">
<div class="footer text-center">
- Anope IRC Services - &copy; 2013-2016 Anope Team - <a href="http://anope.org">http://anope.org</a>
+ Anope IRC Services - &copy; 2013-2014 Anope Team - <a href="http://anope.org">http://anope.org</a>
</div>
</div>
</div>
diff --git a/modules/webcpanel/templates/default/footer.html b/modules/webcpanel/templates/default/footer.html
index fcaaaeb6e..0c6878921 100644
--- a/modules/webcpanel/templates/default/footer.html
+++ b/modules/webcpanel/templates/default/footer.html
@@ -5,7 +5,7 @@
<div class="row">
<div class="col-lg-12">
<div class="footer text-center">
- Anope IRC Services - &copy; 2013-2016 Anope Team - <a href="http://anope.org">http://anope.org</a>
+ Anope IRC Services - &copy; 2013-2014 Anope Team - <a href="http://anope.org">http://anope.org</a>
</div>
</div>
</div>
diff --git a/modules/webcpanel/templates/default/login.html b/modules/webcpanel/templates/default/login.html
index 0916721a2..16e0684fa 100644
--- a/modules/webcpanel/templates/default/login.html
+++ b/modules/webcpanel/templates/default/login.html
@@ -43,7 +43,7 @@
<div class="row">
<div class="col-lg-offset-3 col-lg-6">
<div class="footer text-center">
- Anope IRC Services - &copy; 2013-2016 Anope Team - <a href="http://anope.org">http://anope.org</a>
+ Anope IRC Services - &copy; 2013-2014 Anope Team - <a href="http://anope.org">http://anope.org</a>
</div>
</div>
</div>
diff --git a/modules/webcpanel/templates/default/register.html b/modules/webcpanel/templates/default/register.html
index 6cee78a70..0cf8e2543 100644
--- a/modules/webcpanel/templates/default/register.html
+++ b/modules/webcpanel/templates/default/register.html
@@ -53,7 +53,7 @@
<div class="row">
<div class="col-lg-offset-3 col-lg-6">
<div class="footer text-center">
- Anope IRC Services - &copy; 2013-2016 Anope Team - <a href="http://anope.org">http://anope.org</a>
+ Anope IRC Services - &copy; 2013-2014 Anope Team - <a href="http://anope.org">http://anope.org</a>
</div>
</div>
</div>
diff --git a/modules/webcpanel/webcpanel.cpp b/modules/webcpanel/webcpanel.cpp
index 4f0bc009a..9da3cc70e 100644
--- a/modules/webcpanel/webcpanel.cpp
+++ b/modules/webcpanel/webcpanel.cpp
@@ -1,8 +1,20 @@
/*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Anope IRC Services
*
- * Please read COPYING and README for further details.
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "webcpanel.h"
@@ -14,7 +26,7 @@ class ModuleWebCPanel : public Module
{
ServiceReference<HTTPProvider> provider;
Panel panel;
- PrimitiveExtensibleItem<Anope::string> id, ip;
+ ExtensibleItem<Anope::string> id, ip;
StaticFileServer style_css, logo_png, cubes_png, favicon_ico;
@@ -43,25 +55,42 @@ class ModuleWebCPanel : public Module
public:
- ModuleWebCPanel(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, EXTRA | VENDOR),
- panel(this, "webcpanel"), id(this, "webcpanel_id"), ip(this, "webcpanel_ip"),
- style_css("style.css", "/static/style.css", "text/css"), logo_png("logo.png", "/static/logo.png", "image/png"), cubes_png("cubes.png", "/static/cubes.png", "image/png"), favicon_ico("favicon.ico", "/favicon.ico", "image/x-icon"),
- index("/"), logout("/logout"), _register("/register"), confirm("/confirm"),
- nickserv_info("NickServ", "/nickserv/info"), nickserv_cert("NickServ", "/nickserv/cert"), nickserv_access("NickServ", "/nickserv/access"), nickserv_alist("NickServ", "/nickserv/alist"),
- chanserv_info("ChanServ", "/chanserv/info"), chanserv_set("ChanServ", "/chanserv/set"), chanserv_access("ChanServ", "/chanserv/access"), chanserv_akick("ChanServ", "/chanserv/akick"),
- chanserv_modes("ChanServ", "/chanserv/modes"), chanserv_drop("ChanServ", "/chanserv/drop"), memoserv_memos("MemoServ", "/memoserv/memos"), hostserv_request("HostServ", "/hostserv/request"),
- operserv_akill("OperServ", "/operserv/akill")
+ ModuleWebCPanel(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, EXTRA | VENDOR)
+ , panel(this, "webcpanel")
+ , id(this, "webcpanel_id")
+ , ip(this, "webcpanel_ip")
+ , style_css("style.css", "/static/style.css", "text/css")
+ , logo_png("logo.png", "/static/logo.png", "image/png")
+ , cubes_png("cubes.png", "/static/cubes.png", "image/png")
+ , favicon_ico("favicon.ico", "/favicon.ico", "image/x-icon")
+ , index("/")
+ , logout("/logout")
+ , _register("/register")
+ , confirm("/confirm")
+ , nickserv_info("NickServ", "/nickserv/info")
+ , nickserv_cert("NickServ", "/nickserv/cert")
+ , nickserv_access("NickServ", "/nickserv/access")
+ , nickserv_alist("NickServ", "/nickserv/alist")
+ , chanserv_info("ChanServ", "/chanserv/info")
+ , chanserv_set("ChanServ", "/chanserv/set")
+ , chanserv_access("ChanServ", "/chanserv/access")
+ , chanserv_akick("ChanServ", "/chanserv/akick")
+ , chanserv_modes("ChanServ", "/chanserv/modes")
+ , chanserv_drop("ChanServ", "/chanserv/drop")
+ , memoserv_memos("MemoServ", "/memoserv/memos")
+ , hostserv_request("HostServ", "/hostserv/request")
+ , operserv_akill("OperServ", "/operserv/akill")
{
me = this;
Configuration::Block *block = Config->GetModule(this);
- provider_name = block->Get<const Anope::string>("server", "httpd/main");
- template_name = block->Get<const Anope::string>("template", "default");
+ provider_name = block->Get<Anope::string>("server", "httpd/main");
+ template_name = block->Get<Anope::string>("template", "default");
template_base = Anope::DataDir + "/modules/webcpanel/templates/" + template_name;
- page_title = block->Get<const Anope::string>("title", "Anope IRC Services");
+ page_title = block->Get<Anope::string>("title", "Anope IRC Services");
- provider = ServiceReference<HTTPProvider>("HTTPProvider", provider_name);
+ provider = ServiceReference<HTTPProvider>(provider_name);
if (!provider)
throw ModuleException("Unable to find HTTPD provider. Is m_httpd loaded?");
@@ -75,7 +104,7 @@ class ModuleWebCPanel : public Module
provider->RegisterPage(&this->_register);
provider->RegisterPage(&this->confirm);
- BotInfo *NickServ = Config->GetClient("NickServ");
+ ServiceBot *NickServ = Config->GetClient("NickServ");
if (NickServ)
{
Section s;
@@ -108,7 +137,7 @@ class ModuleWebCPanel : public Module
panel.sections.push_back(s);
}
- BotInfo *ChanServ = Config->GetClient("ChanServ");
+ ServiceBot *ChanServ = Config->GetClient("ChanServ");
if (ChanServ)
{
Section s;
@@ -148,7 +177,7 @@ class ModuleWebCPanel : public Module
panel.sections.push_back(s);
}
- BotInfo *MemoServ = Config->GetClient("MemoServ");
+ ServiceBot *MemoServ = Config->GetClient("MemoServ");
if (MemoServ)
{
Section s;
@@ -163,7 +192,7 @@ class ModuleWebCPanel : public Module
panel.sections.push_back(s);
}
- BotInfo *HostServ = Config->GetClient("HostServ");
+ ServiceBot *HostServ = Config->GetClient("HostServ");
if (HostServ)
{
Section s;
@@ -178,7 +207,7 @@ class ModuleWebCPanel : public Module
panel.sections.push_back(s);
}
- BotInfo *OperServ = Config->GetClient("OperServ");
+ ServiceBot *OperServ = Config->GetClient("OperServ");
if (OperServ)
{
Section s;
@@ -221,7 +250,7 @@ class ModuleWebCPanel : public Module
provider->UnregisterPage(&this->chanserv_drop);
provider->UnregisterPage(&this->memoserv_memos);
-
+
provider->UnregisterPage(&this->hostserv_request);
provider->UnregisterPage(&this->operserv_akill);
@@ -231,9 +260,9 @@ class ModuleWebCPanel : public Module
namespace WebPanel
{
- void RunCommand(const Anope::string &user, NickCore *nc, const Anope::string &service, const Anope::string &c, std::vector<Anope::string> &params, TemplateFileServer::Replacements &r, const Anope::string &key)
+ void RunCommand(const Anope::string &user, NickServ::Account *nc, const Anope::string &service, const Anope::string &c, std::vector<Anope::string> &params, TemplateFileServer::Replacements &r, const Anope::string &key)
{
- ServiceReference<Command> cmd("Command", c);
+ ServiceReference<Command> cmd(c);
if (!cmd)
{
r[key] = "Unable to find command " + c;
@@ -243,13 +272,9 @@ namespace WebPanel
if (params.size() < cmd->min_params)
return;
- BotInfo *bi = Config->GetClient(service);
+ ServiceBot *bi = Config->GetClient(service);
if (!bi)
- {
- if (BotListByNick->empty())
- return;
- bi = BotListByNick->begin()->second; // Pick one...
- }
+ return;
struct MyComandReply : CommandReply
{
@@ -258,7 +283,7 @@ namespace WebPanel
MyComandReply(TemplateFileServer::Replacements &_r, const Anope::string &_k) : re(_r), k(_k) { }
- void SendMessage(BotInfo *source, const Anope::string &msg) anope_override
+ void SendMessage(const MessageSource &, const Anope::string &msg) override
{
re[k] = msg;
}
@@ -271,16 +296,16 @@ namespace WebPanel
cmd->Run(source, "", info, params);
}
- void RunCommandWithName(NickCore *nc, const Anope::string &service, const Anope::string &c, const Anope::string &cmdname, std::vector<Anope::string> &params, TemplateFileServer::Replacements &r, const Anope::string &key)
+ void RunCommandWithName(NickServ::Account *nc, const Anope::string &service, const Anope::string &c, const Anope::string &cmdname, std::vector<Anope::string> &params, TemplateFileServer::Replacements &r, const Anope::string &key)
{
- ServiceReference<Command> cmd("Command", c);
+ ServiceReference<Command> cmd(c);
if (!cmd)
{
r[key] = "Unable to find command " + c;
return;
}
- BotInfo *bi = Config->GetClient(service);
+ ServiceBot *bi = Config->GetClient(service);
if (!bi)
return;
@@ -295,14 +320,14 @@ namespace WebPanel
MyComandReply(TemplateFileServer::Replacements &_r, const Anope::string &_k) : re(_r), k(_k) { }
- void SendMessage(BotInfo *source, const Anope::string &msg) anope_override
+ void SendMessage(const MessageSource &, const Anope::string &msg) override
{
re[k] = msg;
}
}
my_reply(r, key);
- CommandSource source(nc->display, NULL, nc, &my_reply, bi);
+ CommandSource source(nc->GetDisplay(), NULL, nc, &my_reply, bi);
cmd->Run(source, cmdname, *info, params);
}
diff --git a/modules/webcpanel/webcpanel.h b/modules/webcpanel/webcpanel.h
index 5112a632e..33e88821d 100644
--- a/modules/webcpanel/webcpanel.h
+++ b/modules/webcpanel/webcpanel.h
@@ -1,8 +1,20 @@
/*
- * (C) 2003-2016 Anope Team
- * Contact us at team@anope.org
+ * Anope IRC Services
*
- * Please read COPYING and README for further details.
+ * Copyright (C) 2012-2016 Anope Team <team@anope.org>
+ *
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "module.h"
@@ -31,11 +43,13 @@ struct Section
class Panel : public Section, public Service
{
public:
- Panel(Module *c, const Anope::string &n) : Service(c, "Panel", n) { }
+ static constexpr const char *NAME = "panel";
+
+ Panel(Module *c, const Anope::string &n) : Service(c, NAME, "") { }
std::vector<Section> sections;
- NickAlias *GetNickFromSession(HTTPClient *client, HTTPMessage &msg)
+ NickServ::Nick *GetNickFromSession(HTTPClient *client, HTTPMessage &msg)
{
if (!client)
return NULL;
@@ -45,7 +59,7 @@ class Panel : public Section, public Service
if (acc.empty() || id.empty())
return NULL;
- NickAlias *na = NickAlias::Find(acc);
+ NickServ::Nick *na = NickServ::FindNick(acc);
if (na == NULL)
return NULL;
@@ -68,22 +82,22 @@ class WebPanelPage : public HTTPPage
{
}
- virtual bool OnRequest(HTTPProvider *, const Anope::string &, HTTPClient *, HTTPMessage &, HTTPReply &) = 0;
+ virtual bool OnRequest(HTTPProvider *, const Anope::string &, HTTPClient *, HTTPMessage &, HTTPReply &) anope_abstract;
};
class WebPanelProtectedPage : public WebPanelPage
{
Anope::string category;
+ ServiceReference<Panel> panel;
public:
WebPanelProtectedPage(const Anope::string &cat, const Anope::string &u, const Anope::string &ct = "text/html") : WebPanelPage(u, ct), category(cat)
{
}
- bool OnRequest(HTTPProvider *provider, const Anope::string &page_name, HTTPClient *client, HTTPMessage &message, HTTPReply &reply) anope_override anope_final
+ bool OnRequest(HTTPProvider *provider, const Anope::string &page_name, HTTPClient *client, HTTPMessage &message, HTTPReply &reply) override final
{
- ServiceReference<Panel> panel("Panel", "webcpanel");
- NickAlias *na;
+ NickServ::Nick *na;
if (!panel || !(na = panel->GetNickFromSession(client, message)))
{
@@ -95,10 +109,10 @@ class WebPanelProtectedPage : public WebPanelPage
TemplateFileServer::Replacements replacements;
replacements["TITLE"] = page_title;
- replacements["ACCOUNT"] = na->nc->display;
+ replacements["ACCOUNT"] = na->GetAccount()->GetDisplay();
replacements["PAGE_NAME"] = page_name;
replacements["CATEGORY"] = category;
- if (na->nc->IsServicesOper())
+ if (na->GetAccount()->IsServicesOper())
replacements["IS_OPER"];
Anope::string sections, get;
@@ -134,7 +148,7 @@ class WebPanelProtectedPage : public WebPanelPage
return this->OnRequest(provider, page_name, client, message, reply, na, replacements);
}
- virtual bool OnRequest(HTTPProvider *, const Anope::string &, HTTPClient *, HTTPMessage &, HTTPReply &, NickAlias *, TemplateFileServer::Replacements &) = 0;
+ virtual bool OnRequest(HTTPProvider *, const Anope::string &, HTTPClient *, HTTPMessage &, HTTPReply &, NickServ::Nick *, TemplateFileServer::Replacements &) anope_abstract;
/* What get data should be appended to links in the navbar */
virtual std::set<Anope::string> GetData() { return std::set<Anope::string>(); }
@@ -143,7 +157,7 @@ class WebPanelProtectedPage : public WebPanelPage
namespace WebPanel
{
/** Run a command
- * @param User name to run command as, probably nc->display unless nc == NULL
+ * @param User name to run command as, probably nc->GetDisplay() unless nc == NULL
* @param nc Nick core to run command from
* @param service Service for source.owner and source.service
* @param c Command to run (as a service name)
@@ -151,9 +165,9 @@ namespace WebPanel
* @param r Replacements, reply from command goes back here into key
* @param key The key to put the replies into r
*/
- extern void RunCommand(const Anope::string &user, NickCore *nc, const Anope::string &service, const Anope::string &c, std::vector<Anope::string> &params, TemplateFileServer::Replacements &r, const Anope::string &key = "MESSAGES");
+ extern void RunCommand(const Anope::string &user, NickServ::Account *nc, const Anope::string &service, const Anope::string &c, std::vector<Anope::string> &params, TemplateFileServer::Replacements &r, const Anope::string &key = "MESSAGES");
- extern void RunCommandWithName(NickCore *nc, const Anope::string &service, const Anope::string &c, const Anope::string &cmdname, std::vector<Anope::string> &params, TemplateFileServer::Replacements &r, const Anope::string &key = "MESSAGES");
+ extern void RunCommandWithName(NickServ::Account *nc, const Anope::string &service, const Anope::string &c, const Anope::string &cmdname, std::vector<Anope::string> &params, TemplateFileServer::Replacements &r, const Anope::string &key = "MESSAGES");
}
#include "pages/index.h"
diff --git a/modules/m_xmlrpc.cpp b/modules/xmlrpc.cpp
index 7539825d1..41bbfce0a 100644
--- a/modules/m_xmlrpc.cpp
+++ b/modules/xmlrpc.cpp
@@ -1,9 +1,20 @@
/*
+ * Anope IRC Services
*
- * (C) 2010-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2010-2016 Anope Team <team@anope.org>
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "module.h"
@@ -39,12 +50,12 @@ class MyXMLRPCServiceInterface : public XMLRPCServiceInterface, public HTTPPage
public:
MyXMLRPCServiceInterface(Module *creator, const Anope::string &sname) : XMLRPCServiceInterface(creator, sname), HTTPPage("/xmlrpc", "text/xml") { }
- void Register(XMLRPCEvent *event)
+ void Register(XMLRPCEvent *event) override
{
this->events.push_back(event);
}
- void Unregister(XMLRPCEvent *event)
+ void Unregister(XMLRPCEvent *event) override
{
std::deque<XMLRPCEvent *>::iterator it = std::find(this->events.begin(), this->events.end(), event);
@@ -52,7 +63,7 @@ class MyXMLRPCServiceInterface : public XMLRPCServiceInterface, public HTTPPage
this->events.erase(it);
}
- Anope::string Sanitize(const Anope::string &string) anope_override
+ Anope::string Sanitize(const Anope::string &string) override
{
Anope::string ret = string;
for (int i = 0; special[i].character.empty() == false; ++i)
@@ -144,7 +155,7 @@ class MyXMLRPCServiceInterface : public XMLRPCServiceInterface, public HTTPPage
}
public:
- bool OnRequest(HTTPProvider *provider, const Anope::string &page_name, HTTPClient *client, HTTPMessage &message, HTTPReply &reply) anope_override
+ bool OnRequest(HTTPProvider *provider, const Anope::string &page_name, HTTPClient *client, HTTPMessage &message, HTTPReply &reply) override
{
Anope::string content = message.content, tname, data;
XMLRPCRequest request(reply);
@@ -181,7 +192,7 @@ class MyXMLRPCServiceInterface : public XMLRPCServiceInterface, public HTTPPage
return true;
}
- void Reply(XMLRPCRequest &request)
+ void Reply(XMLRPCRequest &request) override
{
if (!request.id.empty())
request.reply("id", request.id);
@@ -198,11 +209,12 @@ class MyXMLRPCServiceInterface : public XMLRPCServiceInterface, public HTTPPage
class ModuleXMLRPC : public Module
{
ServiceReference<HTTPProvider> httpref;
+
public:
MyXMLRPCServiceInterface xmlrpcinterface;
- ModuleXMLRPC(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, EXTRA | VENDOR),
- xmlrpcinterface(this, "xmlrpc")
+ ModuleXMLRPC(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, EXTRA | VENDOR)
+ , xmlrpcinterface(this, "xmlrpc")
{
}
@@ -213,13 +225,16 @@ class ModuleXMLRPC : public Module
httpref->UnregisterPage(&xmlrpcinterface);
}
- void OnReload(Configuration::Conf *conf) anope_override
+ void OnReload(Configuration::Conf *conf) override
{
if (httpref)
httpref->UnregisterPage(&xmlrpcinterface);
- this->httpref = ServiceReference<HTTPProvider>("HTTPProvider", conf->GetModule(this)->Get<const Anope::string>("server", "httpd/main"));
+
+ this->httpref = ServiceReference<HTTPProvider>(conf->GetModule(this)->Get<Anope::string>("server", "httpd/main"));
+
if (!httpref)
throw ConfigException("Unable to find http reference, is m_httpd loaded?");
+
httpref->RegisterPage(&xmlrpcinterface);
}
};
diff --git a/modules/m_xmlrpc_main.cpp b/modules/xmlrpc_main.cpp
index ed89ec07e..cdbcf3266 100644
--- a/modules/m_xmlrpc_main.cpp
+++ b/modules/xmlrpc_main.cpp
@@ -1,17 +1,29 @@
/*
+ * Anope IRC Services
*
- * (C) 2010-2016 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2010-2016 Anope Team <team@anope.org>
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "module.h"
#include "modules/xmlrpc.h"
+#include "modules/operserv/stats.h"
static Module *me;
-class XMLRPCIdentifyRequest : public IdentifyRequest
+class XMLRPCIdentifyRequest : public NickServ::IdentifyRequestListener
{
XMLRPCRequest request;
HTTPReply repl; /* Request holds a reference to the HTTPReply, because we might exist long enough to invalidate it
@@ -20,9 +32,9 @@ class XMLRPCIdentifyRequest : public IdentifyRequest
Reference<XMLRPCServiceInterface> xinterface;
public:
- XMLRPCIdentifyRequest(Module *m, XMLRPCRequest& req, HTTPClient *c, XMLRPCServiceInterface* iface, const Anope::string &acc, const Anope::string &pass) : IdentifyRequest(m, acc, pass), request(req), repl(request.r), client(c), xinterface(iface) { }
+ XMLRPCIdentifyRequest(XMLRPCRequest& req, HTTPClient *c, XMLRPCServiceInterface* iface) : request(req), repl(request.r), client(c), xinterface(iface) { }
- void OnSuccess() anope_override
+ void OnSuccess(NickServ::IdentifyRequest *req) override
{
if (!xinterface || !client)
return;
@@ -30,13 +42,13 @@ class XMLRPCIdentifyRequest : public IdentifyRequest
request.r = this->repl;
request.reply("result", "Success");
- request.reply("account", GetAccount());
+ request.reply("account", req->GetAccount());
xinterface->Reply(request);
client->SendReply(&request.r);
}
- void OnFail() anope_override
+ void OnFail(NickServ::IdentifyRequest *req) override
{
if (!xinterface || !client)
return;
@@ -53,7 +65,7 @@ class XMLRPCIdentifyRequest : public IdentifyRequest
class MyXMLRPCEvent : public XMLRPCEvent
{
public:
- bool Run(XMLRPCServiceInterface *iface, HTTPClient *client, XMLRPCRequest &request) anope_override
+ bool Run(XMLRPCServiceInterface *iface, HTTPClient *client, XMLRPCRequest &request) override
{
if (request.name == "command")
this->DoCommand(iface, client, request);
@@ -84,14 +96,14 @@ class MyXMLRPCEvent : public XMLRPCEvent
request.reply("error", "Invalid parameters");
else
{
- BotInfo *bi = BotInfo::Find(service, true);
+ ServiceBot *bi = ServiceBot::Find(service, true);
if (!bi)
request.reply("error", "Invalid service");
else
{
request.reply("result", "Success");
- NickAlias *na = NickAlias::Find(user);
+ NickServ::Nick *na = NickServ::FindNick(user);
Anope::string out;
@@ -101,14 +113,14 @@ class MyXMLRPCEvent : public XMLRPCEvent
XMLRPCommandReply(Anope::string &s) : str(s) { }
- void SendMessage(BotInfo *, const Anope::string &msg) anope_override
+ void SendMessage(const MessageSource &, const Anope::string &msg) override
{
str += msg + "\n";
};
}
reply(out);
- CommandSource source(user, NULL, na ? *na->nc : NULL, &reply, bi);
+ CommandSource source(user, NULL, na ? na->GetAccount() : NULL, &reply, bi);
Command::Run(source, command);
if (!out.empty())
@@ -122,21 +134,23 @@ class MyXMLRPCEvent : public XMLRPCEvent
Anope::string username = request.data.size() > 0 ? request.data[0] : "";
Anope::string password = request.data.size() > 1 ? request.data[1] : "";
- if (username.empty() || password.empty())
+ if (username.empty() || password.empty() || !NickServ::service)
request.reply("error", "Invalid parameters");
else
{
- XMLRPCIdentifyRequest *req = new XMLRPCIdentifyRequest(me, request, client, iface, username, password);
- FOREACH_MOD(OnCheckAuthentication, (NULL, req));
+ NickServ::IdentifyRequest *req = NickServ::service->CreateIdentifyRequest(new XMLRPCIdentifyRequest(request, client, iface), me, username, password);
+ EventManager::Get()->Dispatch(&Event::CheckAuthentication::OnCheckAuthentication, nullptr, req);
req->Dispatch();
return false;
}
-
+
return true;
}
void DoStats(XMLRPCServiceInterface *iface, HTTPClient *client, XMLRPCRequest &request)
{
+ Stats *stats = Serialize::GetObject<Stats *>();
+
request.reply("uptime", stringify(Anope::CurTime - Anope::StartTime));
request.reply("uplinkname", Me->GetLinks().front()->GetName());
{
@@ -148,7 +162,7 @@ class MyXMLRPCEvent : public XMLRPCEvent
request.reply("uplinkcapab", buf);
}
request.reply("usercount", stringify(UserListByNick.size()));
- request.reply("maxusercount", stringify(MaxUserCount));
+ request.reply("maxusercount", stringify(stats ? stats->GetMaxUserCount() : 0));
request.reply("channelcount", stringify(ChannelList.size()));
}
@@ -227,9 +241,9 @@ class MyXMLRPCEvent : public XMLRPCEvent
request.reply("signon", stringify(u->signon));
if (u->Account())
{
- request.reply("account", iface->Sanitize(u->Account()->display));
+ request.reply("account", iface->Sanitize(u->Account()->GetDisplay()));
if (u->Account()->o)
- request.reply("opertype", iface->Sanitize(u->Account()->o->ot->GetName()));
+ request.reply("opertype", iface->Sanitize(u->Account()->o->GetType()->GetName()));
}
Anope::string channels;
@@ -266,7 +280,7 @@ class MyXMLRPCEvent : public XMLRPCEvent
Anope::string to = request.data.size() > 1 ? request.data[1] : "";
Anope::string message = request.data.size() > 2 ? request.data[2] : "";
- BotInfo *bi = BotInfo::Find(from, true);
+ ServiceBot *bi = ServiceBot::Find(from, true);
User *u = User::Find(to, true);
if (!bi || !u || message.empty())
@@ -285,7 +299,7 @@ class ModuleXMLRPCMain : public Module
MyXMLRPCEvent stats;
public:
- ModuleXMLRPCMain(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, EXTRA | VENDOR), xmlrpc("XMLRPCServiceInterface", "xmlrpc")
+ ModuleXMLRPCMain(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, EXTRA | VENDOR)
{
me = this;