From e66063e6304538d34c40460ca0aa2be5ddb6bdec Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 5 Aug 2011 05:35:31 -0400 Subject: Rewrote the example configurations and split them up into seperate files for each pseudo client. Also reorganized how the modules are stored, and made most of the old "extra" modules "core" --- CMakeLists.txt | 2 +- data/botserv.example.conf | 227 ++++ data/chanserv.example.conf | 724 +++++++++++ data/commands.example.conf | 341 ------ data/example.conf | 923 ++------------ data/global.example.conf | 101 ++ data/hostserv.example.conf | 150 +++ data/memoserv.example.conf | 215 ++++ data/modules.example.conf | 239 +--- data/mysql/tables.sql | 425 ------- data/nickserv.example.conf | 606 +++++++++ data/operserv.example.conf | 579 +++++++++ data/tables.sql | 425 +++++++ docs/TODO | 1 + language/update.sh | 2 +- modules/CMakeLists.txt | 4 +- modules/commands/bs_assign.cpp | 168 +++ modules/commands/bs_badwords.cpp | 331 +++++ modules/commands/bs_bot.cpp | 414 +++++++ modules/commands/bs_botlist.cpp | 91 ++ modules/commands/bs_control.cpp | 158 +++ modules/commands/bs_info.cpp | 244 ++++ modules/commands/bs_kick.cpp | 1011 +++++++++++++++ modules/commands/bs_set.cpp | 307 +++++ modules/commands/cs_access.cpp | 915 ++++++++++++++ modules/commands/cs_akick.cpp | 564 +++++++++ modules/commands/cs_appendtopic.cpp | 117 ++ modules/commands/cs_ban.cpp | 109 ++ modules/commands/cs_clearusers.cpp | 83 ++ modules/commands/cs_clone.cpp | 186 +++ modules/commands/cs_drop.cpp | 102 ++ modules/commands/cs_enforce.cpp | 215 ++++ modules/commands/cs_entrymsg.cpp | 224 ++++ modules/commands/cs_flags.cpp | 455 +++++++ modules/commands/cs_getkey.cpp | 80 ++ modules/commands/cs_info.cpp | 142 +++ modules/commands/cs_invite.cpp | 102 ++ modules/commands/cs_kick.cpp | 91 ++ modules/commands/cs_list.cpp | 136 ++ modules/commands/cs_mode.cpp | 364 ++++++ modules/commands/cs_modes.cpp | 431 +++++++ modules/commands/cs_register.cpp | 193 +++ modules/commands/cs_saset.cpp | 74 ++ modules/commands/cs_saset_noexpire.cpp | 81 ++ modules/commands/cs_set.cpp | 74 ++ modules/commands/cs_set_bantype.cpp | 98 ++ modules/commands/cs_set_description.cpp | 89 ++ modules/commands/cs_set_founder.cpp | 105 ++ modules/commands/cs_set_keeptopic.cpp | 94 ++ modules/commands/cs_set_misc.cpp | 110 ++ modules/commands/cs_set_opnotice.cpp | 93 ++ modules/commands/cs_set_peace.cpp | 93 ++ modules/commands/cs_set_persist.cpp | 180 +++ modules/commands/cs_set_private.cpp | 93 ++ modules/commands/cs_set_restricted.cpp | 91 ++ modules/commands/cs_set_secure.cpp | 94 ++ modules/commands/cs_set_securefounder.cpp | 95 ++ modules/commands/cs_set_secureops.cpp | 92 ++ modules/commands/cs_set_signkick.cpp | 104 ++ modules/commands/cs_set_successor.cpp | 119 ++ modules/commands/cs_set_topiclock.cpp | 92 ++ modules/commands/cs_suspend.cpp | 158 +++ modules/commands/cs_sync.cpp | 63 + modules/commands/cs_tban.cpp | 116 ++ modules/commands/cs_topic.cpp | 84 ++ modules/commands/cs_unban.cpp | 94 ++ modules/commands/cs_xop.cpp | 880 +++++++++++++ modules/commands/gl_global.cpp | 64 + modules/commands/help.cpp | 130 ++ modules/commands/hs_del.cpp | 107 ++ modules/commands/hs_group.cpp | 80 ++ modules/commands/hs_list.cpp | 137 +++ modules/commands/hs_off.cpp | 67 + modules/commands/hs_on.cpp | 77 ++ modules/commands/hs_request.cpp | 424 +++++++ modules/commands/hs_set.cpp | 236 ++++ modules/commands/ms_cancel.cpp | 77 ++ modules/commands/ms_check.cpp | 89 ++ modules/commands/ms_del.cpp | 163 +++ modules/commands/ms_ignore.cpp | 110 ++ modules/commands/ms_info.cpp | 219 ++++ modules/commands/ms_list.cpp | 168 +++ modules/commands/ms_read.cpp | 195 +++ modules/commands/ms_rsend.cpp | 106 ++ modules/commands/ms_send.cpp | 70 ++ modules/commands/ms_sendall.cpp | 74 ++ modules/commands/ms_set.cpp | 313 +++++ modules/commands/ms_staff.cpp | 71 ++ modules/commands/ns_access.cpp | 195 +++ modules/commands/ns_ajoin.cpp | 239 ++++ modules/commands/ns_alist.cpp | 96 ++ modules/commands/ns_cert.cpp | 231 ++++ modules/commands/ns_drop.cpp | 130 ++ modules/commands/ns_getemail.cpp | 82 ++ modules/commands/ns_getpass.cpp | 79 ++ modules/commands/ns_ghost.cpp | 122 ++ modules/commands/ns_group.cpp | 305 +++++ modules/commands/ns_identify.cpp | 93 ++ modules/commands/ns_info.cpp | 173 +++ modules/commands/ns_list.cpp | 169 +++ modules/commands/ns_logout.cpp | 97 ++ modules/commands/ns_recover.cpp | 132 ++ modules/commands/ns_register.cpp | 355 ++++++ modules/commands/ns_release.cpp | 110 ++ modules/commands/ns_resetpass.cpp | 152 +++ modules/commands/ns_saset.cpp | 173 +++ modules/commands/ns_saset_noexpire.cpp | 76 ++ modules/commands/ns_sendpass.cpp | 108 ++ modules/commands/ns_set.cpp | 158 +++ modules/commands/ns_set_autoop.cpp | 107 ++ modules/commands/ns_set_email.cpp | 186 +++ modules/commands/ns_set_greet.cpp | 108 ++ modules/commands/ns_set_hide.cpp | 149 +++ modules/commands/ns_set_kill.cpp | 149 +++ modules/commands/ns_set_language.cpp | 121 ++ modules/commands/ns_set_message.cpp | 114 ++ modules/commands/ns_set_misc.cpp | 126 ++ modules/commands/ns_set_private.cpp | 114 ++ modules/commands/ns_set_secure.cpp | 114 ++ modules/commands/ns_status.cpp | 94 ++ modules/commands/ns_suspend.cpp | 160 +++ modules/commands/ns_update.cpp | 70 ++ modules/commands/os_akill.cpp | 464 +++++++ modules/commands/os_chankill.cpp | 120 ++ modules/commands/os_config.cpp | 217 ++++ modules/commands/os_defcon.cpp | 663 ++++++++++ modules/commands/os_forbid.cpp | 342 ++++++ modules/commands/os_forbid.h | 37 + modules/commands/os_ignore.cpp | 350 ++++++ modules/commands/os_ignore.h | 39 + modules/commands/os_jupe.cpp | 79 ++ modules/commands/os_kick.cpp | 83 ++ modules/commands/os_kill.cpp | 72 ++ modules/commands/os_list.cpp | 187 +++ modules/commands/os_login.cpp | 97 ++ modules/commands/os_mode.cpp | 109 ++ modules/commands/os_modinfo.cpp | 245 ++++ modules/commands/os_module.cpp | 202 +++ modules/commands/os_news.cpp | 450 +++++++ modules/commands/os_news.h | 39 + modules/commands/os_noop.cpp | 94 ++ modules/commands/os_oline.cpp | 83 ++ modules/commands/os_oper.cpp | 219 ++++ modules/commands/os_reload.cpp | 72 ++ modules/commands/os_session.cpp | 749 ++++++++++++ modules/commands/os_session.h | 32 + modules/commands/os_set.cpp | 261 ++++ modules/commands/os_shutdown.cpp | 117 ++ modules/commands/os_stats.cpp | 224 ++++ modules/commands/os_svsnick.cpp | 92 ++ modules/commands/os_sxline.cpp | 807 ++++++++++++ modules/commands/os_update.cpp | 56 + modules/core/botserv.h | 63 - modules/core/bs_assign.cpp | 168 --- modules/core/bs_badwords.cpp | 331 ----- modules/core/bs_bot.cpp | 414 ------- modules/core/bs_botlist.cpp | 91 -- modules/core/bs_control.cpp | 158 --- modules/core/bs_info.cpp | 244 ---- modules/core/bs_kick.cpp | 1011 --------------- modules/core/bs_main.cpp | 204 --- modules/core/bs_set.cpp | 307 ----- modules/core/cs_access.cpp | 915 -------------- modules/core/cs_akick.cpp | 564 --------- modules/core/cs_ban.cpp | 109 -- modules/core/cs_clearusers.cpp | 83 -- modules/core/cs_clone.cpp | 186 --- modules/core/cs_drop.cpp | 102 -- modules/core/cs_flags.cpp | 455 ------- modules/core/cs_getkey.cpp | 80 -- modules/core/cs_info.cpp | 142 --- modules/core/cs_invite.cpp | 102 -- modules/core/cs_kick.cpp | 91 -- modules/core/cs_list.cpp | 136 -- modules/core/cs_main.cpp | 143 --- modules/core/cs_mode.cpp | 364 ------ modules/core/cs_modes.cpp | 431 ------- modules/core/cs_register.cpp | 193 --- modules/core/cs_saset.cpp | 74 -- modules/core/cs_saset_noexpire.cpp | 81 -- modules/core/cs_set.cpp | 74 -- modules/core/cs_set_bantype.cpp | 98 -- modules/core/cs_set_description.cpp | 89 -- modules/core/cs_set_founder.cpp | 105 -- modules/core/cs_set_keeptopic.cpp | 94 -- modules/core/cs_set_opnotice.cpp | 93 -- modules/core/cs_set_peace.cpp | 93 -- modules/core/cs_set_persist.cpp | 180 --- modules/core/cs_set_private.cpp | 93 -- modules/core/cs_set_restricted.cpp | 91 -- modules/core/cs_set_secure.cpp | 94 -- modules/core/cs_set_securefounder.cpp | 95 -- modules/core/cs_set_secureops.cpp | 92 -- modules/core/cs_set_signkick.cpp | 104 -- modules/core/cs_set_successor.cpp | 119 -- modules/core/cs_set_topiclock.cpp | 92 -- modules/core/cs_suspend.cpp | 158 --- modules/core/cs_topic.cpp | 84 -- modules/core/cs_unban.cpp | 94 -- modules/core/cs_xop.cpp | 880 ------------- modules/core/db_mysql.cpp | 1566 ------------------------ modules/core/db_mysql_live.cpp | 287 ----- modules/core/db_plain.cpp | 908 -------------- modules/core/enc_md5.cpp | 372 ------ modules/core/enc_none.cpp | 74 -- modules/core/enc_old.cpp | 382 ------ modules/core/enc_sha1.cpp | 222 ---- modules/core/enc_sha256.cpp | 314 ----- modules/core/gl_global.cpp | 64 - modules/core/gl_main.cpp | 93 -- modules/core/global.h | 20 - modules/core/help.cpp | 130 -- modules/core/hs_del.cpp | 107 -- modules/core/hs_group.cpp | 80 -- modules/core/hs_list.cpp | 137 --- modules/core/hs_main.cpp | 85 -- modules/core/hs_off.cpp | 67 - modules/core/hs_on.cpp | 77 -- modules/core/hs_set.cpp | 236 ---- modules/core/memoserv.h | 41 - modules/core/ms_cancel.cpp | 77 -- modules/core/ms_check.cpp | 89 -- modules/core/ms_del.cpp | 163 --- modules/core/ms_ignore.cpp | 110 -- modules/core/ms_info.cpp | 219 ---- modules/core/ms_list.cpp | 168 --- modules/core/ms_main.cpp | 228 ---- modules/core/ms_read.cpp | 195 --- modules/core/ms_rsend.cpp | 106 -- modules/core/ms_send.cpp | 70 -- modules/core/ms_sendall.cpp | 74 -- modules/core/ms_set.cpp | 313 ----- modules/core/ms_staff.cpp | 71 -- modules/core/nickserv.h | 15 - modules/core/ns_access.cpp | 195 --- modules/core/ns_ajoin.cpp | 239 ---- modules/core/ns_alist.cpp | 96 -- modules/core/ns_cert.cpp | 231 ---- modules/core/ns_drop.cpp | 130 -- modules/core/ns_getemail.cpp | 82 -- modules/core/ns_getpass.cpp | 79 -- modules/core/ns_ghost.cpp | 122 -- modules/core/ns_group.cpp | 305 ----- modules/core/ns_identify.cpp | 93 -- modules/core/ns_info.cpp | 173 --- modules/core/ns_list.cpp | 169 --- modules/core/ns_logout.cpp | 97 -- modules/core/ns_main.cpp | 307 ----- modules/core/ns_recover.cpp | 132 -- modules/core/ns_register.cpp | 355 ------ modules/core/ns_release.cpp | 110 -- modules/core/ns_resetpass.cpp | 152 --- modules/core/ns_saset.cpp | 173 --- modules/core/ns_saset_noexpire.cpp | 76 -- modules/core/ns_sendpass.cpp | 108 -- modules/core/ns_set.cpp | 158 --- modules/core/ns_set_autoop.cpp | 107 -- modules/core/ns_set_email.cpp | 186 --- modules/core/ns_set_greet.cpp | 108 -- modules/core/ns_set_hide.cpp | 149 --- modules/core/ns_set_kill.cpp | 149 --- modules/core/ns_set_language.cpp | 121 -- modules/core/ns_set_message.cpp | 114 -- modules/core/ns_set_private.cpp | 114 -- modules/core/ns_set_secure.cpp | 114 -- modules/core/ns_status.cpp | 94 -- modules/core/ns_suspend.cpp | 160 --- modules/core/ns_update.cpp | 70 -- modules/core/os_akill.cpp | 464 ------- modules/core/os_chankill.cpp | 120 -- modules/core/os_config.cpp | 217 ---- modules/core/os_forbid.cpp | 342 ------ modules/core/os_forbid.h | 37 - modules/core/os_ignore.cpp | 350 ------ modules/core/os_ignore.h | 39 - modules/core/os_jupe.cpp | 79 -- modules/core/os_kick.cpp | 83 -- modules/core/os_kill.cpp | 72 -- modules/core/os_list.cpp | 187 --- modules/core/os_login.cpp | 97 -- modules/core/os_main.cpp | 371 ------ modules/core/os_mode.cpp | 109 -- modules/core/os_modinfo.cpp | 245 ---- modules/core/os_module.cpp | 202 --- modules/core/os_news.cpp | 450 ------- modules/core/os_news.h | 39 - modules/core/os_noop.cpp | 94 -- modules/core/os_oline.cpp | 83 -- modules/core/os_oper.cpp | 219 ---- modules/core/os_reload.cpp | 72 -- modules/core/os_session.cpp | 749 ------------ modules/core/os_session.h | 32 - modules/core/os_set.cpp | 261 ---- modules/core/os_shutdown.cpp | 117 -- modules/core/os_stats.cpp | 224 ---- modules/core/os_svsnick.cpp | 92 -- modules/core/os_sxline.cpp | 807 ------------ modules/core/os_update.cpp | 56 - modules/database/db_mysql.cpp | 1566 ++++++++++++++++++++++++ modules/database/db_mysql_live.cpp | 287 +++++ modules/database/db_plain.cpp | 908 ++++++++++++++ modules/encryption/enc_md5.cpp | 372 ++++++ modules/encryption/enc_none.cpp | 74 ++ modules/encryption/enc_old.cpp | 382 ++++++ modules/encryption/enc_sha1.cpp | 222 ++++ modules/encryption/enc_sha256.cpp | 314 +++++ modules/extra/cs_appendtopic.cpp | 117 -- modules/extra/cs_enforce.cpp | 215 ---- modules/extra/cs_entrymsg.cpp | 224 ---- modules/extra/cs_set_misc.cpp | 110 -- modules/extra/cs_sync.cpp | 63 - modules/extra/cs_tban.cpp | 116 -- modules/extra/hs_request.cpp | 424 ------- modules/extra/language/CMakeLists.txt | 38 - modules/extra/language/cs_appendtopic.de_DE.po | 37 - modules/extra/language/cs_appendtopic.it_IT.po | 39 - modules/extra/language/cs_appendtopic.nl_NL.po | 38 - modules/extra/language/cs_appendtopic.pt_PT.po | 36 - modules/extra/language/cs_appendtopic.ru_RU.po | 41 - modules/extra/language/cs_enforce.de_DE.po | 86 -- modules/extra/language/cs_enforce.it_IT.po | 86 -- modules/extra/language/cs_enforce.nl_NL.po | 89 -- modules/extra/language/cs_enforce.pt_PT.po | 88 -- modules/extra/language/cs_enforce.ru_RU.po | 90 -- modules/extra/language/cs_tban.de_DE.po | 40 - modules/extra/language/cs_tban.it_IT.po | 41 - modules/extra/language/cs_tban.nl_NL.po | 41 - modules/extra/language/cs_tban.pt_PT.po | 40 - modules/extra/language/cs_tban.ru_RU.po | 42 - modules/extra/language/hs_request.it_IT.po | 149 --- modules/extra/language/hs_request.nl_NL.po | 150 --- modules/extra/language/hs_request.pt_PT.po | 148 --- modules/extra/language/hs_request.ru_RU.po | 151 --- modules/extra/language/ns_maxemail.de_DE.po | 28 - modules/extra/language/ns_maxemail.it_IT.po | 32 - modules/extra/language/ns_maxemail.nl_NL.po | 28 - modules/extra/language/ns_maxemail.pt_PT.po | 28 - modules/extra/language/ns_maxemail.ru_RU.po | 30 - modules/extra/language/update.sh | 14 - modules/extra/ns_set_misc.cpp | 126 -- modules/extra/os_defcon.cpp | 663 ---------- modules/pseudoclients/botserv.h | 63 + modules/pseudoclients/bs_main.cpp | 204 +++ modules/pseudoclients/cs_main.cpp | 143 +++ modules/pseudoclients/gl_main.cpp | 93 ++ modules/pseudoclients/global.h | 20 + modules/pseudoclients/hs_main.cpp | 85 ++ modules/pseudoclients/memoserv.h | 41 + modules/pseudoclients/ms_main.cpp | 228 ++++ modules/pseudoclients/nickserv.h | 15 + modules/pseudoclients/ns_main.cpp | 307 +++++ modules/pseudoclients/os_main.cpp | 371 ++++++ modules/third/README | 2 + modules/third/language/CMakeLists.txt | 38 + modules/third/language/README | 1 + modules/third/language/update.sh | 14 + 356 files changed, 33879 insertions(+), 34088 deletions(-) create mode 100644 data/botserv.example.conf create mode 100644 data/chanserv.example.conf delete mode 100644 data/commands.example.conf create mode 100644 data/global.example.conf create mode 100644 data/hostserv.example.conf create mode 100644 data/memoserv.example.conf delete mode 100644 data/mysql/tables.sql create mode 100644 data/nickserv.example.conf create mode 100644 data/operserv.example.conf create mode 100644 data/tables.sql create mode 100644 modules/commands/bs_assign.cpp create mode 100644 modules/commands/bs_badwords.cpp create mode 100644 modules/commands/bs_bot.cpp create mode 100644 modules/commands/bs_botlist.cpp create mode 100644 modules/commands/bs_control.cpp create mode 100644 modules/commands/bs_info.cpp create mode 100644 modules/commands/bs_kick.cpp create mode 100644 modules/commands/bs_set.cpp create mode 100644 modules/commands/cs_access.cpp create mode 100644 modules/commands/cs_akick.cpp create mode 100644 modules/commands/cs_appendtopic.cpp create mode 100644 modules/commands/cs_ban.cpp create mode 100644 modules/commands/cs_clearusers.cpp create mode 100644 modules/commands/cs_clone.cpp create mode 100644 modules/commands/cs_drop.cpp create mode 100644 modules/commands/cs_enforce.cpp create mode 100644 modules/commands/cs_entrymsg.cpp create mode 100644 modules/commands/cs_flags.cpp create mode 100644 modules/commands/cs_getkey.cpp create mode 100644 modules/commands/cs_info.cpp create mode 100644 modules/commands/cs_invite.cpp create mode 100644 modules/commands/cs_kick.cpp create mode 100644 modules/commands/cs_list.cpp create mode 100644 modules/commands/cs_mode.cpp create mode 100644 modules/commands/cs_modes.cpp create mode 100644 modules/commands/cs_register.cpp create mode 100644 modules/commands/cs_saset.cpp create mode 100644 modules/commands/cs_saset_noexpire.cpp create mode 100644 modules/commands/cs_set.cpp create mode 100644 modules/commands/cs_set_bantype.cpp create mode 100644 modules/commands/cs_set_description.cpp create mode 100644 modules/commands/cs_set_founder.cpp create mode 100644 modules/commands/cs_set_keeptopic.cpp create mode 100644 modules/commands/cs_set_misc.cpp create mode 100644 modules/commands/cs_set_opnotice.cpp create mode 100644 modules/commands/cs_set_peace.cpp create mode 100644 modules/commands/cs_set_persist.cpp create mode 100644 modules/commands/cs_set_private.cpp create mode 100644 modules/commands/cs_set_restricted.cpp create mode 100644 modules/commands/cs_set_secure.cpp create mode 100644 modules/commands/cs_set_securefounder.cpp create mode 100644 modules/commands/cs_set_secureops.cpp create mode 100644 modules/commands/cs_set_signkick.cpp create mode 100644 modules/commands/cs_set_successor.cpp create mode 100644 modules/commands/cs_set_topiclock.cpp create mode 100644 modules/commands/cs_suspend.cpp create mode 100644 modules/commands/cs_sync.cpp create mode 100644 modules/commands/cs_tban.cpp create mode 100644 modules/commands/cs_topic.cpp create mode 100644 modules/commands/cs_unban.cpp create mode 100644 modules/commands/cs_xop.cpp create mode 100644 modules/commands/gl_global.cpp create mode 100644 modules/commands/help.cpp create mode 100644 modules/commands/hs_del.cpp create mode 100644 modules/commands/hs_group.cpp create mode 100644 modules/commands/hs_list.cpp create mode 100644 modules/commands/hs_off.cpp create mode 100644 modules/commands/hs_on.cpp create mode 100644 modules/commands/hs_request.cpp create mode 100644 modules/commands/hs_set.cpp create mode 100644 modules/commands/ms_cancel.cpp create mode 100644 modules/commands/ms_check.cpp create mode 100644 modules/commands/ms_del.cpp create mode 100644 modules/commands/ms_ignore.cpp create mode 100644 modules/commands/ms_info.cpp create mode 100644 modules/commands/ms_list.cpp create mode 100644 modules/commands/ms_read.cpp create mode 100644 modules/commands/ms_rsend.cpp create mode 100644 modules/commands/ms_send.cpp create mode 100644 modules/commands/ms_sendall.cpp create mode 100644 modules/commands/ms_set.cpp create mode 100644 modules/commands/ms_staff.cpp create mode 100644 modules/commands/ns_access.cpp create mode 100644 modules/commands/ns_ajoin.cpp create mode 100644 modules/commands/ns_alist.cpp create mode 100644 modules/commands/ns_cert.cpp create mode 100644 modules/commands/ns_drop.cpp create mode 100644 modules/commands/ns_getemail.cpp create mode 100644 modules/commands/ns_getpass.cpp create mode 100644 modules/commands/ns_ghost.cpp create mode 100644 modules/commands/ns_group.cpp create mode 100644 modules/commands/ns_identify.cpp create mode 100644 modules/commands/ns_info.cpp create mode 100644 modules/commands/ns_list.cpp create mode 100644 modules/commands/ns_logout.cpp create mode 100644 modules/commands/ns_recover.cpp create mode 100644 modules/commands/ns_register.cpp create mode 100644 modules/commands/ns_release.cpp create mode 100644 modules/commands/ns_resetpass.cpp create mode 100644 modules/commands/ns_saset.cpp create mode 100644 modules/commands/ns_saset_noexpire.cpp create mode 100644 modules/commands/ns_sendpass.cpp create mode 100644 modules/commands/ns_set.cpp create mode 100644 modules/commands/ns_set_autoop.cpp create mode 100644 modules/commands/ns_set_email.cpp create mode 100644 modules/commands/ns_set_greet.cpp create mode 100644 modules/commands/ns_set_hide.cpp create mode 100644 modules/commands/ns_set_kill.cpp create mode 100644 modules/commands/ns_set_language.cpp create mode 100644 modules/commands/ns_set_message.cpp create mode 100644 modules/commands/ns_set_misc.cpp create mode 100644 modules/commands/ns_set_private.cpp create mode 100644 modules/commands/ns_set_secure.cpp create mode 100644 modules/commands/ns_status.cpp create mode 100644 modules/commands/ns_suspend.cpp create mode 100644 modules/commands/ns_update.cpp create mode 100644 modules/commands/os_akill.cpp create mode 100644 modules/commands/os_chankill.cpp create mode 100644 modules/commands/os_config.cpp create mode 100644 modules/commands/os_defcon.cpp create mode 100644 modules/commands/os_forbid.cpp create mode 100644 modules/commands/os_forbid.h create mode 100644 modules/commands/os_ignore.cpp create mode 100644 modules/commands/os_ignore.h create mode 100644 modules/commands/os_jupe.cpp create mode 100644 modules/commands/os_kick.cpp create mode 100644 modules/commands/os_kill.cpp create mode 100644 modules/commands/os_list.cpp create mode 100644 modules/commands/os_login.cpp create mode 100644 modules/commands/os_mode.cpp create mode 100644 modules/commands/os_modinfo.cpp create mode 100644 modules/commands/os_module.cpp create mode 100644 modules/commands/os_news.cpp create mode 100644 modules/commands/os_news.h create mode 100644 modules/commands/os_noop.cpp create mode 100644 modules/commands/os_oline.cpp create mode 100644 modules/commands/os_oper.cpp create mode 100644 modules/commands/os_reload.cpp create mode 100644 modules/commands/os_session.cpp create mode 100644 modules/commands/os_session.h create mode 100644 modules/commands/os_set.cpp create mode 100644 modules/commands/os_shutdown.cpp create mode 100644 modules/commands/os_stats.cpp create mode 100644 modules/commands/os_svsnick.cpp create mode 100644 modules/commands/os_sxline.cpp create mode 100644 modules/commands/os_update.cpp delete mode 100644 modules/core/botserv.h delete mode 100644 modules/core/bs_assign.cpp delete mode 100644 modules/core/bs_badwords.cpp delete mode 100644 modules/core/bs_bot.cpp delete mode 100644 modules/core/bs_botlist.cpp delete mode 100644 modules/core/bs_control.cpp delete mode 100644 modules/core/bs_info.cpp delete mode 100644 modules/core/bs_kick.cpp delete mode 100644 modules/core/bs_main.cpp delete mode 100644 modules/core/bs_set.cpp delete mode 100644 modules/core/cs_access.cpp delete mode 100644 modules/core/cs_akick.cpp delete mode 100644 modules/core/cs_ban.cpp delete mode 100644 modules/core/cs_clearusers.cpp delete mode 100644 modules/core/cs_clone.cpp delete mode 100644 modules/core/cs_drop.cpp delete mode 100644 modules/core/cs_flags.cpp delete mode 100644 modules/core/cs_getkey.cpp delete mode 100644 modules/core/cs_info.cpp delete mode 100644 modules/core/cs_invite.cpp delete mode 100644 modules/core/cs_kick.cpp delete mode 100644 modules/core/cs_list.cpp delete mode 100644 modules/core/cs_main.cpp delete mode 100644 modules/core/cs_mode.cpp delete mode 100644 modules/core/cs_modes.cpp delete mode 100644 modules/core/cs_register.cpp delete mode 100644 modules/core/cs_saset.cpp delete mode 100644 modules/core/cs_saset_noexpire.cpp delete mode 100644 modules/core/cs_set.cpp delete mode 100644 modules/core/cs_set_bantype.cpp delete mode 100644 modules/core/cs_set_description.cpp delete mode 100644 modules/core/cs_set_founder.cpp delete mode 100644 modules/core/cs_set_keeptopic.cpp delete mode 100644 modules/core/cs_set_opnotice.cpp delete mode 100644 modules/core/cs_set_peace.cpp delete mode 100644 modules/core/cs_set_persist.cpp delete mode 100644 modules/core/cs_set_private.cpp delete mode 100644 modules/core/cs_set_restricted.cpp delete mode 100644 modules/core/cs_set_secure.cpp delete mode 100644 modules/core/cs_set_securefounder.cpp delete mode 100644 modules/core/cs_set_secureops.cpp delete mode 100644 modules/core/cs_set_signkick.cpp delete mode 100644 modules/core/cs_set_successor.cpp delete mode 100644 modules/core/cs_set_topiclock.cpp delete mode 100644 modules/core/cs_suspend.cpp delete mode 100644 modules/core/cs_topic.cpp delete mode 100644 modules/core/cs_unban.cpp delete mode 100644 modules/core/cs_xop.cpp delete mode 100644 modules/core/db_mysql.cpp delete mode 100644 modules/core/db_mysql_live.cpp delete mode 100644 modules/core/db_plain.cpp delete mode 100644 modules/core/enc_md5.cpp delete mode 100644 modules/core/enc_none.cpp delete mode 100644 modules/core/enc_old.cpp delete mode 100644 modules/core/enc_sha1.cpp delete mode 100644 modules/core/enc_sha256.cpp delete mode 100644 modules/core/gl_global.cpp delete mode 100644 modules/core/gl_main.cpp delete mode 100644 modules/core/global.h delete mode 100644 modules/core/help.cpp delete mode 100644 modules/core/hs_del.cpp delete mode 100644 modules/core/hs_group.cpp delete mode 100644 modules/core/hs_list.cpp delete mode 100644 modules/core/hs_main.cpp delete mode 100644 modules/core/hs_off.cpp delete mode 100644 modules/core/hs_on.cpp delete mode 100644 modules/core/hs_set.cpp delete mode 100644 modules/core/memoserv.h delete mode 100644 modules/core/ms_cancel.cpp delete mode 100644 modules/core/ms_check.cpp delete mode 100644 modules/core/ms_del.cpp delete mode 100644 modules/core/ms_ignore.cpp delete mode 100644 modules/core/ms_info.cpp delete mode 100644 modules/core/ms_list.cpp delete mode 100644 modules/core/ms_main.cpp delete mode 100644 modules/core/ms_read.cpp delete mode 100644 modules/core/ms_rsend.cpp delete mode 100644 modules/core/ms_send.cpp delete mode 100644 modules/core/ms_sendall.cpp delete mode 100644 modules/core/ms_set.cpp delete mode 100644 modules/core/ms_staff.cpp delete mode 100644 modules/core/nickserv.h delete mode 100644 modules/core/ns_access.cpp delete mode 100644 modules/core/ns_ajoin.cpp delete mode 100644 modules/core/ns_alist.cpp delete mode 100644 modules/core/ns_cert.cpp delete mode 100644 modules/core/ns_drop.cpp delete mode 100644 modules/core/ns_getemail.cpp delete mode 100644 modules/core/ns_getpass.cpp delete mode 100644 modules/core/ns_ghost.cpp delete mode 100644 modules/core/ns_group.cpp delete mode 100644 modules/core/ns_identify.cpp delete mode 100644 modules/core/ns_info.cpp delete mode 100644 modules/core/ns_list.cpp delete mode 100644 modules/core/ns_logout.cpp delete mode 100644 modules/core/ns_main.cpp delete mode 100644 modules/core/ns_recover.cpp delete mode 100644 modules/core/ns_register.cpp delete mode 100644 modules/core/ns_release.cpp delete mode 100644 modules/core/ns_resetpass.cpp delete mode 100644 modules/core/ns_saset.cpp delete mode 100644 modules/core/ns_saset_noexpire.cpp delete mode 100644 modules/core/ns_sendpass.cpp delete mode 100644 modules/core/ns_set.cpp delete mode 100644 modules/core/ns_set_autoop.cpp delete mode 100644 modules/core/ns_set_email.cpp delete mode 100644 modules/core/ns_set_greet.cpp delete mode 100644 modules/core/ns_set_hide.cpp delete mode 100644 modules/core/ns_set_kill.cpp delete mode 100644 modules/core/ns_set_language.cpp delete mode 100644 modules/core/ns_set_message.cpp delete mode 100644 modules/core/ns_set_private.cpp delete mode 100644 modules/core/ns_set_secure.cpp delete mode 100644 modules/core/ns_status.cpp delete mode 100644 modules/core/ns_suspend.cpp delete mode 100644 modules/core/ns_update.cpp delete mode 100644 modules/core/os_akill.cpp delete mode 100644 modules/core/os_chankill.cpp delete mode 100644 modules/core/os_config.cpp delete mode 100644 modules/core/os_forbid.cpp delete mode 100644 modules/core/os_forbid.h delete mode 100644 modules/core/os_ignore.cpp delete mode 100644 modules/core/os_ignore.h delete mode 100644 modules/core/os_jupe.cpp delete mode 100644 modules/core/os_kick.cpp delete mode 100644 modules/core/os_kill.cpp delete mode 100644 modules/core/os_list.cpp delete mode 100644 modules/core/os_login.cpp delete mode 100644 modules/core/os_main.cpp delete mode 100644 modules/core/os_mode.cpp delete mode 100644 modules/core/os_modinfo.cpp delete mode 100644 modules/core/os_module.cpp delete mode 100644 modules/core/os_news.cpp delete mode 100644 modules/core/os_news.h delete mode 100644 modules/core/os_noop.cpp delete mode 100644 modules/core/os_oline.cpp delete mode 100644 modules/core/os_oper.cpp delete mode 100644 modules/core/os_reload.cpp delete mode 100644 modules/core/os_session.cpp delete mode 100644 modules/core/os_session.h delete mode 100644 modules/core/os_set.cpp delete mode 100644 modules/core/os_shutdown.cpp delete mode 100644 modules/core/os_stats.cpp delete mode 100644 modules/core/os_svsnick.cpp delete mode 100644 modules/core/os_sxline.cpp delete mode 100644 modules/core/os_update.cpp create mode 100644 modules/database/db_mysql.cpp create mode 100644 modules/database/db_mysql_live.cpp create mode 100644 modules/database/db_plain.cpp create mode 100644 modules/encryption/enc_md5.cpp create mode 100644 modules/encryption/enc_none.cpp create mode 100644 modules/encryption/enc_old.cpp create mode 100644 modules/encryption/enc_sha1.cpp create mode 100644 modules/encryption/enc_sha256.cpp delete mode 100644 modules/extra/cs_appendtopic.cpp delete mode 100644 modules/extra/cs_enforce.cpp delete mode 100644 modules/extra/cs_entrymsg.cpp delete mode 100644 modules/extra/cs_set_misc.cpp delete mode 100644 modules/extra/cs_sync.cpp delete mode 100644 modules/extra/cs_tban.cpp delete mode 100644 modules/extra/hs_request.cpp delete mode 100644 modules/extra/language/CMakeLists.txt delete mode 100644 modules/extra/language/cs_appendtopic.de_DE.po delete mode 100644 modules/extra/language/cs_appendtopic.it_IT.po delete mode 100644 modules/extra/language/cs_appendtopic.nl_NL.po delete mode 100644 modules/extra/language/cs_appendtopic.pt_PT.po delete mode 100644 modules/extra/language/cs_appendtopic.ru_RU.po delete mode 100644 modules/extra/language/cs_enforce.de_DE.po delete mode 100644 modules/extra/language/cs_enforce.it_IT.po delete mode 100644 modules/extra/language/cs_enforce.nl_NL.po delete mode 100644 modules/extra/language/cs_enforce.pt_PT.po delete mode 100644 modules/extra/language/cs_enforce.ru_RU.po delete mode 100644 modules/extra/language/cs_tban.de_DE.po delete mode 100644 modules/extra/language/cs_tban.it_IT.po delete mode 100644 modules/extra/language/cs_tban.nl_NL.po delete mode 100644 modules/extra/language/cs_tban.pt_PT.po delete mode 100644 modules/extra/language/cs_tban.ru_RU.po delete mode 100644 modules/extra/language/hs_request.it_IT.po delete mode 100644 modules/extra/language/hs_request.nl_NL.po delete mode 100644 modules/extra/language/hs_request.pt_PT.po delete mode 100644 modules/extra/language/hs_request.ru_RU.po delete mode 100644 modules/extra/language/ns_maxemail.de_DE.po delete mode 100644 modules/extra/language/ns_maxemail.it_IT.po delete mode 100644 modules/extra/language/ns_maxemail.nl_NL.po delete mode 100644 modules/extra/language/ns_maxemail.pt_PT.po delete mode 100644 modules/extra/language/ns_maxemail.ru_RU.po delete mode 100755 modules/extra/language/update.sh delete mode 100644 modules/extra/ns_set_misc.cpp delete mode 100644 modules/extra/os_defcon.cpp create mode 100644 modules/pseudoclients/botserv.h create mode 100644 modules/pseudoclients/bs_main.cpp create mode 100644 modules/pseudoclients/cs_main.cpp create mode 100644 modules/pseudoclients/gl_main.cpp create mode 100644 modules/pseudoclients/global.h create mode 100644 modules/pseudoclients/hs_main.cpp create mode 100644 modules/pseudoclients/memoserv.h create mode 100644 modules/pseudoclients/ms_main.cpp create mode 100644 modules/pseudoclients/nickserv.h create mode 100644 modules/pseudoclients/ns_main.cpp create mode 100644 modules/pseudoclients/os_main.cpp create mode 100644 modules/third/README create mode 100644 modules/third/language/CMakeLists.txt create mode 100644 modules/third/language/README create mode 100755 modules/third/language/update.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index c2106b9da..ad890bc51 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -222,7 +222,7 @@ option(USE_RUN_CC_PL "Use run-cc.pl for building" OFF) # Use the following directories as includes # Note that it is important the binary include directory comes before the # source include directory so the precompiled headers work correctly. -include_directories(${Anope_BINARY_DIR}/include ${Anope_SOURCE_DIR}/include ${Anope_BINARY_DIR}/language ${Anope_SOURCE_DIR}/modules/core) +include_directories(${Anope_BINARY_DIR}/include ${Anope_SOURCE_DIR}/include ${Anope_BINARY_DIR}/language ${Anope_SOURCE_DIR}/modules/pseudoclients) # If using Windows, always add the _WIN32 define if(WIN32) diff --git a/data/botserv.example.conf b/data/botserv.example.conf new file mode 100644 index 000000000..81b08d66d --- /dev/null +++ b/data/botserv.example.conf @@ -0,0 +1,227 @@ +/* + * Example configuration file for BotServ. + */ + +/* + * First, create the service. + */ +service +{ + /* + * The name of the BotServ client + */ + nick = "BotServ" + + /* + * The username of the BotServ client. + */ + user = "services" + + /* + * The hostname of the BotServ client. + */ + host = "localhost.net" + + /* + * The realname of the BotServ client. + */ + gecos = "Bot Service" +} + +/* + * Core BotServ module. + * + * Provides essential functionality for BotServ. + */ +module { name = "bs_main" } + +/* + * Configuration for BotServ provided by bs_main. + */ +botserv +{ + /* + * The name of the client that should be BotServ. + */ + name = "BotServ" + + /* + * The default bot options for newly registered channels. Note that changing these options + * will have no effect on channels which are already registered. The list must be separated + * by spaces. + * + * The options are: + * - dontkickops: Channel operators will be protected against BotServ kicks + * - dontkickvoices: Voiced users will be protected against BotServ kicks + * - greet: The channel's BotServ bot will greet incoming users that have set a greet + * in their NickServ settings + * - fantasy: Enables the use of BotServ fantasy commands in the channel + * + * This directive is optional, if left blank, there will be no defaults. + */ + defaults="greet fantasy" + + /* + * The minimum number of users there must be in a channel before the bot joins it. The best + * value for this setting is 1 or 2. This can be 0, the service bots will not part unless + * specifically unassigned, and will keep the channel open. + */ + minusers = 1 + + /* + * The maximum number of entries a single bad words list can have. Setting it too high can + * reduce performance slightly. + */ + badwordsmax = 32 + + /* + * The amount of time that data for a user is valid in BotServ. If the data exceeds this time, + * it is reset or deleted depending on the case. Do not set it too high, otherwise your + * resources will be slightly affected. + */ + keepdata = 10m + + /* + * The bots are currently not affected by any modes or bans when they try to join a channel. + * But some people may want to make it act like a real bot, that is, for example, remove all + * the bans affecting the bot before joining the channel, remove a ban that affects the bot + * set by a user when it is in the channel, and so on. Since it consumes a bit more CPU + * time, you should not enable this on larger networks. + * + * This directive is optional. + */ + #smartjoin = yes + + /* + * If set, the bots will use a kick reason that does not state the word when it is kicking. + * This is especially useful if you have young people on your network. + * + * This directive is optional. + */ + gentlebadwordreason = yes + + /* + * If set, BotServ will use case sensitive checking for badwords. + * + * This directive is optional. + */ + #casesensitive = yes + + /* + * Defines the prefix for fantasy commands in channels. This character will have to be prepended + * to all fantasy commands. If you choose "!", for example, fantasy commands will be "!kick", + * "!op", etc. This directive is optional, if left out, the default fantasy character is "!". + */ + #fantasycharacter = "!" +} + +/* + * Core BotServ commands. + * + * In Anope modules can provide (multiple) commands, each of which has a unique command name. Once these modules + * are loaded you can then configure the commands to be added to any client you like with any name you like. + * + * Additionally, you may provide a permission name that must be in the opertype of users executing the command. + * + * Sane defaults are provided below that do not need to be edited unless you wish to change the default behavior. + */ + +/* Give it a help command */ +command { service = "BotServ"; name = "HELP"; command = "generic/help"; } + +/* + * bs_assign + * + * Provides the commands botserv/assign and botserv/unassign. + * + * Used for assigning and unassigning bots to channels. + */ +module { name = "bs_assign" } +command { service = "BotServ"; name = "ASSIGN"; command = "botserv/assign"; } +command { service = "BotServ"; name = "UNASSIGN"; command = "botserv/unassign"; } + +/* + * bs_autoassign + * + * Allows service bots to be automatically assigned to channels upon registration. + */ +#module { name = "bs_autoassign" } +bs_autoassign +{ + /* + * Automatically assign ChanServ to channels upon registration. + */ + bot = "ChanServ" +} + +/* + * bs_badwords + * + * Provides the command botserv/badwords. + * + * Used for controlling the channel badword list. + */ +module { name = "bs_badwords" } +command { service = "BotServ"; name = "BADWORDS"; command = "botserv/badwords"; } + +/* + * bs_bot + * + * Provides the command botserv/bot. + * + * Used for administrating BotServ bots. + */ +module { name = "bs_bot" } +command { service = "BotServ"; name = "BOT"; command = "botserv/bot"; } + +/* + * bs_botlist + * + * Provides the command botserv/botlist. + * + * Used for listing all available bots. + */ +module { name = "bs_botlist" } +command { service = "BotServ"; name = "BOTLIST"; command = "botserv/botlist"; } + +/* + * bs_control + * + * Provides the commands botserv/act and botserv/say. + * + * Used for making the bot message a channel. + */ +module { name = "bs_control" } +command { service = "BotServ"; name = "ACT"; command = "botserv/act"; } +command { service = "BotServ"; name = "SAY"; command = "botserv/say"; } + +/* + * bs_info + * + * Provides the command botserv/info. + * + * Used for getting information on bots or channels. + */ +module { name = "bs_info" } +command { service = "BotServ"; name = "INFO"; command = "botserv/info"; } + +/* + * bs_kick + * + * Provides the command botserv/kick. + * + * Used for configuring what bots should kick for. + */ +module { name = "bs_kick" } +command { service = "BotServ"; name = "KICK"; command = "botserv/kick"; } + +/* + * bs_set + * + * Provides the command botserv/set. + * + * Used for setting options such as kickers and fantasy replies. + */ +module { name = "bs_set" } +command { service = "BotServ"; name = "SET"; command = "botserv/set"; } + diff --git a/data/chanserv.example.conf b/data/chanserv.example.conf new file mode 100644 index 000000000..b1b19040a --- /dev/null +++ b/data/chanserv.example.conf @@ -0,0 +1,724 @@ +/* + * Example configuration file for ChanServ. + */ + +/* + * First, create the service. + */ +service +{ + /* + * The name of the ChanServ client + */ + nick = "ChanServ" + + /* + * The username of the ChanServ client. + */ + user = "services" + + /* + * The hostname of the ChanServ client. + */ + host = "localhost.net" + + /* + * The realname of the ChanServ client. + */ + gecos = "Channel Registration Service" +} + +/* + * Core ChanServ module. + * + * Provides essential functionality for ChanServ. + */ +module { name = "cs_main" } + +/* + * Configuration for ChanServ provided by cs_main. + */ +chanserv +{ + /* + * The name of the client that should be ChanServ. + */ + name = "ChanServ" + + /* + * The default options for newly registered channels. Note that changing these options + * will have no effect on channels which are already registered. The list must be separated + * by spaces. + * + * The options are: + * - keeptopic: Retain topic when the channel is not in use + * - opnotice: Send a notice when OP/DEOP commands are used + * - peace: Disallow users from kicking or removing modes from others who are of the same + * access level or superior + * - private: Hide the channel from ChanServ's LIST command + * - restricted: Kick/ban users who are restricted from the channel + * - secure: Enable channel security, requiring the user to be identified with NickServ in + * order to be considered for being on the access list of the channel + * - secureops: Only allow operator status to be given if the user is on the access list + * - securefounder: Only allow the real founder of the channel to drop the channel, change it's + * password, or change the founder or successor + * - signkick: Use of ChanServ's KICK command will cause the user's nick to be signed to the kick. + * - signkicklevel: Same as above, but the kick will not be signed if the user is at the same access + * level or superior to the target + * - topiclock: Disallow the topic to be changed except with ChanServ's TOPIC command + * - persist: Keep the channel open at all times + * - none: No defaults + * + * This directive is optional, if left blank, the options will default to keetopic, secure, securefounder, + * and signkick. If you really want no defaults, use "none" by itself as the option. + */ + defaults="keeptopic peace secure securefounder signkick" + + /* + * The maximum number of channels which may be registered to a single nickname. + * + * This directive is optional, but recommended. + * If not set, there will be no restriction on the numbers of channels a single nickname can have registered. + */ + maxregistered = 20 + + /* + * The length of time before a channel registration expires. + * + * This directive is optional, but recommended. + * If not set, the default is 14 days. + */ + expire = 14d + + /* + * The length of time before a suspended channel becomes unsuspended. + * + * This directive is optional. + * If not set, the default is to never. + */ + #suspendexpire = 90d + + /* + * The lenth of time before a forbidden channel drops. + * + * This directive is optional. + * If not set, the default is to never. + */ + #forbidexpire = 90d + + /* + * The default ban type for newly registered channels (and when importing old databases). + * + * defbantype can be: + * + * 0: ban in the form of *!user@host + * 1: ban in the form of *!*user@host + * 2: ban in the form of *!*@host + * 3: ban in the form of *!*user@*.domain + */ + defbantype = 2 + + /* + * The maximum number of entries on a channel's access list. + */ + accessmax = 1024 + + /* + * The maximum number of entries on a channel's autokick list. + */ + autokickmax = 32 + + /* + * The default reason for an autokick if none is given. + */ + autokickreason = "User has been banned from the channel" + + /* + * The length of time ChanServ stays in a channel after kicking a user from a channel they are not + * permitted to be in. This only occurs when the user is the only one in the channel. + */ + inhabit = 15s + + /* + * The maximum number of channels to be returned for a ChanServ LIST command. + */ + listmax = 50 + + /* + * Allow only IRC Operators to use ChanServ. + * + * This directive is optional. + */ + #opersonly = yes + + /* + * ChanServ levels configuration, for use with cs_access. + * + * These levels are used by the chanserv/access access system. + * The levels configured below will be used as a default by newly registered channels. + * + * The level "founder" is a special level that means anyone with the level_founder permission + * can use (which of course defaults to "founder"). Anyone with the founder level permission in + * a channel can do anything in the channel. + */ + level_change = 10 + level_list = 1 + level_akick = 10 + level_assign = "founder" + level_autohalfop = 4 + level_autoop = 5 + level_autoowner = 9999 + level_autoprotect = 10 + level_autovoice = 3 + level_badwords = 10 + level_ban = 4 + level_fantasia = 3 + level_founder = "founder" + level_getkey = 5 + level_greet = 5 + level_halfop = 5 + level_halfopme = 4 + level_info = 9999 + level_invite = 5 + level_kick = 4 + level_memo = 10 + level_mode = 5 + level_nokick = 1 + level_opdeop = 5 + level_opdeopme = 5 + level_owner = "founder" + level_ownerme = 9999 + level_protect = 9999 + level_protectme = 10 + level_say = 5 + level_signkick = 9999 + level_set = 9999 + level_topic = 5 + level_unban = 4 + level_voice = 4 + level_voiceme = 3 + + /* + * ChanServ flags configuration, for use with cs_flags. + * + * These flags are used by the chanserv/flags access system. + */ + flag_change = "f" + flag_list = "l" + flag_akick = "K" + flag_assign = "s" + flag_autohalfop = "H" + flag_autoop = "O" + flag_autoowner = "Q" + flag_autoprotect = "A" + flag_autovoice = "V" + flag_badwords = "k" + flag_ban = "b" + flag_fantasia = "c" + flag_founder = "F" + flag_getkey = "G" + flag_greet = "g" + flag_halfop = "h" + flag_halfopme = "h" + flag_info = "I" + flag_invite = "i" + flag_kick = "k" + flag_memo = "m" + flag_mode = "s" + flag_nokick = "N" + flag_opdeop = "o" + flag_opdeopme = "o" + flag_owner = "q" + flag_ownerme = "q" + flag_protect = "a" + flag_protectme = "a" + flag_say = "B" + flag_set = "s" + flag_signkick = "K" + flag_topic = "t" + flag_unban = "u" + flag_voice = "v" + flag_voiceme = "v" +} + +/* + * Core ChanServ commands. + * + * In Anope modules can provide (multiple) commands, each of which has a unique command name. Once these modules + * are loaded you can then configure the commands to be added to any client you like with any name you like. + * + * Additionally, you may provide a permission name that must be in the opertype of users executing the command. + * + * Sane defaults are provided below that do not need to be edited unless you wish to change the default behavior. + */ + +/* Give it a help command */ +command { service = "ChanServ"; name = "HELP"; command = "generic/help"; } + +/* + * cs_access + * + * Provides commands chanserv/access and chanserv/levels. + * Provides the access system "levels". + * + * Used for giving users access in channels. + */ +module { name = "cs_access" } +command { service = "ChanServ"; name = "ACCESS"; command = "chanserv/access"; } +command { service = "ChanServ"; name = "LEVELS"; command = "chanserv/levels"; } + +/* + * cs_akick + * + * Provides the command chanserv/akick. + * + * Used for preventing users from joining channels. + */ +module { name = "cs_akick" } +command { service = "ChanServ"; name = "AKICK"; command = "chanserv/akick"; } + +/* + * cs_appendtopic + * + * Provides the chanserv/appendtopic command. + * + * Used to append text to existing channel topics. + */ +module { name = "cs_appendtopic" } +command { service = "ChanServ"; name = "APPENDTOPIC"; command = "chanserv/appendtopic"; } + +/* + * cs_ban + * + * Provides the command chanserv/ban. + * + * Used for banning users from channels. + */ +module { name = "cs_ban" } +command { service = "ChanServ"; name = "BAN"; command = "chanserv/ban"; } +command { service = "ChanServ"; name = "KB"; command = "chanserv/ban"; } + +/* + * cs_clearusers + * + * Provides the command chanserv/clearusers. + * + * Used for kicking all users from channels. + */ +module { name = "cs_clearusers" } +command { service = "ChanServ"; name = "CLEARUSERS"; command = "chanserv/clearusers"; } + +/* + * cs_clone + * + * Provides the command chanserv/clone. + * + * Used for copying channel settings from one channel to another. + */ +module { name = "cs_clone" } +command { service = "ChanServ"; name = "CLONE"; command = "chanserv/clone"; } + +/* + * cs_drop + * + * Provides the command chanserv/drop. + * + * Used for unregistering channels. + */ +module { name = "cs_drop" } +command { service = "ChanServ"; name = "DROP"; command = "chanserv/drop"; } + +/* + * cs_enforce + * + * Provides the command chanserv/enforce. + * + * Used to enforce various channel settings such as secureops and restricted. + */ +module { name = "cs_enforce" } +command { service = "ChanServ"; name = "ENFORCE"; command = "chanserv/enforce"; } + +/* + * cs_entrymsg + * + * Provides the command chanserv/entrymsg. + * + * Used to configure entry messages sent to users when they join a channel. + */ +module { name = "cs_entrymsg" } +command { service = "ChanServ"; name = "ENTRYMSG"; command = "chanserv/entrymsg"; } +cs_entrymsg +{ + /* The maximum number of entrymsgs allowed per channel. If not set, defaults to 5. */ + maxentries = 5 +} + +/* + * cs_flags + * + * Provides the command chanserv/flags. + * Provides the access system "flags". + * + * Used for giving users access in channels. + */ +module { name = "cs_flags" } +command { service = "ChanServ"; name = "FLAGS"; command = "chanserv/flags"; } + +/* + * cs_getkey + * + * Provides the command chanserv/getkey. + * + * Used for getting the key for channels. + */ +module { name = "cs_getkey" } +command { service = "ChanServ"; name = "GETKEY"; command = "chanserv/getkey"; } + +/* + * cs_info + * + * Provides the command chanserv/info. + * + * Used for getting information about channels. + */ +module { name = "cs_info" } +command { service = "ChanServ"; name = "INFO"; command = "chanserv/info"; } + +/* + * cs_invite + * + * Provides the command chanserv/invite. + * + * Used for inviting yourself in to channels. + */ +module { name = "cs_invite" } +command { service = "ChanServ"; name = "INVITE"; command = "chanserv/invite"; } + +/* + * cs_kick + * + * Provides the command chanserv/kick. + * + * Used for kicking users from channels. + */ +module { name = "cs_kick" } +command { service = "ChanServ"; name = "KICK"; command = "chanserv/kick"; } +command { service = "ChanServ"; name = "K"; command = "chanserv/kick"; } + +/* + * cs_list + * + * Provides the command chanserv/list. + * + * Used for retrieving and searching the registered channel list. + */ +module { name = "cs_list" } +command { service = "ChanServ"; name = "LIST"; command = "chanserv/list"; permission = "chanserv/list"; } + +/* + * cs_mode + * + * Provides the command chanserv/mode. + * + * Used for changing mode locks and changing modes. + */ +module { name = "cs_mode" } +command { service = "ChanServ"; name = "MODE"; command = "chanserv/mode"; } + +/* + * cs_mode + * + * Provides the commands chanserv/op, chanserv/deop, chanserv/halfop, chanserv/dehalfop + * chanserv/voice, chanserv/devoice, chanserv/protect, chanserv/deprotect, + * chanserv/owner, and chanserv/deowner. + * + * Used for setting and removing modes on users. + */ +module { name = "cs_modes" } +command { service = "ChanServ"; name = "OP"; command = "chanserv/op"; } +command { service = "ChanServ"; name = "DEOP"; command = "chanserv/deop"; } +command { service = "ChanServ"; name = "HALFOP"; command = "chanserv/halfop"; } +command { service = "ChanServ"; name = "DEHALFOP"; command = "chanserv/dehalfop"; } +command { service = "ChanServ"; name = "VOICE"; command = "chanserv/voice"; } +command { service = "ChanServ"; name = "DEVOICE"; command = "chanserv/devoice"; } +command { service = "ChanServ"; name = "PROTECT"; command = "chanserv/protect"; } +command { service = "ChanServ"; name = "DEPROTECT"; command = "chanserv/deprotect"; } +command { service = "ChanServ"; name = "OWNER"; command = "chanserv/owner"; } +command { service = "ChanServ"; name = "DEOWNER"; command = "chanserv/deowner"; } + +/* + * cs_register + * + * Provides the commands chanserv/register. + * + * Used for registering channels. + */ +module { name = "cs_register" } +command { service = "ChanServ"; name = "REGISTER"; command = "chanserv/register"; } + +/* + * cs_set + * + * Provides the command chanserv/set. + * + * Is a dummy command to provide a help wrapper for the various SET commands. + */ +module { name = "cs_set" } +command { service = "ChanServ"; name = "SET"; command = "chanserv/set"; } + +/* + * cs_saset + * + * Provides the command chanserv/saset. + * + * Is a dummy command to provide a help wrapper for the various SASET commands. + */ +module { name = "cs_saset" } +command { service = "ChanServ"; name = "SASET"; command = "chanserv/saset"; permission = "chanserv/saset"; } + +/* + * cs_set_bantype + * + * Provides the commands chanserv/set/bantype and chanserv/saset/bantype. + * + * Used for controlling what format bans are placed on channels. + */ +module { name = "cs_set_bantype" } +command { service = "ChanServ"; name = "SET BANTYPE"; command = "chanserv/set/bantype"; } +command { service = "ChanServ"; name = "SASET BANTYPE"; command = "chanserv/saset/bantype"; permission = "chanserv/saset/bantype"; } + +/* + * cs_set_description + * + * Provides the commands chanserv/set/description and chanserv/saset/description. + * + * Used for changing channels descriptions. + */ +module { name = "cs_set_description" } +command { service = "ChanServ"; name = "SET DESCRIPTION"; command = "chanserv/set/description"; } +command { service = "ChanServ"; name = "SET DESC"; command = "chanserv/set/description"; } +command { service = "ChanServ"; name = "SASET DESCRIPTION"; command = "chanserv/saset/description"; permission = "chanserv/saset/description"; } +command { service = "ChanServ"; name = "SASET DESC"; command = "chanserv/saset/description"; permission = "chanserv/saset/description"; } + +/* + * cs_set_founder + * + * Provides the commands chanserv/set/founder and chanserv/saset/founder. + * + * Used for changing channel founders. + */ +module { name = "cs_set_founder" } +command { service = "ChanServ"; name = "SET FOUNDER"; command = "chanserv/set/founder"; } +command { service = "ChanServ"; name = "SASET FOUNDER"; command = "chanserv/saset/founder"; permission = "chanserv/saset/founder"; } + +/* + * cs_set_keeptopic + * + * Provides the commands chanserv/set/keeptopic and chanserv/saset/keeptopic. + * + * Used for enabling keeptopic on channels, which causes ChanServ to restore the channel topic + * when a channel is created. + */ +module { name = "cs_set_keeptopic" } +command { service = "ChanServ"; name = "SET KEEPTOPIC"; command = "chanserv/set/keeptopic"; } +command { service = "ChanServ"; name = "SASET KEEPTOPIC"; command = "chanserv/saset/keeptopic"; permission = "chanserv/saset/keeptopic"; } + +/* + * cs_set_misc + * + * Provides the command chanserv/set/misc. + * + * Allows you to create arbitrary commands to set data, and have that data show up in chanserv/info. + */ +module { name = "cs_set_misc" } +command { service = "ChanServ"; name = "SET URL"; command = "chanserv/set/misc"; } +command { service = "ChanServ"; name = "SET EMAIL"; command = "chanserv/set/misc"; } + +/* + * cs_set_opnotice + * + * Provides the commands chanserv/set/opnotice and chanserv/saset/opnotice. + * + * Used for setting whether channel ops should be notified of mode changes made through ChanServ. + */ +module { name = "cs_set_opnotice" } +command { service = "ChanServ"; name = "SET OPNOTICE"; command = "chanserv/set/opnotice"; } +command { service = "ChanServ"; name = "SASET OPNOTICE"; command = "chanserv/saset/opnotice"; permission = "chanserv/saset/optnotice"; } + +/* + * cs_set_peace + * + * Provides the commands chanserv/set/peace and chanserv/saset/peace. + * + * Used for setting the peace option, which prevents users from kicking other users with + * higher level access than them. + */ +module { name = "cs_set_peace" } +command { service = "ChanServ"; name = "SET PEACE"; command = "chanserv/set/peace"; } +command { service = "ChanServ"; name = "SASET PEACE"; command = "chanserv/saset/peace"; permission = "chanserv/saset/peace"; } + +/* + * cs_set_persist + * + * Provides the commands chanserv/set/persist and chanserv/saset/persist. + * + * Used for setting whether ChanServ should stay in channels after the last user leaves. + */ +module { name = "cs_set_persist" } +command { service = "ChanServ"; name = "SET PERSIST"; command = "chanserv/set/persist"; } +command { service = "ChanServ"; name = "SASET PERSIST"; command = "chanserv/saset/persist"; permission = "chanserv/saset/persist"; } + +/* + * cs_set_private + * + * Provides the commands chanserv/set/private and chanserv/saset/private. + * + * Used for setting whether channels should show up in chanserv/list. + */ +module { name = "cs_set_private" } +command { service = "ChanServ"; name = "SET PRIVATE"; command = "chanserv/set/private"; } +command { service = "ChanServ"; name = "SASET PRIVATE"; command = "chanserv/saset/private"; permission = "chanserv/saset/private"; } + +/* + * cs_set_restricted + * + * Provides the commands chanserv/set/restricted and chanserv/saset/restricted. + * + * Used for setting whether users not on a channel's access list can join. + */ +module { name = "cs_set_restricted" } +command { service = "ChanServ"; name = "SET RESTRICTED"; command = "chanserv/set/restricted"; } +command { service = "ChanServ"; name = "SASET RESTRICTED"; command = "chanserv/saset/restricted"; permission = "chanserv/saset/restricted"; } + +/* + * cs_set_secure + * + * Provides the commands chanserv/set/secure and chanserv/saset/secure. + * + * Used for setting whether users who are recognized for accounts should have their access in channels. + */ +module { name = "cs_set_secure" } +command { service = "ChanServ"; name = "SET SECURE"; command = "chanserv/set/secure"; } +command { service = "ChanServ"; name = "SASET SECURE"; command = "chanserv/saset/secure"; permission = "chanserv/saset/secure"; } + +/* + * cs_set_securefounder + * + * Provides the commands chanserv/set/securefounder and chanserv/saset/securefounder. + * + * Used for setting whether users with founder level access in channels have true founder or not. + */ +module { name = "cs_set_securefounder" } +command { service = "ChanServ"; name = "SET SECUREFOUNDER"; command = "chanserv/set/securefounder"; } +command { service = "ChanServ"; name = "SASET SECUREFOUNDER"; command = "chanserv/saset/securefounder"; permission = "chanserv/saset/securefounder"; } + +/* + * cs_set_secureops + * + * Provides the commands chanserv/set/secureops and chanserv/saset/secureops. + * + * Used for restricting who can have channel op privilege in a channel to those whom have access in the channel. + */ +module { name = "cs_set_secureops" } +command { service = "ChanServ"; name = "SET SECUREOPS"; command = "chanserv/set/secureops"; } +command { service = "ChanServ"; name = "SASET SECUREOPS"; command = "chanserv/saset/secureops"; permission = "chanserv/saset/secureops"; } + +/* + * cs_set_signkick + * + * Provides the commands chanserv/set/signkick and chanserv/saset/signkick. + * + * Used for setting signkick, which appends the kicker's name to kicks sent through ChanServ. + */ +module { name = "cs_set_signkick" } +command { service = "ChanServ"; name = "SET SIGNKICK"; command = "chanserv/set/signkick"; } +command { service = "ChanServ"; name = "SASET SIGNKICK"; command = "chanserv/saset/signkick"; permission = "chanserv/saset/signkick"; } + +/* + * cs_set_successor + * + * Provides the commands chanserv/set/successor and chanserv/saset/successor. + * + * Used for setting channel successors, which become channel founders if the founders account expires. + */ +module { name = "cs_set_successor" } +command { service = "ChanServ"; name = "SET SUCCESSOR"; command = "chanserv/set/successor"; } +command { service = "ChanServ"; name = "SASET SUCCESSOR"; command = "chanserv/saset/successor"; permission = "chanserv/saset/successor"; } + +/* + * cs_set_topiclock + * + * Provides the commands chanserv/set/topiclock and chanserv/saset/topiclock. + * + * Used for setting topiclock, which prevents channel topics from being modified. + */ +module { name = "cs_set_topiclock" } +command { service = "ChanServ"; name = "SET TOPICLOCK"; command = "chanserv/set/topiclock"; } +command { service = "ChanServ"; name = "SASET TOPICLOCK"; command = "chanserv/saset/topiclock"; permission = "chanserv/saset/topiclock"; } + +/* + * cs_set_noexpire + * + * Provides the command chanserv/saset/noexpire. + * + * Used for setting noexpire, which prevents channels from expiring. + */ +module { name = "cs_saset_noexpire" } +command { service = "ChanServ"; name = "SASET NOEXPIRE"; command = "chanserv/saset/noexpire"; permission = "chanserv/saset/noexpire"; } + +/* + * cs_suspend + * + * Provides the commands chanserv/suspend and chanserv/unsuspend. + * + * Used for suspending and unsuspending channels. Suspended channels can not be used but their settings are stored. + */ +module { name = "cs_suspend" } +command { service = "ChanServ"; name = "SUSPEND"; command = "chanserv/suspend"; permission = "chanserv/suspend"; } +command { service = "ChanServ"; name = "UNSUSPEND"; command = "chanserv/unsuspend"; permission = "chanserv/suspend"; } + +/* + * cs_sync + * + * Provides the command chanserv/sync. + * + * Used to sync users channel status modes with what access they have. + */ +module { name = "cs_sync" } +command { service = "ChanServ"; name = "SYNC"; command = "chanserv/sync"; } + +/* + * cs_topic + * + * Provides the command chanserv/topic. + * + * Used for changing the channel topic. Usedful in conjunction with chanserv/set/topiclock. + */ +module { name = "cs_topic" } +command { service = "ChanServ"; name = "TOPIC"; command = "chanserv/topic"; } + +/* + * cs_unban + * + * Provides the command chanserv/unban. + * + * Used for unbanning users from channels. + */ +module { name = "cs_unban" } +command { service = "ChanServ"; name = "UNBAN"; command = "chanserv/unban"; } + +/* + * cs_xop + * + * Provides the commands chanserv/qop, chanserv/sop, chanserv/aop, chanserv/hop, and chanserv/vop. + * Provides the access system "XOP". + * + * Used for giving users access in channels. + */ +module { name = "cs_xop" } +command { service = "ChanServ"; name = "QOP"; command = "chanserv/qop"; } +command { service = "ChanServ"; name = "SOP"; command = "chanserv/sop"; } +command { service = "ChanServ"; name = "AOP"; command = "chanserv/aop"; } +command { service = "ChanServ"; name = "HOP"; command = "chanserv/hop"; } +command { service = "ChanServ"; name = "VOP"; command = "chanserv/vop"; } + diff --git a/data/commands.example.conf b/data/commands.example.conf deleted file mode 100644 index 6290dae6e..000000000 --- a/data/commands.example.conf +++ /dev/null @@ -1,341 +0,0 @@ - -/* - * [RECOMMENDED] Core modules and command configuration - * - * In Anope modules can provide (multiple) commands, each of which has a unique command name. Once these modules - * are loaded you can then configure the commands to be added to any client you like with any name you like. - * - * Additionally, you may provide a permission name that must be in the opertype of users executing the command. - * - * Sane defaults are below. - */ - -module { name = "help" } -command { service = "BotServ"; name = "HELP"; command = "generic/help"; } -command { service = "ChanServ"; name = "HELP"; command = "generic/help"; } -command { service = "Global"; name = "HELP"; command = "generic/help"; } -command { service = "HostServ"; name = "HELP"; command = "generic/help"; } -command { service = "MemoServ"; name = "HELP"; command = "generic/help"; } -command { service = "NickServ"; name = "HELP"; command = "generic/help"; } -command { service = "OperServ"; name = "HELP"; command = "generic/help"; } - -module { name = "bs_assign" } -command { service = "BotServ"; name = "ASSIGN"; command = "botserv/assign"; } -command { service = "BotServ"; name = "UNASSIGN"; command = "botserv/unassign"; } -module { name = "bs_badwords" } -command { service = "BotServ"; name = "BADWORDS"; command = "botserv/badwords"; } -module { name = "bs_bot" } -command { service = "BotServ"; name = "BOT"; command = "botserv/bot"; } -module { name = "bs_botlist" } -command { service = "BotServ"; name = "BOTLIST"; command = "botserv/botlist"; } -module { name = "bs_control" } -command { service = "BotServ"; name = "ACT"; command = "botserv/act"; } -command { service = "BotServ"; name = "SAY"; command = "botserv/say"; } -module { name = "bs_info" } -command { service = "BotServ"; name = "INFO"; command = "botserv/info"; } -module { name = "bs_kick" } -command { service = "BotServ"; name = "KICK"; command = "botserv/kick"; } -module { name = "bs_set" } -command { service = "BotServ"; name = "SET"; command = "botserv/set"; } - -module { name = "cs_access" } -command { service = "ChanServ"; name = "ACCESS"; command = "chanserv/access"; } -command { service = "ChanServ"; name = "LEVELS"; command = "chanserv/levels"; } -module { name = "cs_akick" } -command { service = "ChanServ"; name = "AKICK"; command = "chanserv/akick"; } -module { name = "cs_ban" } -command { service = "ChanServ"; name = "BAN"; command = "chanserv/ban"; } -command { service = "ChanServ"; name = "KB"; command = "chanserv/ban"; } -module { name = "cs_clearusers" } -command { service = "ChanServ"; name = "CLEARUSERS"; command = "chanserv/clearusers"; } -module { name = "cs_clone" } -command { service = "ChanServ"; name = "CLONE"; command = "chanserv/clone"; } -module { name = "cs_drop" } -command { service = "ChanServ"; name = "DROP"; command = "chanserv/drop"; } -module { name = "cs_flags" } -command { service = "ChanServ"; name = "FLAGS"; command = "chanserv/flags"; } -module { name = "cs_getkey" } -command { service = "ChanServ"; name = "GETKEY"; command = "chanserv/getkey"; } -module { name = "cs_info" } -command { service = "ChanServ"; name = "INFO"; command = "chanserv/info"; } -module { name = "cs_invite" } -command { service = "ChanServ"; name = "INVITE"; command = "chanserv/invite"; } -module { name = "cs_kick" } -command { service = "ChanServ"; name = "KICK"; command = "chanserv/kick"; } -command { service = "ChanServ"; name = "K"; command = "chanserv/kick"; } -module { name = "cs_list" } -command { service = "ChanServ"; name = "LIST"; command = "chanserv/list"; permission = "chanserv/list"; } -module { name = "cs_mode" } -command { service = "ChanServ"; name = "MODE"; command = "chanserv/mode"; } -module { name = "cs_modes" } -command { service = "ChanServ"; name = "OP"; command = "chanserv/op"; } -command { service = "ChanServ"; name = "DEOP"; command = "chanserv/deop"; } -command { service = "ChanServ"; name = "HALFOP"; command = "chanserv/halfop"; } -command { service = "ChanServ"; name = "DEHALFOP"; command = "chanserv/dehalfop"; } -command { service = "ChanServ"; name = "VOICE"; command = "chanserv/voice"; } -command { service = "ChanServ"; name = "DEVOICE"; command = "chanserv/devoice"; } -command { service = "ChanServ"; name = "PROTECT"; command = "chanserv/protect"; } -command { service = "ChanServ"; name = "DEPROTECT"; command = "chanserv/deprotect"; } -command { service = "ChanServ"; name = "OWNER"; command = "chanserv/owner"; } -command { service = "ChanServ"; name = "DEOWNER"; command = "chanserv/deowner"; } -module { name = "cs_register" } -command { service = "ChanServ"; name = "REGISTER"; command = "chanserv/register"; } -module { name = "cs_set" } -command { service = "ChanServ"; name = "SET"; command = "chanserv/set"; } -module { name = "cs_saset" } -command { service = "ChanServ"; name = "SASET"; command = "chanserv/saset"; permission = "chanserv/saset"; } -module { name = "cs_set_bantype" } -command { service = "ChanServ"; name = "SET BANTYPE"; command = "chanserv/set/bantype"; } -command { service = "ChanServ"; name = "SASET BANTYPE"; command = "chanserv/saset/bantype"; permission = "chanserv/saset/bantype"; } -module { name = "cs_set_description" } -command { service = "ChanServ"; name = "SET DESCRIPTION"; command = "chanserv/set/description"; } -command { service = "ChanServ"; name = "SET DESC"; command = "chanserv/set/description"; } -command { service = "ChanServ"; name = "SASET DESCRIPTION"; command = "chanserv/saset/description"; permission = "chanserv/saset/description"; } -command { service = "ChanServ"; name = "SASET DESC"; command = "chanserv/saset/description"; permission = "chanserv/saset/desc"; } -module { name = "cs_set_founder" } -command { service = "ChanServ"; name = "SET FOUNDER"; command = "chanserv/set/founder"; } -command { service = "ChanServ"; name = "SASET FOUNDER"; command = "chanserv/saset/founder"; permission = "chanserv/saset/founder"; } -module { name = "cs_set_keeptopic" } -command { service = "ChanServ"; name = "SET KEEPTOPIC"; command = "chanserv/set/keeptopic"; } -command { service = "ChanServ"; name = "SASET KEEPTOPIC"; command = "chanserv/saset/keeptopic"; permission = "chanserv/saset/keeptopic"; } -module { name = "cs_set_opnotice" } -command { service = "ChanServ"; name = "SET OPNOTICE"; command = "chanserv/set/opnotice"; } -command { service = "ChanServ"; name = "SASET OPNOTICE"; command = "chanserv/saset/opnotice"; permission = "chanserv/saset/optnotice"; } -module { name = "cs_set_peace" } -command { service = "ChanServ"; name = "SET PEACE"; command = "chanserv/set/peace"; } -command { service = "ChanServ"; name = "SASET PEACE"; command = "chanserv/saset/peace"; permission = "chanserv/saset/peace"; } -module { name = "cs_set_persist" } -command { service = "ChanServ"; name = "SET PERSIST"; command = "chanserv/set/persist"; } -command { service = "ChanServ"; name = "SASET PERSIST"; command = "chanserv/saset/persist"; permission = "chanserv/saset/persist"; } -module { name = "cs_set_private" } -command { service = "ChanServ"; name = "SET PRIVATE"; command = "chanserv/set/private"; } -command { service = "ChanServ"; name = "SASET PRIVATE"; command = "chanserv/saset/private"; permission = "chanserv/saset/private"; } -module { name = "cs_set_restricted" } -command { service = "ChanServ"; name = "SET RESTRICTED"; command = "chanserv/set/restricted"; } -command { service = "ChanServ"; name = "SASET RESTRICTED"; command = "chanserv/saset/restricted"; permission = "chanserv/saset/restricted"; } -module { name = "cs_set_secure" } -command { service = "ChanServ"; name = "SET SECURE"; command = "chanserv/set/secure"; } -command { service = "ChanServ"; name = "SASET SECURE"; command = "chanserv/saset/secure"; permission = "chanserv/saset/secure"; } -module { name = "cs_set_securefounder" } -command { service = "ChanServ"; name = "SET SECUREFOUNDER"; command = "chanserv/set/securefounder"; } -command { service = "ChanServ"; name = "SASET SECUREFOUNDER"; command = "chanserv/saset/securefounder"; permission = "chanserv/saset/securefounder"; } -module { name = "cs_set_secureops" } -command { service = "ChanServ"; name = "SET SECUREOPS"; command = "chanserv/set/secureops"; } -command { service = "ChanServ"; name = "SASET SECUREOPS"; command = "chanserv/saset/secureops"; permission = "chanserv/saset/secureops"; } -module { name = "cs_set_signkick" } -command { service = "ChanServ"; name = "SET SIGNKICK"; command = "chanserv/set/signkick"; } -command { service = "ChanServ"; name = "SASET SIGNKICK"; command = "chanserv/saset/signkick"; permission = "chanserv/saset/signkick"; } -module { name = "cs_set_successor" } -command { service = "ChanServ"; name = "SET SUCCESSOR"; command = "chanserv/set/successor"; } -command { service = "ChanServ"; name = "SASET SUCCESSOR"; command = "chanserv/saset/successor"; permission = "chanserv/saset/successor"; } -module { name = "cs_set_topiclock" } -command { service = "ChanServ"; name = "SET TOPICLOCK"; command = "chanserv/set/topiclock"; } -command { service = "ChanServ"; name = "SASET TOPICLOCK"; command = "chanserv/saset/topiclock"; permission = "chanserv/saset/topiclock"; } -module { name = "cs_saset_noexpire" } -command { service = "ChanServ"; name = "SASET NOEXPIRE"; command = "chanserv/saset/noexpire"; permission = "chanserv/saset/noexpire"; } -module { name = "cs_suspend" } -command { service = "ChanServ"; name = "SUSPEND"; command = "chanserv/suspend"; permission = "chanserv/suspend"; } -command { service = "ChanServ"; name = "UNSUSPEND"; command = "chanserv/unsuspend"; permission = "chanserv/suspend"; } -module { name = "cs_topic" } -command { service = "ChanServ"; name = "TOPIC"; command = "chanserv/topic"; } -module { name = "cs_unban" } -command { service = "ChanServ"; name = "UNBAN"; command = "chanserv/unban"; } -module { name = "cs_xop" } -command { service = "ChanServ"; name = "QOP"; command = "chanserv/qop"; } -command { service = "ChanServ"; name = "SOP"; command = "chanserv/sop"; } -command { service = "ChanServ"; name = "AOP"; command = "chanserv/aop"; } -command { service = "ChanServ"; name = "HOP"; command = "chanserv/hop"; } -command { service = "ChanServ"; name = "VOP"; command = "chanserv/vop"; } - -module { name = "gl_global" } -command { service = "Global"; name = "GLOBAL"; command = "global/global"; permission = "global/global"; } - -module { name = "hs_del" } -command { service = "HostServ"; name = "DEL"; command = "hostserv/del"; permission = "hostserv/del"; } -command { service = "HostServ"; name = "DELALL"; command = "hostserv/delall"; permission = "hostserv/del"; } -module { name = "hs_group" } -command { service = "HostServ"; name = "GROUP"; command = "hostserv/group"; } -module { name = "hs_list" } -command { service = "HostServ"; name = "LIST"; command = "hostserv/list"; permission = "hostserv/list"; } -module { name = "hs_off" } -command { service = "HostServ"; name = "OFF"; command = "hostserv/off"; } -module { name = "hs_on" } -command { service = "HostServ"; name = "ON"; command = "hostserv/on"; } -module { name = "hs_set" } -command { service = "HostServ"; name = "SET"; command = "hostserv/set"; permission = "hostserv/set"; } -command { service = "HostServ"; name = "SETALL"; command = "hostserv/setall"; permission = "hostserv/set"; } - -module { name = "ms_cancel" } -command { service = "MemoServ"; name = "CANCEL"; command = "memoserv/cancel"; } -module { name = "ms_check" } -command { service = "MemoServ"; name = "CHECK"; command = "memoserv/check"; } -module { name = "ms_del" } -command { service = "MemoServ"; name = "DEL"; command = "memoserv/del"; } -module { name = "ms_ignore" } -command { service = "MemoServ"; name = "IGNORE"; command = "memoserv/ignore"; } -module { name = "ms_info" } -command { service = "MemoServ"; name = "INFO"; command = "memoserv/info"; } -module { name = "ms_list" } -command { service = "MemoServ"; name = "LIST"; command = "memoserv/list"; } -module { name = "ms_read" } -command { service = "MemoServ"; name = "READ"; command = "memoserv/read"; } -module { name = "ms_rsend" } -command { service = "MemoServ"; name = "RSEND"; command = "memoserv/rsend"; } -module { name = "ms_send" } -command { service = "MemoServ"; name = "SEND"; command = "memoserv/send"; } -module { name = "ms_sendall" } -command { service = "MemoServ"; name = "SENDALL"; command = "memoserv/sendall"; permission = "memoserv/sendall"; } -module { name = "ms_set" } -command { service = "MemoServ"; name = "SET"; command = "memoserv/set"; } -module { name = "ms_staff" } -command { service = "MemoServ"; name = "STAFF"; command = "memoserv/staff"; permission = "memoserv/staff"; } - -module { name = "ns_access" } -command { service = "NickServ"; name = "ACCESS"; command = "nickserv/access"; } -module { name = "ns_ajoin" } -command { service = "NickServ"; name = "AJOIN"; command = "nickserv/ajoin"; } -module { name = "ns_alist" } -command { service = "NickServ"; name = "ALIST"; command = "nickserv/alist"; } -module { name = "ns_cert" } -command { service = "NickServ"; name = "CERT"; command = "nickserv/cert"; } -module { name = "ns_drop" } -command { service = "NickServ"; name = "DROP"; command = "nickserv/drop"; } -module { name = "ns_getemail" } -command { service = "NickServ"; name = "GETEMAIL"; command = "nickserv/getemail"; permission = "nickserv/getemail"; } -module { name = "ns_getpass" } -command { service = "NickServ"; name = "GETPASS"; command = "nickserv/getpass"; permission = "nickserv/getpass"; } -module { name = "ns_ghost" } -command { service = "NickServ"; name = "GHOST"; command = "nickserv/ghost"; } -module { name = "ns_group" } -command { service = "NickServ"; name = "GLIST"; command = "nickserv/glist"; } -command { service = "NickServ"; name = "GROUP"; command = "nickserv/group"; } -command { service = "NickServ"; name = "UNGROUP"; command = "nickserv/ungroup"; } -module { name = "ns_identify" } -command { service = "NickServ"; name = "ID"; command = "nickserv/identify"; } -command { service = "NickServ"; name = "IDENTIFY"; command = "nickserv/identify"; } -module { name = "ns_info" } -command { service = "NickServ"; name = "INFO"; command = "nickserv/info"; } -module { name = "ns_list" } -command { service = "NickServ"; name = "LIST"; command = "nickserv/list"; } -module { name = "ns_logout" } -command { service = "NickServ"; name = "LOGOUT"; command = "nickserv/logout"; } -module { name = "ns_recover" } -command { service = "NickServ"; name = "RECOVER"; command = "nickserv/recover"; } -module { name = "ns_register" } -command { service = "NickServ"; name = "CONFIRM"; command = "nickserv/confirm"; } -command { service = "NickServ"; name = "REGISTER"; command = "nickserv/register"; } -command { service = "NickServ"; name = "RESEND"; command = "nickserv/resend"; } -module { name = "ns_release" } -command { service = "NickServ"; name = "RELEASE"; command = "nickserv/release"; } -module { name = "ns_resetpass" } -command { service = "NickServ"; name = "RESETPASS"; command = "nickserv/resetpass"; } -module { name = "ns_saset" } -command { service = "NickServ"; name = "SASET"; command = "nickserv/saset"; } -command { service = "NickServ"; name = "SASET DISPLAY"; command = "nickserv/saset/display"; } -command { service = "NickServ"; name = "SASET PASSWORD"; command = "nickserv/saset/password"; } -module { name = "ns_set" } -command { service = "NickServ"; name = "SET"; command = "nickserv/set"; } -command { service = "NickServ"; name = "SET DISPLAY"; command = "nickserv/set/display"; } -command { service = "NickServ"; name = "SET PASSWORD"; command = "nickserv/set/password"; } -module { name = "ns_set_autoop" } -command { service = "NickServ"; name = "SET AUTOOP"; command = "nickserv/set/autoop"; } -command { service = "NickServ"; name = "SASET AUTOOP"; command = "nickserv/saset/autoop"; permission = "nickserv/saset/autoop"; } -module { name = "ns_set_email" } -command { service = "NickServ"; name = "SET EMAIL"; command = "nickserv/set/email"; } -command { service = "NickServ"; name = "SASET EMAIL"; command = "nickserv/saset/email"; permission = "nickserv/saset/email"; } -module { name = "ns_set_greet" } -command { service = "NickServ"; name = "SET GREET"; command = "nickserv/set/greet"; } -command { service = "NickServ"; name = "SASET GREET"; command = "nickserv/saset/greet"; permission = "nickserv/saset/greet"; } -module { name = "ns_set_hide" } -command { service = "NickServ"; name = "SET HIDE"; command = "nickserv/set/hide"; } -command { service = "NickServ"; name = "SASET HIDE"; command = "nickserv/saset/hide"; permission = "nickserv/saset/hide"; } -module { name = "ns_set_kill" } -command { service = "NickServ"; name = "SET KILL"; command = "nickserv/set/kill"; } -command { service = "NickServ"; name = "SASET KILL"; command = "nickserv/saset/kill"; permission = "nickserv/saset/kill"; } -module { name = "ns_set_language" } -command { service = "NickServ"; name = "SET LANGUAGE"; command = "nickserv/set/language"; } -command { service = "NickServ"; name = "SASET LANGUAGE"; command = "nickserv/saset/language"; permission = "nickserv/saset/language"; } -module { name = "ns_set_message" } -command { service = "NickServ"; name = "SET MESSAGE"; command = "nickserv/set/message"; } -command { service = "NickServ"; name = "SASET MESSAGE"; command = "nickserv/saset/message"; permission = "nickserv/saset/message"; } -module { name = "ns_set_private" } -command { service = "NickServ"; name = "SET PRIVATE"; command = "nickserv/set/private"; } -command { service = "NickServ"; name = "SASET PRIVATE"; command = "nickserv/saset/private"; permission = "nickserv/saset/private"; } -module { name = "ns_set_secure" } -command { service = "NickServ"; name = "SET SECURE"; command = "nickserv/set/secure"; } -command { service = "NickServ"; name = "SASET SECURE"; command = "nickserv/saset/secure"; permission = "nickserv/saset/secure"; } -module { name = "ns_saset_noexpire" } -command { service = "NickServ"; name = "SASET NOEXPIRE"; command = "nickserv/saset/noexpire"; permission = "nickserv/saset/noexpire" } -module { name = "ns_sendpass" } -command { service = "NickServ"; name = "SENDPASS"; command = "nickserv/sendpass"; permission = "nickserv/sendpass"; } -module { name = "ns_status" } -command { service = "NickServ"; name = "STATUS"; command = "nickserv/status"; } -module { name = "ns_suspend" } -command { service = "NickServ"; name = "SUSPEND"; command = "nickserv/suspend"; permission = "nickserv/suspend"; } -command { service = "NickServ"; name = "UNSUSPEND"; command = "nickserv/unsuspend"; permission = "nickserv/suspend"; } -module { name = "ns_update" } -command { service = "NickServ"; name = "UPDATE"; command = "nickserv/update"; } - -module { name = "os_akill" } -command { service = "OperServ"; name = "AKILL"; command = "operserv/akill"; permission = "operserv/akill"; } -module { name = "os_chankill" } -command { service = "OperServ"; name = "CHANKILL"; command = "operserv/chankill"; permission = "operserv/chankill"; } -module { name = "os_list" } -command { service = "OperServ"; name = "CHANLIST"; command = "operserv/chanlist"; } -command { service = "OperServ"; name = "USERLIST"; command = "operserv/userlist"; } -module { name = "os_config" } -command { service = "OperServ"; name = "CONFIG"; command = "operserv/config"; permission = "operserv/config"; } -module { name = "os_forbid" } -command { service = "OperServ"; name = "FORBID"; command = "operserv/forbid"; permission = "operserv/forbid"; } -module { name = "os_ignore" } -command { service = "OperServ"; name = "IGNORE"; command = "operserv/ignore"; permission = "operserv/ignore"; } -module { name = "os_jupe" } -command { service = "OperServ"; name = "JUPE"; command = "operserv/jupe"; permission = "operserv/jupe"; } -module { name = "os_kick" } -command { service = "OperServ"; name = "KICK"; command = "operserv/kick"; permission = "operserv/kick"; } -module { name = "os_kill" } -command { service = "OperServ"; name = "KILL"; command = "operserv/kill"; permission = "operserv/kill"; } -module { name = "os_login" } -command { service = "OperServ"; name = "LOGIN"; command = "operserv/login"; } -module { name = "os_mode" } -command { service = "OperServ"; name = "MODE"; command = "operserv/mode"; permission = "operserv/mode"; } -command { service = "OperServ"; name = "UMODE"; command = "operserv/umode"; permission = "operserv/umode"; } -module { name = "os_modinfo" } -command { service = "OperServ"; name = "MODINFO"; command = "operserv/modinfo"; } -command { service = "OperServ"; name = "MODLIST"; command = "operserv/modlist"; permission = "operserv/modlist"; } -module { name = "os_module" } -command { service = "OperServ"; name = "MODLOAD"; command = "operserv/modload"; permission = "operserv/modload"; } -command { service = "OperServ"; name = "MODRELOAD"; command = "operserv/modreload"; permission = "operserv/modload"; } -command { service = "OperServ"; name = "MODUNLOAD"; command = "operserv/modunload"; permission = "operserv/modload"; } -module { name = "os_news" } -command { service = "OperServ"; name = "LOGONNEWS"; command = "operserv/logonnews"; permission = "operserv/news"; } -command { service = "OperServ"; name = "OPERNEWS"; command = "operserv/opernews"; permission = "operserv/news"; } -command { service = "OperServ"; name = "RANDOMNEWS"; command = "operserv/randomnews"; permission = "operserv/news"; } -module { name = "os_noop" } -command { service = "OperServ"; name = "NOOP"; command = "operserv/noop"; permission = "operserv/noop"; } -module { name = "os_oline" } -command { service = "OperServ"; name = "OLINE"; command = "operserv/oline"; permission = "operserv/oline"; } -module { name = "os_oper" } -command { service = "OperServ"; name = "OPER"; command = "operserv/oper"; permission = "operserv/oper"; } -module { name = "os_reload" } -command { service = "OperServ"; name = "RELOAD"; command = "operserv/reload"; permission = "operserv/reload"; } -module { name = "os_session" } -command { service = "OperServ"; name = "EXCEPTION"; command = "operserv/exception"; permission = "operserv/exception"; } -command { service = "OperServ"; name = "SESSION"; command = "operserv/session"; permission = "operserv/session"; } -module { name = "os_set" } -command { service = "OperServ"; name = "SET"; command = "operserv/set"; permission = "operserv/set"; } -module { name = "os_shutdown" } -command { service = "OperServ"; name = "QUIT"; command = "operserv/quit"; permission = "operserv/quit"; } -command { service = "OperServ"; name = "RESTART"; command = "operserv/restart"; permission = "operserv/restart"; } -command { service = "OperServ"; name = "SHUTDOWN"; command = "operserv/shutdown"; permission = "operserv/shutdown"; } -module { name = "os_stats" } -command { service = "OperServ"; name = "STATS"; command = "operserv/stats"; permission = "operserv/stats"; } -module { name = "os_svsnick" } -command { service = "OperServ"; name = "SVSNICK"; command = "operserv/svsnick"; permission = "operserv/svsnick"; } -module { name = "os_sxline" } -command { service = "OperServ"; name = "SNLINE"; command = "operserv/snline"; permission = "operserv/snline"; } -command { service = "OperServ"; name = "SQLINE"; command = "operserv/sqline"; permission = "operserv/sqline"; } -command { service = "OperServ"; name = "SZLINE"; command = "operserv/szline"; permission = "operserv/szline"; } -module { name = "os_update" } -command { service = "OperServ"; name = "UPDATE"; command = "operserv/update"; permission = "operserv/update"; } - diff --git a/data/example.conf b/data/example.conf index 14065ddf4..a14cf9ef6 100644 --- a/data/example.conf +++ b/data/example.conf @@ -72,6 +72,27 @@ * program. */ +/* + * [OPTIONAL] Additional Includes + * + * You can include additional configuration files here. + * You may also include executable files, which will be executed and + * the output from it will be included into your configuration. + */ + +/* +include +{ + type = "file" + name = "some.conf" +} + +include +{ + type = "executable" + name = "/usr/bin/wget -q -O - http://some.miconfigured.network.com/services.conf" +} +*/ /* * [REQUIRED] IRCd Config @@ -171,6 +192,7 @@ serverinfo /* * [REQUIRED] Protocol module + * * This directive tells Anope which IRCd Protocol to speak when connecting. * You MUST modify this to match the IRCd you run. * @@ -463,800 +485,94 @@ options } /* - * [RECOMMENDED] Service client configuration + * [OPTIONAL] BotServ + * + * Includes botserv.example.conf, which is necessary for BotServ functionality. * - * Defines some of the core services. Commands are later added to these services. + * Remove this block to disable BotServ. */ - -service -{ - /* - * The nick, user name, host, and real name of the service. - */ - nick = "BotServ" - user = "services" - host = "localhost.net" - gecos = "Bot Service" -} - -service -{ - nick = "ChanServ" - user = "services" - host = "localhost.net" - gecos = "Channel Registration Service" -} - -service -{ - nick = "Global" - user = "services" - host = "localhost.net" - gecos = "Global Noticer" -} - -service -{ - nick = "HostServ" - user = "services" - host = "localhost.net" - gecos = "vHost Service" -} - -service -{ - nick = "MemoServ" - user = "services" - host = "localhost.net" - gecos = "Memo Service" -} - -service -{ - nick = "NickServ" - user = "services" - host = "localhost.net" - gecos = "Nickname Registration Service" -} - -service +include { - nick = "OperServ" - user = "services" - host = "localhost.net" - gecos = "Operator Service" + type = "file" + name = "botserv.example.comf" } /* - * [RECOMMENDED] Core clients + * [RECOMMENDED] ChanServ + * + * Includes chanserv.example.conf, which is necessary for ChanServ functionality. + * + * Remove this block to disable ChanServ. */ - -module { name = "bs_main" } -botserv +include { - /* - * The name of the client that should be BotServ. - * Clients are configured above. - */ - name = "BotServ" - - /* - * The default bot options for newly registered channels. Note that changing these options - * will have no effect on channels which are already registered. The list must be separated - * by spaces. - * - * The options are: - * - dontkickops: Channel operators will be protected against BotServ kicks - * - dontkickvoices: Voiced users will be protected against BotServ kicks - * - greet: The channel's BotServ bot will greet incoming users that have set a greet - * in their NickServ settings - * - fantasy: Enables the use of BotServ fantasy commands in the channel - * - * This directive is optional, if left blank, there will be no defaults. - */ - defaults="greet fantasy" - - /* - * The minimum number of users there must be in a channel before the bot joins it. The best - * value for this setting is 1 or 2. This can be 0, the service bots will not part unless - * specifically unassigned, and will keep the channel open. - */ - minusers = 1 - - /* - * The maximum number of entries a single bad words list can have. Setting it too high can - * reduce performance slightly. - */ - badwordsmax = 32 - - /* - * The amount of time that data for a user is valid in BotServ. If the data exceeds this time, - * it is reset or deleted depending on the case. Do not set it too high, otherwise your - * resources will be slightly affected. - */ - keepdata = 10m - - /* - * The bots are currently not affected by any modes or bans when they try to join a channel. - * But some people may want to make it act like a real bot, that is, for example, remove all - * the bans affecting the bot before joining the channel, remove a ban that affects the bot - * set by a user when it is in the channel, and so on. Since it consumes a bit more CPU - * time, you should not enable this on larger networks. - * - * This directive is optional. - */ - #smartjoin = yes - - /* - * If set, the bots will use a kick reason that does not state the word when it is kicking. - * This is especially useful if you have young people on your network. - * - * This directive is optional. - */ - gentlebadwordreason = yes - - /* - * If set, BotServ will use case sensitive checking for badwords. - * - * This directive is optional. - */ - #casesensitive = yes - - /* - * Defines the prefix for fantasy commands in channels. This character will have to be prepended - * to all fantasy commands. If you choose "!", for example, fantasy commands will be "!kick", - * "!op", etc. This directive is optional, if left out, the default fantasy character is "!". - */ - #fantasycharacter = "!" + type = "file" + name = "chanserv.example.conf" } /* - * [RECOMMENDED] Core ChanServ module. + * [RECOMMENDED] Global + * + * Includes global.example.conf, which is necessary for Global funcionality. * - * Provides essential functionality for ChanServ + * Remove this block to disable Global. */ -module { name = "cs_main" } - -chanserv -{ - /* - * The name of the client that should be ChanServ. Clients are configured - * with the service blocks. - */ - name = "ChanServ" - - /* - * The default options for newly registered channels. Note that changing these options - * will have no effect on channels which are already registered. The list must be separated - * by spaces. - * - * The options are: - * - keeptopic: Retain topic when the channel is not in use - * - opnotice: Send a notice when OP/DEOP commands are used - * - peace: Disallow users from kicking or removing modes from others who are of the same - * access level or superior - * - private: Hide the channel from ChanServ's LIST command - * - restricted: Kick/ban users who are restricted from the channel - * - secure: Enable channel security, requiring the user to be identified with NickServ in - * order to be considered for being on the access list of the channel - * - secureops: Only allow operator status to be given if the user is on the access list - * - securefounder: Only allow the real founder of the channel to drop the channel, change it's - * password, or change the founder or successor - * - signkick: Use of ChanServ's KICK command will cause the user's nick to be signed to the kick. - * - signkicklevel: Same as above, but the kick will not be signed if the user is at the same access - * level or superior to the target - * - topiclock: Disallow the topic to be changed except with ChanServ's TOPIC command - * - persist: Keep the channel open at all times - * - none: No defaults - * - * This directive is optional, if left blank, the options will default to keetopic, secure, securefounder, - * and signkick. If you really want no defaults, use "none" by itself as the option. - */ - defaults="keeptopic peace secure securefounder signkick" - - /* - * The maximum number of channels which may be registered to a single nickname. - * - * This directive is optional, but recommended. - * If not set, there will be no restriction on the numbers of channels a single nickname can have registered. - */ - maxregistered = 20 - - /* - * The length of time before a channel registration expires. - * - * This directive is optional, but recommended. - * If not set, the default is 14 days. - */ - expire = 14d - - /* - * The length of time before a suspended channel becomes unsuspended. - * - * This directive is optional. - * If not set, the default is to never. - */ - #suspendexpire = 90d - - /* - * The lenth of time before a forbidden channel drops. - * - * This directive is optional. - * If not set, the default is to never. - */ - #forbidexpire = 90d - - /* - * The default ban type for newly registered channels (and when importing old databases). - * - * defbantype can be: - * - * 0: ban in the form of *!user@host - * 1: ban in the form of *!*user@host - * 2: ban in the form of *!*@host - * 3: ban in the form of *!*user@*.domain - */ - defbantype = 2 - - /* - * The maximum number of entries on a channel's access list. - */ - accessmax = 1024 - - /* - * The maximum number of entries on a channel's autokick list. - */ - autokickmax = 32 - - /* - * The default reason for an autokick if none is given. - */ - autokickreason = "User has been banned from the channel" - - /* - * The length of time ChanServ stays in a channel after kicking a user from a channel they are not - * permitted to be in. This only occurs when the user is the only one in the channel. - */ - inhabit = 15s - - /* - * The maximum number of channels to be returned for a ChanServ LIST command. - */ - listmax = 50 - - /* - * Allow only IRC Operators to use ChanServ. - * - * This directive is optional. - */ - #opersonly = yes - - /* - * ChanServ levels configuration, for use with cs_access. - * - * These levels are used by the chanserv/access access system. - * The levels configured below will be used as a default by newly registered channels. - * - * The level "founder" is a special level that means anyone with the level_founder permission - * can use (which of course defaults to "founder"). Anyone with the founder level permission in - * a channel can do anything in the channel. - */ - level_change = 10 - level_list = 1 - level_akick = 10 - level_assign = "founder" - level_autohalfop = 4 - level_autoop = 5 - level_autoowner = 9999 - level_autoprotect = 10 - level_autovoice = 3 - level_badwords = 10 - level_ban = 4 - level_fantasia = 3 - level_founder = "founder" - level_getkey = 5 - level_greet = 5 - level_halfop = 5 - level_halfopme = 4 - level_info = 9999 - level_invite = 5 - level_kick = 4 - level_memo = 10 - level_mode = 5 - level_nokick = 1 - level_opdeop = 5 - level_opdeopme = 5 - level_owner = "founder" - level_ownerme = 9999 - level_protect = 9999 - level_protectme = 10 - level_say = 5 - level_signkick = 9999 - level_set = 9999 - level_topic = 5 - level_unban = 4 - level_voice = 4 - level_voiceme = 3 - - /* - * ChanServ flags configuration, for use with cs_flags. - * - * These flags are used by the chanserv/flags access system. - */ - flag_change = "f" - flag_list = "l" - flag_akick = "K" - flag_assign = "s" - flag_autohalfop = "H" - flag_autoop = "O" - flag_autoowner = "Q" - flag_autoprotect = "A" - flag_autovoice = "V" - flag_badwords = "k" - flag_ban = "b" - flag_fantasia = "c" - flag_founder = "F" - flag_getkey = "G" - flag_greet = "g" - flag_halfop = "h" - flag_halfopme = "h" - flag_info = "I" - flag_invite = "i" - flag_kick = "k" - flag_memo = "m" - flag_mode = "s" - flag_nokick = "N" - flag_opdeop = "o" - flag_opdeopme = "o" - flag_owner = "q" - flag_ownerme = "q" - flag_protect = "a" - flag_protectme = "a" - flag_say = "B" - flag_set = "s" - flag_signkick = "K" - flag_topic = "t" - flag_unban = "u" - flag_voice = "v" - flag_voiceme = "v" -} - -module { name = "gl_main" } -global +include { - /* - * The name of the client that should be Global. Clients are configured - * with the service blocks. - */ - name = "Global" - - /* - * If set, Services will send global messages on starting up and shutting - * down/restarting. - * - * This directive is optional. - */ - #globaloncycle = yes - - /* - * This is the global message that will be sent when Services are being - * shutdown/restarted. This directive is only required if you enable - * globaloncycle above. - */ - globaloncycledown = "Services are restarting, they will be back shortly - please be good while we're gone" - - /* - * This is the global message that will be sent when Services (re)join the - * network. This directive is only required if you enable globaloncycle above. - */ - globaloncycleup = "Services are now back online - have a nice day" - - /* - * If set, Services will hide the IRC operator's nick in a global - * message/notice. - * - * This directive is optional. - */ - #anonymousglobal = yes + type = "file" + name = "global.example.conf" } -module { name = "hs_main" } -hostserv +/* + * [OPTIONAL] HostServ + * + * Includes hostserv.example.conf, which is necessary for HostServ funcionality. + * + * Remove this block to disable HostServ. + */ +include { - /* - * The name of the client that should be HostServ. Clients are configured - * with the service blocks. - */ - name = "HostServ" + type = "file" + name = "hostserv.example.conf" } -module { name = "ms_main" } -memoserv +/* + * [OPTIONAL] MemoServ + * + * Includes memoserv.example.conf, which is necessary for MemoServ funcionality. + * + * Remove this block to disable MemoServ. + */ +include { - /* - * The name of the client that should be MemoServ. Clients are configured - * with the service blocks. - */ - name = "MemoServ" - - /* - * The maximum number of memos a user is allowed to keep by default. Normal users may set the - * limit anywhere between 0 and this value. Services Admins can change it to any value or - * disable it. - * - * This directive is optional, but recommended. If not set, the limit is disabled - * by default, and normal users can set any limit they want. - */ - maxmemos = 20 - - /* - * The delay between consecutive uses of the MemoServ SEND command. This can help prevent spam - * as well as denial-of-service attacks from sending large numbers of memos and filling up disk - * space (and memory). The default 3-second wait means a maximum average of 150 bytes of memo - * per second per user under the current IRC protocol. - * - * This directive is optional, but recommended. - */ - senddelay = 3s - - /* - * Allow the use of memo receipts for the following groups: - * - * 1 - Opers Only - * 2 - Everybody - * - * This directive is optional. - */ - #memoreceipt = 1 + type = "file" + name = "memoserv.example.conf" } -module { name = "ns_main" } -nickserv +/* + * [OPTIONAL] NickServ + * + * Includes memoserv.example.conf, which is necessary for NickServ funcionality. + * + * Remove this block to disable NickServ. + */ +include { - /* - * The name of the client that should be NickServ. Clients are configured - * with the service blocks. - */ - name = "NickServ" - - /* - * Force users to give an e-mail address when they register a nick. - * - * This directive is recommended to be enabled, and required if e-mail registration is enabled. - */ - forceemail = yes - - /* - * Require users who change their email address to confirm they - * own it. - */ - confirmemailchanges = no - - /* - * Require an e-mail to be sent to the user before they can register their nick. - */ - #emailregistration = yes - - /* - * The default options for newly registered nicks. Note that changing these options - * will have no effect on nicks which are already registered. The list must be separated - * by spaces. - * - * The options are: - * - kill: Kill nick if not identified within 60 seconds - * - killquick: Kill nick if not identified within 20 seconds, this one overrides the above - * option and the above must be specified with this one - * - secure: Enable nickname security, requiring the nick's password before any operations - * can be done on it - * - private: Hide the nick from NickServ's LIST command - * - hideemail: Hide's the nick's e-mail address from NickServ's INFO command - * - hideusermask: Hide's the nick's last or current user@host from NickServ's INFO command - * - hidequit: Hide's the nick's last quit message - * - memosignon: Notify user if they have a new memo when they sign into the nick - * - memoreceive: Notify user if they have a new memo as soon as it's received - * - autoop: User will be automatically opped in channels they enter and have access to - * - msg: Services messages will be sent as PRIVMSGs instead of NOTICEs, requires UsePrivmsg - * to be enabled as well - * - * This directive is optional, if left blank, the options will default to secure, memosignon, and - * memoreceive. If you really want no defaults, use "none" by itself as the option. - */ - defaults="secure private hideemail hideusermask memosignon memoreceive autoop" - - /* - * A list of languages to load on startup that will be available in /nickserv set language. - * Useful if you translate Anope to your language. (Explained further in docs/LANGUAGE). - * Note that english should not be listed here because it is the base language. - */ - languages = "ca_ES de_DE el_GR es_ES fr_FR hu_HU it_IT nl_NL pl_PL pt_PT ru_RU tr_TR" - - /* - * Default language that non- and newly-registered nicks will receive messages in. - * Leave empty to default to English. - */ - #defaultlanguage = "es_ES" - - /* - * The minimum length of time between consecutive uses of NickServ's REGISTER command. This - * directive is optional, but recommended. If not set, this restriction will be disabled. - */ - regdelay = 30s - - /* - * The minimum length of time between consecutive uses of NickServ's RESEND command. - * - * This directive is optional, but recommended. If not set, this restriction will be disabled. - */ - resenddelay = 90s - - /* - * The length of time before a nick registration expires. - * - * This directive is optional, but recommended. If not set, the default is 21 days. - */ - expire = 21d - - /* - * The length of time before a suspended nick becomes unsuspended. - * - * This directive is optional. If not set, the default is to never. - */ - #suspendexpire = 90d - - /* - * The length of time a user using an unconfirmed account has - * before the account will be released for general use again. - * - * This directive is only required if the e-mail registration option is enabled. - */ - #unconfirmedexpire = 1d - - /* - * The maximum number of nicks allowed in a group. - * - * This directive is optional, but recommended. If not set or set to 0, no limits will be applied. - */ - maxaliases = 16 - - /* - * The maximum number of entries allowed on a nickname's access list. - */ - accessmax = 32 - - /* - * The username (and possibly hostname) used for the fake user created when NickServ collides - * a user. Should be in the user@host format. - */ - enforceruser = "enforcer" - enforcerhost = "localhost.net" - - /* - * The delay before a NickServ collided nick is released. - */ - releasetimeout = 1m - - /* - * Allow the use of the IMMED option in the NickServ SET KILL command. - * - * This directive is optional. - */ - #allowkillimmed = yes - - /* - * If set, the NickServ GROUP command won't allow any group change. This is recommended for - * better performance and to protect against nick stealing, however users will have less - * flexibility. - * - * This directive is optional, but recommended. - */ - #nogroupchange = yes - - /* - * The maximum number of nicks to be returned for a NickServ LIST command. - */ - listmax = 50 - - /* - * When a user's nick is forcibly changed to enforce a "nick kill", their new nick will start - * with this value. The rest will be made up of 6 or 7 digits. - */ - guestnickprefix = "Guest" - - /* - * Prevents the use of the DROP, FORBID, GETPASS, and SET PASSWORD commands by Services Admins - * on other Services Admins or the Services Root(s). - * - * This directive is optional, but recommended. - */ - secureadmins = yes - - /* - * If set, any user wanting to use the privileges of Services Root, Services Admin, or Services - * Operator must have been logged as an IRC Operator with the /oper command. - * - * This directive is optional, but recommended. - */ - strictprivileges = yes - - /* - * If set, Services will set the channel modes a user has access to upon identifying, assuming - * they are not already set. - * - * This directive is optional. - */ - modeonid = yes - - /* - * If set, Services will add the usermask of registering users to the access list of their - * newly created account. If not set, users will always have to identify to NickServ before - * being recognized, unless they manually add an address to the access list of their account. - * This directive is optional. - */ - addaccessonreg = yes - - /* - * The maximum number of channels a user can have on NickServ's AJOIN command. - */ - ajoinmax = 10 + type = "file" + name = "nickserv.example.conf" } -module { name = "os_main" } -operserv +/* + * [RECOMMENDED] OperServ + * + * Includes operserv.example.conf, which is necessary for OperServ funcionality. + * + * Remove this block to disable OperServ. + */ +include { - /* - * The name of the client that should be OperServ. Clients are configured - * with the service blocks. - */ - name = "OperServ" - - /* - * If set, Services Admins will be able to use SUPERADMIN [ON|OFF] which will temporarily grant - * them extra privileges such as being a founder on ALL channels, ability to adjust another - * users' modes, etc. - * - * This directive is optional. - */ - #superadmin = yes - - /* - * These define the default expiration times for, respectively, AKILLs, CHANKILLs, SNLINEs, - * SQLINEs, and SZLINEs. - */ - autokillexpiry = 30d - chankillexpiry = 30d - snlineexpiry = 30d - sqlineexpiry = 30d - szlineexpiry = 30d - - /* - * If set, this option will make Services send an AKILL command immediately after it has been - * added with AKILL ADD. This eliminates the need for killing the user after the AKILL has - * been added. - * - * This directive is optional, but recommended. - */ - akillonadd = yes - - /* - * If set, this option will make Services send an (SVS)KILL command immediately after SNLINE ADD. - * This eliminates the need for killing the user after the SNLINE has been added. - * - *This directive is optional. - */ - #killonsnline = yes - - /* - * If set, this option will make Services send an (SVS)KILL command immediately after SQLINE ADD. - * This eliminates the need for killing the user after the SQLINE has been added. - * - * This directive is optional. - */ - #killonsqline = yes - - /* - * Defines what actions should trigger notifications. The list must be separated by spaces. - * - * The notifications are: - * - oper: A user has become an IRC operator - * - bados: A non-IRCop attempts to use OperServ - * - akillexpire: An AKILL has expired - * - snlineexpire: An SNLINE has expired - * - sqlineexpire: An SQLINE has expired - * - szlineexpire: An SZLINE has expired - * - exceptionexpire: A session exception has expired - * - * This directive is optional, if left blank, there will be no notifications. - */ - notifications="oper" - - /* - * Enables session limiting. Session limiting prevents users from connecting more than a certain - * number of times from the same host at the same time - thus preventing most types of cloning. - * Once a host reaches it's session limit, all clients attempting to connect from that host will - * be killed. Exceptions to the default session limit can be defined via the exception list. It - * should be noted that session limiting, along with a large exception list, can degrade Services' - * performance. - * - * See the source and comments in sessions.c and the online help for more information about - * session limiting. - * - * This directive is optional. - */ - limitsessions = yes - - /* - * Default session limit per host. Once a host reaches it's session limit, all clients attempting - * to connect from that host will be killed. A value of zero means an unlimited session limit. - * - * This directive is optional. - * If not given and session limiting is enabled, it will default to no limit. - */ - defaultsessionlimit = 3 - - /* - * The maximum session limit that may be set for a host in an exception. - * - * This directive is only required if session limiting is enabled. - */ - maxsessionlimit = 100 - - /* - * Sets the default expiry time for session exceptions. - * - * This directive is only required if session limiting is enabled. - */ - exceptionexpiry = 1d - - /* - * The message that will be NOTICE'd to a user just before they are removed from the network because - * their host's session limit has been exceeded. It may be used to give a slightly more descriptive - * reason for the impending kill as opposed to simply "Session limit exceeded". - * - * This directive is optional, if not set, nothing will be sent. - */ - sessionlimitexceeded = "The session limit for your host %s has been exceeded." - - /* - * Same as above, but should be used to provide a website address where users can find out more - * about session limits and how to go about applying for an exception. - * - * Note: This directive has been intentionally commented out in an effort to remind you to change - * the URL it contains. It is recommended that you supply an address/URL where people can get help - * regarding session limits. - * - * This directive is optional, if not set, nothing will be sent. - */ - #sessionlimitdetailsloc = "Please visit http://your.website.url/ for more information about session limits." - - /* - * If set and is not 0, this directive tells Services to add an AKILL the number of subsequent kills - * for the same host exceeds this value, preventing the network from experiencing KILL floods. - * - * This directive is optional. - */ - maxsessionkill = 15 - - /* - * Sets the expiry time for AKILLs set for hosts exceeding the maxsessionkill directive limit. - * - * This directive is optional, if not set, defaults to 30 minutes. - */ - sessionautokillexpiry = 30m - - /* - * Adds the nickname of the IRC Operator issuing an AKILL to the kill reason. - * - * This directive is optional. - */ - addakiller = yes - - /* - * If set, only IRC Operators will be permitted to use OperServ, regardless of module-based command - * access restrictions. - * - * This directive is optional, but recommended. - */ - opersonly = yes + type = "file" + name = "operserv.example.conf" } /* @@ -1608,6 +924,49 @@ dns timeout = 5 } +/* + * [REQUIRED] Database configuration. + * + * This section is used to configure databases used by Anope. + * You should at least load one database method, otherwise any data you + * have will not be stored! + */ + +/* + * db_plain + * + * This is the default flatfile database format. + */ +module { name = "db_plain" } +db_plain +{ + /* + * The database name db_plain should use + */ + database = "anope.db" +} + +/* + * db_mysql and db_mysql_live + * + * Enables (live) MySQL support. + * + * The db_mysql_live module is an extension to db_mysql, and should only be used if + * db_mysql is being used. This module pulls data in real time from SQL as it is + * requested by the core as a result of someone executing commands. + * + * This effectively allows you to edit your database and have it be immediately + * reflected back in Anope. + * + * For information on how to make db_mysql_live use asynchronous queries see + * m_async_commands. + * + * At this time db_mysql_live only supports pulling data in real time from the three + * main tables: anope_cs_info, anope_ns_alias, and anope_ns_core. + */ +#module { name = "db_mysql" } +#module { name = "db_mysql_live" } + /* * [REQUIRED] Encryption modules. * @@ -1653,34 +1012,10 @@ module { name = "enc_md5" } */ #module { name = "enc_old" } -/* - * [OPTIONAL] Additional includes - * - * You can include additional configuration files here. - * You may also include executable files, which will be executed and - * the output from it will be included into your configuration. - */ - -/* Include core commands */ -include -{ - type = "file" - name = "commands.example.conf" -} - /* Extra (optional) modules */ include { - type = "file" - name = "modules.example.conf" + type = "file" + name = "modules.example.conf" } -/* -include -{ - type = "executable" - name = "/usr/bin/wget -q -O - http://some.miconfigured.network.com/services.conf" -} -*/ - - diff --git a/data/global.example.conf b/data/global.example.conf new file mode 100644 index 000000000..5a3566e4e --- /dev/null +++ b/data/global.example.conf @@ -0,0 +1,101 @@ +/* + * Example configuration file for Global. + */ + +/* + * First, create the service. + */ +service +{ + /* + * The name of the Global client + */ + nick = "Global" + + /* + * The username of the Global client. + */ + user = "services" + + /* + * The hostname of the Global client. + */ + host = "localhost.net" + + /* + * The realname of the Global client. + */ + gecos = "Global Noticer" +} + +/* + * Core Global module. + * + * Provides essential functionality for Global. + */ +module { name = "gl_main" } + +/* + * Configuration for Global provided by gl_main. + */ +global +{ + /* + * The name of the client that should be Global. + */ + name = "Global" + + /* + * If set, Services will send global messages on starting up and shutting + * down/restarting. + * + * This directive is optional. + */ + #globaloncycle = yes + + /* + * This is the global message that will be sent when Services are being + * shutdown/restarted. This directive is only required if you enable + * globaloncycle above. + */ + globaloncycledown = "Services are restarting, they will be back shortly - please be good while we're gone" + + /* + * This is the global message that will be sent when Services (re)join the + * network. This directive is only required if you enable globaloncycle above. + */ + globaloncycleup = "Services are now back online - have a nice day" + + /* + * If set, Services will hide the IRC operator's nick in a global + * message/notice. + * + * This directive is optional. + */ + #anonymousglobal = yes +} + +/* + * Core Global commands. + * + * In Anope modules can provide (multiple) commands, each of which has a unique command name. Once these modules + * are loaded you can then configure the commands to be added to any client you like with any name you like. + * + * Additionally, you may provide a permission name that must be in the opertype of users executing the command. + * + * Sane defaults are provided below that do not need to be edited unless you wish to change the default behavior. + */ + +/* Give it a help command */ +command { service = "Global"; name = "HELP"; command = "generic/help"; } + +/* + * gl_global + * + * Provides the command global/global. + * + * Used for sending a message to every online user. + */ +module { name = "gl_global" } +command { service = "Global"; name = "GLOBAL"; command = "global/global"; permission = "global/global"; } + diff --git a/data/hostserv.example.conf b/data/hostserv.example.conf new file mode 100644 index 000000000..0d04d764e --- /dev/null +++ b/data/hostserv.example.conf @@ -0,0 +1,150 @@ +/* + * Example configuration file for HostServ. + */ + +/* + * First, create the service. + */ +service +{ + /* + * The name of the HostServ client + */ + nick = "HostServ" + + /* + * The username of the HostServ client. + */ + user = "services" + + /* + * The hostname of the HostServ client. + */ + host = "localhost.net" + + /* + * The realname of the HostServ client. + */ + gecos = "vHost Service" +} + +/* + * Core HostServ module. + * + * Provides essential functionality for HostServ. + */ +module { name = "hs_main" } + +/* + * Configuration for HostServ provided by hs_main. + */ +hostserv +{ + /* + * The name of the client that should be HostServ. + */ + name = "HostServ" +} + +/* + * Core HostServ commands. + * + * In Anope modules can provide (multiple) commands, each of which has a unique command name. Once these modules + * are loaded you can then configure the commands to be added to any client you like with any name you like. + * + * Additionally, you may provide a permission name that must be in the opertype of users executing the command. + * + * Sane defaults are provided below that do not need to be edited unless you wish to change the default behavior. + */ + +/* Give it a help command */ +command { service = "HostServ"; name = "HELP"; command = "generic/help"; } + +/* + * hs_del + * + * Provides the commands hostserv/del and hostserv/delall. + * + * Used for removing users vHosts. + */ +module { name = "hs_del" } +command { service = "HostServ"; name = "DEL"; command = "hostserv/del"; permission = "hostserv/del"; } +command { service = "HostServ"; name = "DELALL"; command = "hostserv/delall"; permission = "hostserv/del"; } + +/* + * hs_group + * + * Provides the command hostserv/group. + * + * Used for grouping one vHost to many nicks. + */ +module { name = "hs_group" } +command { service = "HostServ"; name = "GROUP"; command = "hostserv/group"; } + +/* + * hs_list + * + * Provides the command hostserv/list. + * + * Used for listing actively set vHosts. + */ +module { name = "hs_list" } +command { service = "HostServ"; name = "LIST"; command = "hostserv/list"; permission = "hostserv/list"; } + +/* + * hs_off + * + * Provides the command hostserv/off. + * + * Used for turning off your vHost. + */ +module { name = "hs_off" } +command { service = "HostServ"; name = "OFF"; command = "hostserv/off"; } + +/* + * hs_on + * + * Provides the command hostserv/on. + * + * Used for turning on your vHost. + */ +module { name = "hs_on" } +command { service = "HostServ"; name = "ON"; command = "hostserv/on"; } + +/* + * hs_request + * + * Provides the commands hostserv/request, hostserv/active, hostserv/reject, and hostserv/waiting. + * + * Used to manage vhosts requested by users. + */ +module { name = "hs_request" } +command { service = "HostServ"; name = "REQUEST"; command = "hostserv/request"; } +command { service = "HostServ"; name = "ACTIVATE"; command = "hostserv/activate"; } +command { service = "HostServ"; name = "REJECT"; command = "hostserv/reject"; } +command { service = "HostServ"; name = "WAITING"; command = "hostserv/waiting"; } +hs_request +{ + /* + * If set, Services will send a memo to the user requesting a vHost when it's been + * approved or rejected. + */ + #memouser = yes + + /* + * If set, Services will send a memo to all Services staff when a new vHost is requested. + */ + #memooper = yes +} + +/* + * hs_set + * + * Provides the commands hostserv/set and hostserv/setall. + * + * Used for setting users vhosts. + */ +module { name = "hs_set" } +command { service = "HostServ"; name = "SET"; command = "hostserv/set"; permission = "hostserv/set"; } +command { service = "HostServ"; name = "SETALL"; command = "hostserv/setall"; permission = "hostserv/set"; } + diff --git a/data/memoserv.example.conf b/data/memoserv.example.conf new file mode 100644 index 000000000..2ce15b5fe --- /dev/null +++ b/data/memoserv.example.conf @@ -0,0 +1,215 @@ +/* + * Example configuration file for MemoServ. + */ + +/* + * First, create the service. + */ +service +{ + /* + * The name of the MemoServ client + */ + nick = "MemoServ" + + /* + * The username of the MemoServ client. + */ + user = "services" + + /* + * The hostname of the MemoServ client. + */ + host = "localhost.net" + + /* + * The realname of the MemoServ client. + */ + gecos = "Memo Service" +} + +/* + * Core MemoServ module. + * + * Provides essential functionality for MemoServ. + */ +module { name = "ms_main" } + +/* + * Configuration for MemoServ provided by ms_main. + */ +memoserv +{ + /* + * The name of the client that should be MemoServ. Clients are configured + * with the service blocks. + */ + name = "MemoServ" + + /* + * The maximum number of memos a user is allowed to keep by default. Normal users may set the + * limit anywhere between 0 and this value. Services Admins can change it to any value or + * disable it. + * + * This directive is optional, but recommended. If not set, the limit is disabled + * by default, and normal users can set any limit they want. + */ + maxmemos = 20 + + /* + * The delay between consecutive uses of the MemoServ SEND command. This can help prevent spam + * as well as denial-of-service attacks from sending large numbers of memos and filling up disk + * space (and memory). The default 3-second wait means a maximum average of 150 bytes of memo + * per second per user under the current IRC protocol. + * + * This directive is optional, but recommended. + */ + senddelay = 3s + + /* + * Allow the use of memo receipts for the following groups: + * + * 1 - Opers Only + * 2 - Everybody + * + * This directive is optional. + */ + #memoreceipt = 1 +} + +/* + * Core MemoServ commands. + * + * In Anope modules can provide (multiple) commands, each of which has a unique command name. Once these modules + * are loaded you can then configure the commands to be added to any client you like with any name you like. + * + * Additionally, you may provide a permission name that must be in the opertype of users executing the command. + * + * Sane defaults are provided below that do not need to be edited unless you wish to change the default behavior. + */ + +/* Give it a help command */ +command { service = "MemoServ"; name = "HELP"; command = "generic/help"; } + +/* + * ms_cancel + * + * Provides the command memoserv/cancel. + * + * Used to cancel memos already sent but not yet read. + */ +module { name = "ms_cancel" } +command { service = "MemoServ"; name = "CANCEL"; command = "memoserv/cancel"; } + +/* + * ms_check + * + * Provides the command memoserv/check. + * + * Used to check if a sent memo has been read. + */ +module { name = "ms_check" } +command { service = "MemoServ"; name = "CHECK"; command = "memoserv/check"; } + +/* + * ms_del + * + * Provides the command memoserv/del. + * + * Used to delete your memos. + */ +module { name = "ms_del" } +command { service = "MemoServ"; name = "DEL"; command = "memoserv/del"; } + +/* + * ms_ignore + * + * Provides the command memoserv/ignore. + * + * Used to ignore memos from specific users. + */ +module { name = "ms_ignore" } +command { service = "MemoServ"; name = "IGNORE"; command = "memoserv/ignore"; } + +/* + * ms_info + * + * Provides the command memoserv/info. + * + * Used to show memo related information about an account or a channel. + */ +module { name = "ms_info" } +command { service = "MemoServ"; name = "INFO"; command = "memoserv/info"; } + +/* + * ms_list + * + * Provides the command memoserv/list. + * + * Used to list your current memos. + */ +module { name = "ms_list" } +command { service = "MemoServ"; name = "LIST"; command = "memoserv/list"; } + +/* + * ms_read + * + * Provides the command memoserv/read. + * + * Used to read your memos. + */ +module { name = "ms_read" } +command { service = "MemoServ"; name = "READ"; command = "memoserv/read"; } + +/* + * ms_rsend + * + * Provides the command memoserv/rsend. + * + * Used to send a memo requiring a receipt be sent back once it is read. + * + * Requires configuring memoserv:memoreceipt. + */ +#module { name = "ms_rsend" } +#command { service = "MemoServ"; name = "RSEND"; command = "memoserv/rsend"; } + +/* + * ms_send + * + * Provides the command memoserv/send + * + * Used to send memos. + */ +module { name = "ms_send" } +command { service = "MemoServ"; name = "SEND"; command = "memoserv/send"; } + +/* + * ms_sendlal + * + * Provides the command memoserv/sendall + * + * Used to send a mass memo to every registered user. + */ +module { name = "ms_sendall" } +command { service = "MemoServ"; name = "SENDALL"; command = "memoserv/sendall"; permission = "memoserv/sendall"; } + +/* + * ms_set + * + * Provides the command memoserv/set. + * + * Used to set settings such as how you are notified of new memos, and your memo limit. + */ +module { name = "ms_set" } +command { service = "MemoServ"; name = "SET"; command = "memoserv/set"; } + +/* + * ms_staff + * + * Provides the command memoserv/staff. + * + * Used to send a memo to all registered staff members. + */ +module { name = "ms_staff" } +command { service = "MemoServ"; name = "STAFF"; command = "memoserv/staff"; permission = "memoserv/staff"; } + diff --git a/data/modules.example.conf b/data/modules.example.conf index 4a492bfda..8ee15877e 100644 --- a/data/modules.example.conf +++ b/data/modules.example.conf @@ -8,129 +8,13 @@ */ /* - * bs_autoassign + * help * - * Allows service bots to be automatically assigned to channels upon registration. - */ -#module { name = "bs_autoassign" } -bs_autoassign -{ - /* - * Automatically assign ChanServ to channels upon registration. - */ - bot = "ChanServ" -} - -/* - * cs_appendtopic + * Provides commands generic/help * - * Adds the APPENDTOPIC command to ChanServ, which allows users to easially append text to - * the end of existing channel topics. + * Is a generic help command that can be used with any client. */ -module { name = "cs_appendtopic" } -command { service = "ChanServ"; name = "APPENDTOPIC"; command = "chanserv/appendtopic"; } - -/* - * cs_enforce - * - * Adds the ENFORCE commad to ChanServ, which allows enforcing various channel settings like - * SECUREOPS and RESTRICTED. - */ -module { name = "cs_enforce" } -command { service = "ChanServ"; name = "ENFORCE"; command = "chanserv/enforce"; } - -/* - * cs_entrymsg - * - * Allows you to set entry messages on your channel, which are shown to anyone - * who joins. - */ -module { name = "cs_entrymsg" } -command { service = "ChanServ"; name = "ENTRYMSG"; command = "chanserv/entrymsg"; } -cs_entrymsg -{ - /* The maximum number of entrymsgs allowed per channel. If not set, defaults to 5. */ - maxentries = 5 -} - -/* - * cs_set_misc - * - * Allows you to create misc /chanserv set commands, and have the data - * show up in /chanserv info. - */ -module { name = "cs_set_misc" } -command { service = "ChanServ"; name = "SET URL"; command = "chanserv/set/misc"; } -command { service = "ChanServ"; name = "SET EMAIL"; command = "chanserv/set/misc"; } - -/* - * cs_sync - * - * Adds the chanserv/sync command, which syncs users channel status modes on a channel - * with their access level. -*/ -#module { name = "cs_sync" } -#command { service = "ChanServ"; name = "SYNC"; command = "chanserv/sync"; } - -/* - * db_plain - * - * This is the default flatfile database format. You must have at least one database - * module loaded or Anope will not save databases! - */ -module { name = "db_plain" } -db_plain -{ - /* - * The database db_plain should use - */ - database = "anope.db" -} - -/* - * db_mysql and db_mysql_live - * - * Enables (live) MySQL support. - * - * The db_mysql_live module is an extension to db_mysql, and should only be used if - * db_mysql is being used. This module pulls data in real time from SQL as it is - * requested by the core as a result of someone executing commands. - * - * This effectively allows you to edit your database and have it be immediately - * reflected back in Anope. - * - * For information on how to make db_mysql_live use asynchronous queries see - * m_async_commands. - * - * At this time db_mysql_live only supports pulling data in real time from the three - * main tables: anope_cs_info, anope_ns_alias, and anope_ns_core. - */ -#module { name = "db_mysql" } -#module { name = "db_mysql_live" } - -/* - * hs_request - * - * Allows users to request vhosts which opers may then view, accept or deny - */ -module { name = "hs_request" } -command { service = "HostServ"; name = "REQUEST"; command = "hostserv/request"; } -command { service = "HostServ"; name = "ACTIVATE"; command = "hostserv/activate"; } -command { service = "HostServ"; name = "REJECT"; command = "hostserv/reject"; } -command { service = "HostServ"; name = "WAITING"; command = "hostserv/waiting"; } -hs_request -{ - /* - * If set, Services will send a memo to the user requesting a vHost when it's been - * approved or rejected. - */ - #memouser = yes - - /* - * If set, Services will send a memo to all Services staff when a new vHost is requested. - */ - #memooper = yes -} +module { name = "help" } /* * m_async_commands @@ -403,118 +287,3 @@ ns_maxemail #maxemails = 1 } -/* - * ns_set_misc - * - * Allows you to create misc /nickserv set commands, and have the data - * show up in /nickserv info. - */ -module { name = "ns_set_misc" } -command { service = "NickServ"; name = "SET URL"; command = "nickserv/set/misc"; } -command { service = "NickServ"; name = "SET ICQ"; command = "nickserv/set/misc"; } - -/* - * os_defcon - * - * Allows you to set services in defcon mode, which can be used to restrict services access - * during bot attacks. - */ -#module { name = "os_defcon" } -command { service = "OperServ"; name = "DEFCON"; command = "operserv/defcon"; } -defcon -{ - /* - * Default DefCon level (1-5) to use when starting Services up. Level 5 constitutes normal operation - * while level 1 constitutes the most restrictive operation. If this setting is left out or set to - * 0, DefCon will be disabled and the rest of this block will be ignored. - */ - #defaultlevel = 5 - - /* - * The following 4 directives define what operations will take place when DefCon is set to levels - * 1 through 4. Each level is a list that must be separated by spaces. - * - * The following operations can be defined at each level: - * - nonewchannels: Disables registering new channels - * - nonewnicks: Disables registering new nicks - * - nomlockchanges: Disables changing MLOCK on registered channels - * - forcechanmodes: Forces all channels to have the modes given in the later chanmodes directive - * - reducedsessions: Reduces the session limit to the value given in the later sessionlimit directive - * - nonewclients: KILL any new clients trying to connect - * - operonly: Services will ignore all non-IRCops - * - silentoperonly: Services will silently ignore all non-IRCops - * - akillnewclients: AKILL any new clients trying to connect - * - nonewmemos: No new memos will be sent to block MemoServ attacks - */ - level4 = "nonewchannels nonewnicks nomlockchanges reducedsessions" - level3 = "nonewchannels nonewnicks nomlockchanges forcechanmodes reducedsessions" - level2 = "nonewchannels nonewnicks nomlockchanges forcechanmodes reducedsessions silentoperonly" - level1 = "nonewchannels nonewnicks nomlockchanges forcechanmodes reducedsessions silentoperonly akillnewclients" - - /* - * New session limit to use when a DefCon level is using "reduced" session limiting. - */ - #sessionlimit = 2 - - /* - * Length of time to add an AKILL for when DefCon is preventing new clients from connecting to the - * network. - */ - #akillexpire = 5m - - /* - * The channel modes to set on all channels when the DefCon channel mode system is in use. - * - * Note 1: Choose these modes carefully, because when DefCon switches to a level which does NOT have - * the mode setting selected, Services will set the reverse on all channels, e.g. if this setting - * is +RN when DefCon is used, all channels will be set to +RN, when DefCon is removed, all - * channels will be set to -RN. You don't want to set this to +k for example, because when DefCon - * is removed all channels with -k. - * - * Note 2: MLOCKed modes will not be lost. - */ - #chanmodes = "+R" - - /* - * This value can be used to automatically return the network to DefCon level 5 after the specified - * time period, just in case any IRC Operator forgets to remove a DefCon setting. - * - * This directive is optional. - */ - #timeout = 15m - - /* - * If set, Services will send a global message on DefCon level changes. - * - * This directive is optional. - */ - #globalondefcon = yes - - /* - * If set, Services will send the global message defined in the message directive on DefCon level - * changes. - * - * This directive is optional. - */ - #globalondefconmore = yes - - /* - * Defines the message that will be sent on DefCon level changes when globalondefconmore is set. - * - * This directive is required only when globalondefconmore is set. - */ - #message = "Put your message to send your users here. Dont forget to uncomment globalondefconmore" - - /* - * Defines the message that will be sent when DefCon is returned to level 5. This directive is optional, - * and will also override globalondefcon and globalondefconmore when set. - */ - #offmessage = "Services are now back to normal, sorry for any inconvenience" - - /* - * Defines the reason to use when clients are KILLed or AKILLed from the network while the proper - * DefCon operation is in effect. - */ - #akillreason = "This network is currently not accepting connections, please try again later" -} - diff --git a/data/mysql/tables.sql b/data/mysql/tables.sql deleted file mode 100644 index 4f9665869..000000000 --- a/data/mysql/tables.sql +++ /dev/null @@ -1,425 +0,0 @@ --- phpMyAdmin SQL Dump --- version 3.3.5 --- http://www.phpmyadmin.net --- --- Host: localhost --- Generation Time: Apr 28, 2011 at 07:08 PM --- Server version: 5.1.50 --- PHP Version: 5.3.3-pl1-gentoo - -SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO"; - --- --- Database: `anope` --- - --- -------------------------------------------------------- - --- --- Table structure for table `anope_bs_badwords` --- - -CREATE TABLE IF NOT EXISTS `anope_bs_badwords` ( - `channel` varchar(255) NOT NULL DEFAULT '', - `word` varchar(255) NOT NULL, - `type` varchar(50) NOT NULL, - UNIQUE KEY `channel` (`channel`,`word`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - --- -------------------------------------------------------- - --- --- Table structure for table `anope_bs_core` --- - -CREATE TABLE IF NOT EXISTS `anope_bs_core` ( - `nick` varchar(255) NOT NULL DEFAULT '', - `user` varchar(255) NOT NULL DEFAULT '', - `host` text NOT NULL, - `rname` text NOT NULL, - `flags` text NOT NULL, - `created` int(10) unsigned NOT NULL DEFAULT '0', - `chancount` int(11) NOT NULL DEFAULT '0', - PRIMARY KEY (`nick`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - --- -------------------------------------------------------- - --- --- Table structure for table `anope_bs_info_metadata` --- - -CREATE TABLE IF NOT EXISTS `anope_bs_info_metadata` ( - `botname` varchar(255) NOT NULL DEFAULT '', - `name` varchar(255) NOT NULL DEFAULT '', - `value` text NOT NULL, - KEY `FK_anope_bs_info_metadata_botname` (`botname`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - --- -------------------------------------------------------- - --- --- Table structure for table `anope_cs_access` --- - -CREATE TABLE IF NOT EXISTS `anope_cs_access` ( - `provider` varchar(255) NOT NULL DEFAULT '', - `data` varchar(255) NOT NULL DEFAULT '', - `mask` varchar(255) NOT NULL DEFAULT '', - `channel` varchar(255) NOT NULL DEFAULT '', - `last_seen` int(10) unsigned NOT NULL DEFAULT '0', - `creator` varchar(255) NOT NULL DEFAULT '', - `created` int(11) unsigned NOT NULL DEFAULT '0', - UNIQUE KEY `channel` (`channel`,`display`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - --- -------------------------------------------------------- - --- --- Table structure for table `anope_cs_akick` --- - -CREATE TABLE IF NOT EXISTS `anope_cs_akick` ( - `channel` varchar(255) NOT NULL DEFAULT '', - `flags` varchar(255) NOT NULL DEFAULT '', - `mask` varchar(255) NOT NULL DEFAULT '', - `reason` text NOT NULL, - `creator` varchar(255) NOT NULL DEFAULT '', - `created` int(10) unsigned NOT NULL DEFAULT '0', - `last_used` int(10) unsigned NOT NULL DEFAULT '0', - UNIQUE KEY `channel` (`channel`,`mask`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - --- -------------------------------------------------------- - --- --- Table structure for table `anope_cs_info` --- - -CREATE TABLE IF NOT EXISTS `anope_cs_info` ( - `name` varchar(255) NOT NULL DEFAULT '', - `founder` text NOT NULL, - `successor` text NOT NULL, - `descr` text NOT NULL, - `time_registered` int(10) unsigned NOT NULL DEFAULT '0', - `last_used` int(10) unsigned NOT NULL DEFAULT '0', - `last_topic` text NOT NULL, - `last_topic_setter` text NOT NULL, - `last_topic_time` int(10) unsigned NOT NULL DEFAULT '0', - `flags` text NOT NULL, - `bantype` smallint(6) NOT NULL DEFAULT '0', - `memomax` smallint(5) unsigned NOT NULL DEFAULT '0', - `botnick` varchar(255) NOT NULL DEFAULT '', - `botflags` text NOT NULL, - `capsmin` smallint(6) NOT NULL DEFAULT '0', - `capspercent` smallint(6) NOT NULL DEFAULT '0', - `floodlines` smallint(6) NOT NULL DEFAULT '0', - `floodsecs` smallint(6) NOT NULL DEFAULT '0', - `repeattimes` smallint(6) NOT NULL DEFAULT '0', - PRIMARY KEY (`name`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - --- -------------------------------------------------------- - --- --- Table structure for table `anope_cs_info_metadata` --- - -CREATE TABLE IF NOT EXISTS `anope_cs_info_metadata` ( - `channel` varchar(255) NOT NULL DEFAULT '', - `name` varchar(255) NOT NULL DEFAULT '', - `value` text NOT NULL, - KEY `FK_anope_cs_info_metadata_channel` (`channel`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - --- -------------------------------------------------------- - --- --- Table structure for table `anope_cs_levels` --- - -CREATE TABLE IF NOT EXISTS `anope_cs_levels` ( - `channel` varchar(255) NOT NULL DEFAULT '', - `position` int(11) NOT NULL DEFAULT '0', - `level` int(11) NOT NULL DEFAULT '0', - UNIQUE KEY `channel` (`channel`,`position`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - --- -------------------------------------------------------- - --- --- Table structure for table `anope_cs_mlock` --- - -CREATE TABLE IF NOT EXISTS `anope_cs_mlock` ( - `channel` varchar(255) NOT NULL, - `mode` varchar(127) NOT NULL, - `status` int(11) NOT NULL, - `setter` varchar(255) NOT NULL, - `created` int(11) NOT NULL, - `param` varchar(255) NOT NULL, - UNIQUE KEY `entry` (`channel`,`mode`,`status`,`setter`,`param`), - KEY `FK_anope_cs_mlock` (`channel`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - --- -------------------------------------------------------- - --- --- Table structure for table `anope_cs_ttb` --- - -CREATE TABLE IF NOT EXISTS `anope_cs_ttb` ( - `channel` varchar(255) NOT NULL DEFAULT '', - `ttb_id` int(11) NOT NULL DEFAULT '0', - `value` int(11) NOT NULL DEFAULT '0', - UNIQUE KEY `channel` (`channel`,`ttb_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - --- -------------------------------------------------------- - --- --- Table structure for table `anope_extra` --- - -CREATE TABLE IF NOT EXISTS `anope_extra` ( - `data` text NOT NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - --- -------------------------------------------------------- - --- --- Table structure for table `anope_hs_core` --- - -CREATE TABLE IF NOT EXISTS `anope_hs_core` ( - `nick` varchar(255) NOT NULL, - `vident` varchar(64) NOT NULL, - `vhost` varchar(255) NOT NULL, - `creator` varchar(255) NOT NULL, - `time` int(11) NOT NULL, - KEY `FK_anope_hs_core_nick` (`nick`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - --- -------------------------------------------------------- - --- --- Table structure for table `anope_info` --- - -CREATE TABLE IF NOT EXISTS `anope_info` ( - `version` int(11) DEFAULT NULL, - `date` datetime DEFAULT NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - --- -------------------------------------------------------- - --- --- Table structure for table `anope_ms_info` --- - -CREATE TABLE IF NOT EXISTS `anope_ms_info` ( - `receiver` varchar(255) NOT NULL, - `flags` int(11) NOT NULL DEFAULT '0', - `time` int(10) unsigned NOT NULL DEFAULT '0', - `sender` text NOT NULL, - `text` blob NOT NULL, - `serv` enum('NICK','CHAN') NOT NULL DEFAULT 'NICK', - KEY `FK_anope_ms_info_receiver` (`receiver`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - --- -------------------------------------------------------- - --- --- Table structure for table `anope_ns_access` --- - -CREATE TABLE IF NOT EXISTS `anope_ns_access` ( - `display` varchar(255) NOT NULL DEFAULT '', - `access` varchar(160) NOT NULL, - KEY `FK_anope_ns_access_display` (`display`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - --- -------------------------------------------------------- - --- --- Table structure for table `anope_ns_alias` --- - -CREATE TABLE IF NOT EXISTS `anope_ns_alias` ( - `nick` varchar(255) NOT NULL DEFAULT '', - `last_quit` text NOT NULL, - `last_realname` text NOT NULL, - `last_usermask` text NOT NULL, - `time_registered` int(10) unsigned NOT NULL DEFAULT '0', - `last_seen` int(10) unsigned NOT NULL DEFAULT '0', - `flags` text NOT NULL, - `display` varchar(255) NOT NULL DEFAULT '', - PRIMARY KEY (`nick`), - KEY `FK_anope_ns_alias_display` (`display`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - --- -------------------------------------------------------- - --- --- Table structure for table `anope_ns_alias_metadata` --- - -CREATE TABLE IF NOT EXISTS `anope_ns_alias_metadata` ( - `nick` varchar(255) NOT NULL DEFAULT '', - `name` varchar(255) NOT NULL DEFAULT '', - `value` text NOT NULL, - KEY `FK_anope_ns_alias_metadata_nick` (`nick`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - --- -------------------------------------------------------- - --- --- Table structure for table `anope_ns_core` --- - -CREATE TABLE IF NOT EXISTS `anope_ns_core` ( - `display` varchar(255) NOT NULL DEFAULT '', - `pass` text NOT NULL, - `email` text NOT NULL, - `greet` text NOT NULL, - `flags` text NOT NULL, - `language` varchar(5) NOT NULL DEFAULT '', - `memomax` smallint(5) unsigned NOT NULL DEFAULT '0', - PRIMARY KEY (`display`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - --- -------------------------------------------------------- - --- --- Table structure for table `anope_ns_core_metadata` --- - -CREATE TABLE IF NOT EXISTS `anope_ns_core_metadata` ( - `nick` varchar(255) NOT NULL DEFAULT '', - `name` varchar(255) NOT NULL DEFAULT '', - `value` text NOT NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - --- -------------------------------------------------------- - --- --- Table structure for table `anope_os_core` --- - -CREATE TABLE IF NOT EXISTS `anope_os_core` ( - `maxusercnt` int(11) NOT NULL DEFAULT '0', - `maxusertime` int(10) unsigned NOT NULL DEFAULT '0', -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - --- -------------------------------------------------------- - --- --- Table structure for table `anope_os_exceptions` --- - -CREATE TABLE IF NOT EXISTS `anope_os_exceptions` ( - `mask` varchar(255) NOT NULL, - `slimit` int(11) NOT NULL DEFAULT '0', - `who` text NOT NULL, - `reason` text NOT NULL, - `time` int(10) unsigned NOT NULL DEFAULT '0', - `expires` int(10) unsigned NOT NULL DEFAULT '0' -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - --- -------------------------------------------------------- - --- --- Table structure for table `anope_os_xlines` --- - -CREATE TABLE IF NOT EXISTS `anope_os_xlines` ( - `type` varchar(1) NOT NULL, - `mask` varchar(255) NOT NULL, - `xby` text NOT NULL, - `reason` text NOT NULL, - `seton` int(10) unsigned NOT NULL DEFAULT '0', - `expire` int(10) unsigned NOT NULL DEFAULT '0' -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - --- --- Constraints for dumped tables --- - --- --- Constraints for table `anope_bs_badwords` --- -ALTER TABLE `anope_bs_badwords` - ADD CONSTRAINT `FK_anope_bs_badwords_channel` FOREIGN KEY (`channel`) REFERENCES `anope_cs_info` (`name`) ON DELETE CASCADE ON UPDATE CASCADE; - --- --- Constraints for table `anope_bs_info_metadata` --- -ALTER TABLE `anope_bs_info_metadata` - ADD CONSTRAINT `FK_anope_bs_info_metadata_botname` FOREIGN KEY (`botname`) REFERENCES `anope_bs_core` (`nick`) ON DELETE CASCADE ON UPDATE CASCADE; - --- --- Constraints for table `anope_cs_access` --- -ALTER TABLE `anope_cs_access` - ADD CONSTRAINT `FK_anope_cs_access_channel` FOREIGN KEY (`channel`) REFERENCES `anope_cs_info` (`name`) ON DELETE CASCADE ON UPDATE CASCADE; - --- --- Constraints for table `anope_cs_akick` --- -ALTER TABLE `anope_cs_akick` - ADD CONSTRAINT `FK_anope_cs_akick_channel` FOREIGN KEY (`channel`) REFERENCES `anope_cs_info` (`name`) ON DELETE CASCADE ON UPDATE CASCADE; - --- --- Constraints for table `anope_cs_info_metadata` --- -ALTER TABLE `anope_cs_info_metadata` - ADD CONSTRAINT `FK_anope_cs_info_metadata_channel` FOREIGN KEY (`channel`) REFERENCES `anope_cs_info` (`name`) ON DELETE CASCADE ON UPDATE CASCADE; - --- --- Constraints for table `anope_cs_levels` --- -ALTER TABLE `anope_cs_levels` - ADD CONSTRAINT `FK_anope_cs_levels_channel` FOREIGN KEY (`channel`) REFERENCES `anope_cs_info` (`name`) ON DELETE CASCADE ON UPDATE CASCADE; - --- --- Constraints for table `anope_cs_mlock` --- -ALTER TABLE `anope_cs_mlock` - ADD CONSTRAINT `FK_anope_cs_mlock_channel` FOREIGN KEY (`channel`) REFERENCES `anope_cs_info` (`name`) ON DELETE CASCADE ON UPDATE CASCADE; - --- --- Constraints for table `anope_cs_ttb` --- -ALTER TABLE `anope_cs_ttb` - ADD CONSTRAINT `FK_anope_cs_ttb_channel` FOREIGN KEY (`channel`) REFERENCES `anope_cs_info` (`name`) ON DELETE CASCADE ON UPDATE CASCADE; - --- --- Constraints for table `anope_hs_core` --- -ALTER TABLE `anope_hs_core` - ADD CONSTRAINT `FK_anope_hs_core_nick` FOREIGN KEY (`nick`) REFERENCES `anope_ns_alias` (`nick`) ON DELETE CASCADE ON UPDATE CASCADE; - --- --- Constraints for table `anope_ms_info` --- -ALTER TABLE `anope_ms_info` - ADD CONSTRAINT `FK_anope_ms_info_receiver` FOREIGN KEY (`receiver`) REFERENCES `anope_ns_alias` (`nick`) ON DELETE CASCADE ON UPDATE CASCADE; - --- --- Constraints for table `anope_ns_access` --- -ALTER TABLE `anope_ns_access` - ADD CONSTRAINT `FK_anope_ns_access_display` FOREIGN KEY (`display`) REFERENCES `anope_ns_core` (`display`) ON DELETE CASCADE ON UPDATE CASCADE; - --- --- Constraints for table `anope_ns_alias` --- -ALTER TABLE `anope_ns_alias` - ADD CONSTRAINT `FK_anope_ns_alias_display` FOREIGN KEY (`display`) REFERENCES `anope_ns_core` (`display`) ON DELETE CASCADE ON UPDATE CASCADE; - --- --- Constraints for table `anope_ns_alias_metadata` --- -ALTER TABLE `anope_ns_alias_metadata` - ADD CONSTRAINT `FK_anope_ns_alias_metadata_nick` FOREIGN KEY (`nick`) REFERENCES `anope_ns_alias` (`nick`) ON DELETE CASCADE ON UPDATE CASCADE; - diff --git a/data/nickserv.example.conf b/data/nickserv.example.conf new file mode 100644 index 000000000..5f9ff769d --- /dev/null +++ b/data/nickserv.example.conf @@ -0,0 +1,606 @@ +/* + * Example configuration file for NickServ. + */ + +/* + * First, create the service. + */ +service +{ + /* + * The name of the NickServ client + */ + nick = "NickServ" + + /* + * The username of the NickServ client. + */ + user = "services" + + /* + * The hostname of the NickServ client. + */ + host = "localhost.net" + + /* + * The realname of the NickServ client. + */ + gecos = "Nickname Registration Service" +} + +/* + * Core NickServ module. + * + * Provides essential functionality for NickServ. + */ +module { name = "ns_main" } + +/* + * Configuration for NickServ provided by ns_main. + */ +nickserv +{ + /* + * The name of the client that should be NickServ. + */ + name = "NickServ" + + /* + * Force users to give an e-mail address when they register a nick. + * + * This directive is recommended to be enabled, and required if e-mail registration is enabled. + */ + forceemail = yes + + /* + * Require users who change their email address to confirm they + * own it. + */ + confirmemailchanges = no + + /* + * Require an e-mail to be sent to the user before they can register their nick. + */ + #emailregistration = yes + + /* + * The default options for newly registered nicks. Note that changing these options + * will have no effect on nicks which are already registered. The list must be separated + * by spaces. + * + * The options are: + * - kill: Kill nick if not identified within 60 seconds + * - killquick: Kill nick if not identified within 20 seconds, this one overrides the above + * option and the above must be specified with this one + * - secure: Enable nickname security, requiring the nick's password before any operations + * can be done on it + * - private: Hide the nick from NickServ's LIST command + * - hideemail: Hide's the nick's e-mail address from NickServ's INFO command + * - hideusermask: Hide's the nick's last or current user@host from NickServ's INFO command + * - hidequit: Hide's the nick's last quit message + * - memosignon: Notify user if they have a new memo when they sign into the nick + * - memoreceive: Notify user if they have a new memo as soon as it's received + * - autoop: User will be automatically opped in channels they enter and have access to + * - msg: Services messages will be sent as PRIVMSGs instead of NOTICEs, requires UsePrivmsg + * to be enabled as well + * + * This directive is optional, if left blank, the options will default to secure, memosignon, and + * memoreceive. If you really want no defaults, use "none" by itself as the option. + */ + defaults="secure private hideemail hideusermask memosignon memoreceive autoop" + + /* + * A list of languages to load on startup that will be available in /nickserv set language. + * Useful if you translate Anope to your language. (Explained further in docs/LANGUAGE). + * Note that english should not be listed here because it is the base language. + */ + languages = "ca_ES de_DE el_GR es_ES fr_FR hu_HU it_IT nl_NL pl_PL pt_PT ru_RU tr_TR" + + /* + * Default language that non- and newly-registered nicks will receive messages in. + * Leave empty to default to English. + */ + #defaultlanguage = "es_ES" + + /* + * The minimum length of time between consecutive uses of NickServ's REGISTER command. This + * directive is optional, but recommended. If not set, this restriction will be disabled. + */ + regdelay = 30s + + /* + * The minimum length of time between consecutive uses of NickServ's RESEND command. + * + * This directive is optional, but recommended. If not set, this restriction will be disabled. + */ + resenddelay = 90s + + /* + * The length of time before a nick registration expires. + * + * This directive is optional, but recommended. If not set, the default is 21 days. + */ + expire = 21d + + /* + * The length of time before a suspended nick becomes unsuspended. + * + * This directive is optional. If not set, the default is to never. + */ + #suspendexpire = 90d + + /* + * The length of time a user using an unconfirmed account has + * before the account will be released for general use again. + * + * This directive is only required if the e-mail registration option is enabled. + */ + #unconfirmedexpire = 1d + + /* + * The maximum number of nicks allowed in a group. + * + * This directive is optional, but recommended. If not set or set to 0, no limits will be applied. + */ + maxaliases = 16 + + /* + * The maximum number of entries allowed on a nickname's access list. + */ + accessmax = 32 + + /* + * The username (and possibly hostname) used for the fake user created when NickServ collides + * a user. Should be in the user@host format. + */ + enforceruser = "enforcer" + enforcerhost = "localhost.net" + + /* + * The delay before a NickServ collided nick is released. + */ + releasetimeout = 1m + + /* + * Allow the use of the IMMED option in the NickServ SET KILL command. + * + * This directive is optional. + */ + #allowkillimmed = yes + + /* + * If set, the NickServ GROUP command won't allow any group change. This is recommended for + * better performance and to protect against nick stealing, however users will have less + * flexibility. + * + * This directive is optional, but recommended. + */ + #nogroupchange = yes + + /* + * The maximum number of nicks to be returned for a NickServ LIST command. + */ + listmax = 50 + + /* + * When a user's nick is forcibly changed to enforce a "nick kill", their new nick will start + * with this value. The rest will be made up of 6 or 7 digits. + */ + guestnickprefix = "Guest" + + /* + * Prevents the use of the DROP, FORBID, GETPASS, and SET PASSWORD commands by Services Admins + * on other Services Admins or the Services Root(s). + * + * This directive is optional, but recommended. + */ + secureadmins = yes + + /* + * If set, any user wanting to use the privileges of Services Root, Services Admin, or Services + * Operator must have been logged as an IRC Operator with the /oper command. + * + * This directive is optional, but recommended. + */ + strictprivileges = yes + + /* + * If set, Services will set the channel modes a user has access to upon identifying, assuming + * they are not already set. + * + * This directive is optional. + */ + modeonid = yes + + /* + * If set, Services will add the usermask of registering users to the access list of their + * newly created account. If not set, users will always have to identify to NickServ before + * being recognized, unless they manually add an address to the access list of their account. + * This directive is optional. + */ + addaccessonreg = yes + + /* + * The maximum number of channels a user can have on NickServ's AJOIN command. + */ + ajoinmax = 10 +} + +/* + * Core NickServ commands. + * + * In Anope modules can provide (multiple) commands, each of which has a unique command name. Once these modules + * are loaded you can then configure the commands to be added to any client you like with any name you like. + * + * Additionally, you may provide a permission name that must be in the opertype of users executing the command. + * + * Sane defaults are provided below that do not need to be edited unless you wish to change the default behavior. + */ + +/* Give it a help command */ +command { service = "NickServ"; name = "HELP"; command = "generic/help"; } + +/* + * ns_access + * + * Provides the command nickserv/access. + * + * Used for configuring what hosts have access to your account. + */ +module { name = "ns_access" } +command { service = "NickServ"; name = "ACCESS"; command = "nickserv/access"; } + +/* + * ns_ajoin + * + * Provides the command nickserv/ajoin. + * + * Used for configuring channels to join once you identify. + */ +module { name = "ns_ajoin" } +command { service = "NickServ"; name = "AJOIN"; command = "nickserv/ajoin"; } + +/* + * ns_alist + * + * Provides the command nickserv/alist. + * + * Used for viewing what channels you have access to. + */ +module { name = "ns_alist" } +command { service = "NickServ"; name = "ALIST"; command = "nickserv/alist"; } + +/* + * ns_cert + * + * Provides the command nickserv/cert. + * + * Used for configuring your SSL certificate list, which can be used to automatically identify you. + */ +module { name = "ns_cert" } +command { service = "NickServ"; name = "CERT"; command = "nickserv/cert"; } + +/* + * ns_drop + * + * Provides the command nickserv/drop. + * + * Used for unregistering names. + */ +module { name = "ns_drop" } +command { service = "NickServ"; name = "DROP"; command = "nickserv/drop"; } + +/* + * ns_getemail + * + * Provides the command nickserv/getemail. + * + * Used for getting registered accounts by searching for emails. + */ +module { name = "ns_getemail" } +command { service = "NickServ"; name = "GETEMAIL"; command = "nickserv/getemail"; permission = "nickserv/getemail"; } + +/* + * ns_getpass + * + * Provides the command nickserv/getpass. + * + * Used for getting users passwords. + * + * Requires no encryption is being used. + */ +#module { name = "ns_getpass" } +#command { service = "NickServ"; name = "GETPASS"; command = "nickserv/getpass"; permission = "nickserv/getpass"; } + +/* + * ns_ghost + * + * Provides the command nickserv/ghost. + * + * Used for disconnecting "ghost" sessions. + */ +module { name = "ns_ghost" } +command { service = "NickServ"; name = "GHOST"; command = "nickserv/ghost"; } + +/* + * ns_group + * + * Provides the commands nickserv/group, nickserv/glist, and nickserv/ungroup. + * + * Used for controlling nick groups. + */ +module { name = "ns_group" } +command { service = "NickServ"; name = "GLIST"; command = "nickserv/glist"; } +command { service = "NickServ"; name = "GROUP"; command = "nickserv/group"; } +command { service = "NickServ"; name = "UNGROUP"; command = "nickserv/ungroup"; } + +/* + * ns_identify + * + * Provides the command nickserv/identify. + * + * Used for identifying to accounts. + */ +module { name = "ns_identify" } +command { service = "NickServ"; name = "ID"; command = "nickserv/identify"; } +command { service = "NickServ"; name = "IDENTIFY"; command = "nickserv/identify"; } + +/* + * ns_info + * + * Provides the command nickserv/info. + * + * Used for gathering information about an account. + */ +module { name = "ns_info" } +command { service = "NickServ"; name = "INFO"; command = "nickserv/info"; } + +/* + * ns_list + * + * Provides the command nickserv/list. + * + * Used for retrieving and searching the registered account list. + */ +module { name = "ns_list" } +command { service = "NickServ"; name = "LIST"; command = "nickserv/list"; } + +/* + * ns_logout + * + * Provides the command nickserv/logout. + * + * Used for logging out of your account. + */ +module { name = "ns_logout" } +command { service = "NickServ"; name = "LOGOUT"; command = "nickserv/logout"; } + +/* + * ns_recover + * + * Provides the command nickserv/recover. + * + * Used for forcing someone on your nick to a guest nick. + */ +module { name = "ns_recover" } +command { service = "NickServ"; name = "RECOVER"; command = "nickserv/recover"; } + +/* + * ns_register + * + * Provides the commands nickserv/confirm, nickserv/register, and nickserv/resend. + * + * Used for registering accounts. + */ +module { name = "ns_register" } +command { service = "NickServ"; name = "CONFIRM"; command = "nickserv/confirm"; } +command { service = "NickServ"; name = "REGISTER"; command = "nickserv/register"; } +command { service = "NickServ"; name = "RESEND"; command = "nickserv/resend"; } + +/* + * ns_release + * + * Provides the command nickserv/release. + * + * Used for releasing names held by nickserv/recover. + */ +module { name = "ns_release" } +command { service = "NickServ"; name = "RELEASE"; command = "nickserv/release"; } + +/* + * ns_resetpass + * + * Provides the command nickserv/resetpass. + * + * Used for resetting passwords by emailing users a temporary one. + */ +module { name = "ns_resetpass" } +command { service = "NickServ"; name = "RESETPASS"; command = "nickserv/resetpass"; } + +/* + * ns_saset + * + * Provides commands nickserv/saset, nickserv/saset/display, and nickserv/saset/password. + * + * Used as a help wrapper for SASET commands, and used to force change users display name or password. + */ +module { name = "ns_saset" } +command { service = "NickServ"; name = "SASET"; command = "nickserv/saset"; } +command { service = "NickServ"; name = "SASET DISPLAY"; command = "nickserv/saset/display"; } +command { service = "NickServ"; name = "SASET PASSWORD"; command = "nickserv/saset/password"; } + +/* + * ns_set + * + * Provides the commands nickserv/set, nickserv/set/display, and nickserv/set/password. + * + * Used as a help wrapper for SET commands, and used for users to change their display name or password. + */ +module { name = "ns_set" } +command { service = "NickServ"; name = "SET"; command = "nickserv/set"; } +command { service = "NickServ"; name = "SET DISPLAY"; command = "nickserv/set/display"; } +command { service = "NickServ"; name = "SET PASSWORD"; command = "nickserv/set/password"; } + +/* + * ns_set_autoop + * + * Provides the commands nickserv/set/autoop and nickserv/saset/autoop. + * + * Used for setting autoop, which determines whether or not modes are automatically set on you when joining a channel. + */ +module { name = "ns_set_autoop" } +command { service = "NickServ"; name = "SET AUTOOP"; command = "nickserv/set/autoop"; } +command { service = "NickServ"; name = "SASET AUTOOP"; command = "nickserv/saset/autoop"; permission = "nickserv/saset/autoop"; } + +/* + * ns_set_email + * + * Provides the commands nickserv/set/email and nickserv/saset/email. + * + * Used for setting users email addresses. + */ +module { name = "ns_set_email" } +command { service = "NickServ"; name = "SET EMAIL"; command = "nickserv/set/email"; } +command { service = "NickServ"; name = "SASET EMAIL"; command = "nickserv/saset/email"; permission = "nickserv/saset/email"; } + +/* + * ns_set_greet + * + * Provides the commands nickserv/set/greet and nickserv/saset/greet. + * + * Used for changing users greet message, which is displayed when they enter channels. + */ +module { name = "ns_set_greet" } +command { service = "NickServ"; name = "SET GREET"; command = "nickserv/set/greet"; } +command { service = "NickServ"; name = "SASET GREET"; command = "nickserv/saset/greet"; permission = "nickserv/saset/greet"; } + +/* + * ns_set_hide + * + * Provides the commands nickserv/set/hide and nickserv/saset/hide. + * + * Used for configuring which options are publically shown in nickserv/info for your account. + */ +module { name = "ns_set_hide" } +command { service = "NickServ"; name = "SET HIDE"; command = "nickserv/set/hide"; } +command { service = "NickServ"; name = "SASET HIDE"; command = "nickserv/saset/hide"; permission = "nickserv/saset/hide"; } + +/* + * ns_set_kill + * + * Provides the commands nickserv/set/kill and nickserv/saset/kill. + * + * Used for configuring if and for how long other users are allowed to use your nick. + */ +module { name = "ns_set_kill" } +command { service = "NickServ"; name = "SET KILL"; command = "nickserv/set/kill"; } +command { service = "NickServ"; name = "SASET KILL"; command = "nickserv/saset/kill"; permission = "nickserv/saset/kill"; } + +/* + * ns_set_language + * + * Provides the commands nickserv/set/language and nickserv/saset/language. + * + * Used for configuring what language services use when messaging you. + */ +module { name = "ns_set_language" } +command { service = "NickServ"; name = "SET LANGUAGE"; command = "nickserv/set/language"; } +command { service = "NickServ"; name = "SASET LANGUAGE"; command = "nickserv/saset/language"; permission = "nickserv/saset/language"; } + +/* + * ns_set_message + * + * Provides the commands nickserv/set/message and nickserv/saset/message. + * + * Used to configure how services send messages to you. + */ +module { name = "ns_set_message" } +command { service = "NickServ"; name = "SET MESSAGE"; command = "nickserv/set/message"; } +command { service = "NickServ"; name = "SASET MESSAGE"; command = "nickserv/saset/message"; permission = "nickserv/saset/message"; } + +/* + * ns_set_private + * + * Provides the commands nickserv/set/private and nickserv/saset/private. + * + * Used for configuring whether or not your account shows up in nickserv/list. + */ +module { name = "ns_set_private" } +command { service = "NickServ"; name = "SET PRIVATE"; command = "nickserv/set/private"; } +command { service = "NickServ"; name = "SASET PRIVATE"; command = "nickserv/saset/private"; permission = "nickserv/saset/private"; } + +/* + * ns_set_secure + * + * Provides the commands nickserv/set/secure and nickserv/saset/secure. + * + * Used for configuring whether you can gain access to your nick by simply being recognized by nickserv/access. + */ +module { name = "ns_set_secure" } +command { service = "NickServ"; name = "SET SECURE"; command = "nickserv/set/secure"; } +command { service = "NickServ"; name = "SASET SECURE"; command = "nickserv/saset/secure"; permission = "nickserv/saset/secure"; } + +/* + * ns_saset_noexpire + * + * Provides the command nickserv/saset/noexpire. + * + * Used for configuring noexpire, which prevents nicks from expiring. + */ +module { name = "ns_saset_noexpire" } +command { service = "NickServ"; name = "SASET NOEXPIRE"; command = "nickserv/saset/noexpire"; permission = "nickserv/saset/noexpire" } + +/* + * ns_sendpass + * + * Provides the command nickserv/sendpass. + * + * Used to send users their password via email. + * + * Requires that no encryption is being used. + */ +module { name = "ns_sendpass" } +command { service = "NickServ"; name = "SENDPASS"; command = "nickserv/sendpass"; permission = "nickserv/sendpass"; } + +/* + * ns_set_misc + * + * Provides the command nickserv/set/misc. + * + * Allows you to create arbitrary commands to set data, and have that data show up in nickserv/info. + */ +module { name = "ns_set_misc" } +command { service = "NickServ"; name = "SET URL"; command = "nickserv/set/misc"; } +command { service = "NickServ"; name = "SET ICQ"; command = "nickserv/set/misc"; } + +/* + * ns_status + * + * Provides the nickserv/status command. + * + * Used to determine if a user is recognized or identified by services. + */ +module { name = "ns_status" } +command { service = "NickServ"; name = "STATUS"; command = "nickserv/status"; } + +/* + * ns_suspend + * + * Provides the commands nickserv/suspend and nickserv/unsuspend. + * + * Used to suspend and unsuspend nicknames. Suspended nicknames can not be used but their settings are stored. + */ +module { name = "ns_suspend" } +command { service = "NickServ"; name = "SUSPEND"; command = "nickserv/suspend"; permission = "nickserv/suspend"; } +command { service = "NickServ"; name = "UNSUSPEND"; command = "nickserv/unsuspend"; permission = "nickserv/suspend"; } + +/* + * ns_update + * + * Provides the command nickserv/update. + * + * Used to update your status on all channels, turn on your vHost, etc. + */ +module { name = "ns_update" } +command { service = "NickServ"; name = "UPDATE"; command = "nickserv/update"; } + diff --git a/data/operserv.example.conf b/data/operserv.example.conf new file mode 100644 index 000000000..48d6c4b42 --- /dev/null +++ b/data/operserv.example.conf @@ -0,0 +1,579 @@ +/* + * Example configuration file for OperServ. + */ + +/* + * First, create the service. + */ +service +{ + /* + * The name of the OperServ client + */ + nick = "OperServ" + + /* + * The username of the OperServ client. + */ + user = "services" + + /* + * The hostname of the OperServ client. + */ + host = "localhost.net" + + /* + * The realname of the OperServ client. + */ + gecos = "Operator Service" +} + +/* + * Core OperServ module. + * + * Provides essential functionality for OperServ. + */ +module { name = "os_main" } + +/* + * Configuration for OperServ provided by os_main. + */ +operserv +{ + /* + * The name of the client that should be OperServ. + */ + name = "OperServ" + + /* + * If set, Services Admins will be able to use SUPERADMIN [ON|OFF] which will temporarily grant + * them extra privileges such as being a founder on ALL channels. + * + * This directive is optional. + */ + #superadmin = yes + + /* + * These define the default expiration times for, respectively, AKILLs, CHANKILLs, SNLINEs, + * SQLINEs, and SZLINEs. + */ + autokillexpiry = 30d + chankillexpiry = 30d + snlineexpiry = 30d + sqlineexpiry = 30d + szlineexpiry = 30d + + /* + * If set, this option will make Services send an AKILL command immediately after it has been + * added with AKILL ADD. This eliminates the need for killing the user after the AKILL has + * been added. + * + * This directive is optional, but recommended. + */ + akillonadd = yes + + /* + * If set, this option will make Services send an (SVS)KILL command immediately after SNLINE ADD. + * This eliminates the need for killing the user after the SNLINE has been added. + * + *This directive is optional. + */ + #killonsnline = yes + + /* + * If set, this option will make Services send an (SVS)KILL command immediately after SQLINE ADD. + * This eliminates the need for killing the user after the SQLINE has been added. + * + * This directive is optional. + */ + #killonsqline = yes + + /* + * Defines what actions should trigger notifications. The list must be separated by spaces. + * + * The notifications are: + * - oper: A user has become an IRC operator + * - bados: A non-IRCop attempts to use OperServ + * - akillexpire: An AKILL has expired + * - snlineexpire: An SNLINE has expired + * - sqlineexpire: An SQLINE has expired + * - szlineexpire: An SZLINE has expired + * - exceptionexpire: A session exception has expired + * + * This directive is optional, if left blank, there will be no notifications. + */ + notifications = "oper" + + /* + * Enables session limiting. Session limiting prevents users from connecting more than a certain + * number of times from the same host at the same time - thus preventing most types of cloning. + * Once a host reaches it's session limit, all clients attempting to connect from that host will + * be killed. Exceptions to the default session limit can be defined via the exception list. It + * should be noted that session limiting, along with a large exception list, can degrade Services' + * performance. + * + * See the source and comments in sessions.c and the online help for more information about + * session limiting. + * + * This directive is optional. + */ + limitsessions = yes + + /* + * Default session limit per host. Once a host reaches it's session limit, all clients attempting + * to connect from that host will be killed. A value of zero means an unlimited session limit. + * + * This directive is optional. + * If not given and session limiting is enabled, it will default to no limit. + */ + defaultsessionlimit = 3 + + /* + * The maximum session limit that may be set for a host in an exception. + * + * This directive is only required if session limiting is enabled. + */ + maxsessionlimit = 100 + + /* + * Sets the default expiry time for session exceptions. + * + * This directive is only required if session limiting is enabled. + */ + exceptionexpiry = 1d + + /* + * The message that will be NOTICE'd to a user just before they are removed from the network because + * their host's session limit has been exceeded. It may be used to give a slightly more descriptive + * reason for the impending kill as opposed to simply "Session limit exceeded". + * + * This directive is optional, if not set, nothing will be sent. + */ + sessionlimitexceeded = "The session limit for your host %s has been exceeded." + + /* + * Same as above, but should be used to provide a website address where users can find out more + * about session limits and how to go about applying for an exception. + * + * Note: This directive has been intentionally commented out in an effort to remind you to change + * the URL it contains. It is recommended that you supply an address/URL where people can get help + * regarding session limits. + * + * This directive is optional, if not set, nothing will be sent. + */ + #sessionlimitdetailsloc = "Please visit http://your.website.url/ for more information about session limits." + + /* + * If set and is not 0, this directive tells Services to add an AKILL the number of subsequent kills + * for the same host exceeds this value, preventing the network from experiencing KILL floods. + * + * This directive is optional. + */ + maxsessionkill = 15 + + /* + * Sets the expiry time for AKILLs set for hosts exceeding the maxsessionkill directive limit. + * + * This directive is optional, if not set, defaults to 30 minutes. + */ + sessionautokillexpiry = 30m + + /* + * Adds the nickname of the IRC Operator issuing an AKILL to the kill reason. + * + * This directive is optional. + */ + addakiller = yes + + /* + * If set, only IRC Operators will be permitted to use OperServ, regardless of module-based command + * access restrictions. + * + * This directive is optional, but recommended. + */ + opersonly = yes +} + +/* + * Core OperServ commands. + * + * In Anope modules can provide (multiple) commands, each of which has a unique command name. Once these modules + * are loaded you can then configure the commands to be added to any client you like with any name you like. + * + * Additionally, you may provide a permission name that must be in the opertype of users executing the command. + * + * Sane defaults are provided below that do not need to be edited unless you wish to change the default behavior. + */ + +/* Give it a help command */ +command { service = "OperServ"; name = "HELP"; command = "generic/help"; } + +/* + * os_akill + * + * Provides the command operserv/akill. + * + * Used to ban users from the network. + */ +module { name = "os_akill" } +command { service = "OperServ"; name = "AKILL"; command = "operserv/akill"; permission = "operserv/akill"; } + +/* + * os_chankill + * + * Provides the command operserv/chankill. + * + * Used to akill uses from an entire channel. + */ +module { name = "os_chankill" } +command { service = "OperServ"; name = "CHANKILL"; command = "operserv/chankill"; permission = "operserv/chankill"; } + +/* + * os_defcon + * + * Provides the command operserv/defcon. + * + * Allows you to set services in defcon mode, which can be used to restrict services access + * during bot attacks. + */ +#module { name = "os_defcon" } +#command { service = "OperServ"; name = "DEFCON"; command = "operserv/defcon"; } +defcon +{ + /* + * Default DefCon level (1-5) to use when starting Services up. Level 5 constitutes normal operation + * while level 1 constitutes the most restrictive operation. If this setting is left out or set to + * 0, DefCon will be disabled and the rest of this block will be ignored. + */ + #defaultlevel = 5 + + /* + * The following 4 directives define what operations will take place when DefCon is set to levels + * 1 through 4. Each level is a list that must be separated by spaces. + * + * The following operations can be defined at each level: + * - nonewchannels: Disables registering new channels + * - nonewnicks: Disables registering new nicks + * - nomlockchanges: Disables changing MLOCK on registered channels + * - forcechanmodes: Forces all channels to have the modes given in the later chanmodes directive + * - reducedsessions: Reduces the session limit to the value given in the later sessionlimit directive + * - nonewclients: KILL any new clients trying to connect + * - operonly: Services will ignore all non-IRCops + * - silentoperonly: Services will silently ignore all non-IRCops + * - akillnewclients: AKILL any new clients trying to connect + * - nonewmemos: No new memos will be sent to block MemoServ attacks + */ + level4 = "nonewchannels nonewnicks nomlockchanges reducedsessions" + level3 = "nonewchannels nonewnicks nomlockchanges forcechanmodes reducedsessions" + level2 = "nonewchannels nonewnicks nomlockchanges forcechanmodes reducedsessions silentoperonly" + level1 = "nonewchannels nonewnicks nomlockchanges forcechanmodes reducedsessions silentoperonly akillnewclients" + + /* + * New session limit to use when a DefCon level is using "reduced" session limiting. + */ + #sessionlimit = 2 + + /* + * Length of time to add an AKILL for when DefCon is preventing new clients from connecting to the + * network. + */ + #akillexpire = 5m + + /* + * The channel modes to set on all channels when the DefCon channel mode system is in use. + * + * Note 1: Choose these modes carefully, because when DefCon switches to a level which does NOT have + * the mode setting selected, Services will set the reverse on all channels, e.g. if this setting + * is +RN when DefCon is used, all channels will be set to +RN, when DefCon is removed, all + * channels will be set to -RN. You don't want to set this to +k for example, because when DefCon + * is removed all channels with -k. + * + * Note 2: MLOCKed modes will not be lost. + */ + #chanmodes = "+R" + + /* + * This value can be used to automatically return the network to DefCon level 5 after the specified + * time period, just in case any IRC Operator forgets to remove a DefCon setting. + * + * This directive is optional. + */ + #timeout = 15m + + /* + * If set, Services will send a global message on DefCon level changes. + * + * This directive is optional. + */ + #globalondefcon = yes + + /* + * If set, Services will send the global message defined in the message directive on DefCon level + * changes. + * + * This directive is optional. + */ + #globalondefconmore = yes + + /* + * Defines the message that will be sent on DefCon level changes when globalondefconmore is set. + * + * This directive is required only when globalondefconmore is set. + */ + #message = "Put your message to send your users here. Dont forget to uncomment globalondefconmore" + + /* + * Defines the message that will be sent when DefCon is returned to level 5. This directive is optional, + * and will also override globalondefcon and globalondefconmore when set. + */ + #offmessage = "Services are now back to normal, sorry for any inconvenience" + + /* + * Defines the reason to use when clients are KILLed or AKILLed from the network while the proper + * DefCon operation is in effect. + */ + #akillreason = "This network is currently not accepting connections, please try again later" +} + +/* + * os_list + * + * Provides the commands operserv/chanlist and operserv/userlist. + * + * Used to list and search the channels and users currently on the network. + */ +module { name = "os_list" } +command { service = "OperServ"; name = "CHANLIST"; command = "operserv/chanlist"; } +command { service = "OperServ"; name = "USERLIST"; command = "operserv/userlist"; } + +/* + * os_config + * + * Provides the command operserv/config. + * + * Used to view and set configuration options while services are running. + */ +module { name = "os_config" } +command { service = "OperServ"; name = "CONFIG"; command = "operserv/config"; permission = "operserv/config"; } + +/* + * os_forbid + * + * Provides the command operserv/forbid. + * + * Used to forbid specific nicks, channels, emails, etc. from being used. + */ +module { name = "os_forbid" } +command { service = "OperServ"; name = "FORBID"; command = "operserv/forbid"; permission = "operserv/forbid"; } + +/* + * os_ignore + * + * Provides the command operserv/ignore. + * + * Used to make Services ignore users. + */ +module { name = "os_ignore" } +command { service = "OperServ"; name = "IGNORE"; command = "operserv/ignore"; permission = "operserv/ignore"; } + +/* + * os_jupe + * + * Provides the command operserv/jupe. + * + * Used to disconnect servers from the network and prevent them from relinking. + */ +module { name = "os_jupe" } +command { service = "OperServ"; name = "JUPE"; command = "operserv/jupe"; permission = "operserv/jupe"; } + +/* + * os_kick + * + * Provides the command operserv/kick. + * + * Used to kick users from channels. + */ +module { name = "os_kick" } +command { service = "OperServ"; name = "KICK"; command = "operserv/kick"; permission = "operserv/kick"; } + +/* + * os_kill + * + * Provides the command operserv/kill. + * + * Used to forcibly disconnect users from the network. + */ +module { name = "os_kill" } +command { service = "OperServ"; name = "KILL"; command = "operserv/kill"; permission = "operserv/kill"; } + +/* + * os_login + * + * Provides the command operserv/login. + * + * Used to login to OperServ, only required if your oper block requires this. + */ +module { name = "os_login" } +command { service = "OperServ"; name = "LOGIN"; command = "operserv/login"; } + +/* + * os_mode + * + * Provides the commands operserv/mode and operserv/umode. + * + * Used to change user and channel modes. + */ +module { name = "os_mode" } +command { service = "OperServ"; name = "MODE"; command = "operserv/mode"; permission = "operserv/mode"; } +command { service = "OperServ"; name = "UMODE"; command = "operserv/umode"; permission = "operserv/umode"; } + +/* + * os_modinfo + * + * Provides the commands operserv/modinfo and operserv/modlist. + * + * Used to show information about loaded modules. + */ +module { name = "os_modinfo" } +command { service = "OperServ"; name = "MODINFO"; command = "operserv/modinfo"; } +command { service = "OperServ"; name = "MODLIST"; command = "operserv/modlist"; permission = "operserv/modlist"; } + +/* + * os_module + * + * Provides the commands operserv/modload, operserv/modreload, and operserv/modunload. + * + * Used to load, reload, and unload modules. + */ +module { name = "os_module" } +command { service = "OperServ"; name = "MODLOAD"; command = "operserv/modload"; permission = "operserv/modload"; } +command { service = "OperServ"; name = "MODRELOAD"; command = "operserv/modreload"; permission = "operserv/modload"; } +command { service = "OperServ"; name = "MODUNLOAD"; command = "operserv/modunload"; permission = "operserv/modload"; } + +/* + * os_news + * + * Provides the commands operserv/logonnews, operserv/opernews, and operserv/randomnews. + * + * Used to configure news notices shown to users when they connect, and opers when they oper. + */ +module { name = "os_news" } +command { service = "OperServ"; name = "LOGONNEWS"; command = "operserv/logonnews"; permission = "operserv/news"; } +command { service = "OperServ"; name = "OPERNEWS"; command = "operserv/opernews"; permission = "operserv/news"; } +command { service = "OperServ"; name = "RANDOMNEWS"; command = "operserv/randomnews"; permission = "operserv/news"; } + +/* + * os_noop + * + * Provides the command operserv/noop. + * + * Used to NOOP a server, which prevents users from opering on that server. + */ +module { name = "os_noop" } +command { service = "OperServ"; name = "NOOP"; command = "operserv/noop"; permission = "operserv/noop"; } + +/* + * os_oline + * + * Provides the command operserv/oline. + * + * What even does this do. + */ +module { name = "os_oline" } +command { service = "OperServ"; name = "OLINE"; command = "operserv/oline"; permission = "operserv/oline"; } + +/* + * os_oper + * + * Provides the command opersev/oper. + * + * Used to configure opers and show information about opertypes. + */ +module { name = "os_oper" } +command { service = "OperServ"; name = "OPER"; command = "operserv/oper"; permission = "operserv/oper"; } + +/* + * os_reload + * + * Provides the command operserv/relad. + * + * Used to reload the services.conf configuration file. + */ +module { name = "os_reload" } +command { service = "OperServ"; name = "RELOAD"; command = "operserv/reload"; permission = "operserv/reload"; } + +/* + * os_session + * + * Provides the commands operserv/exception and operserv/session. + * + * Used to manage the session limit exception list, and view currently active sessions. + */ +module { name = "os_session" } +command { service = "OperServ"; name = "EXCEPTION"; command = "operserv/exception"; permission = "operserv/exception"; } +command { service = "OperServ"; name = "SESSION"; command = "operserv/session"; permission = "operserv/session"; } + +/* + * os_set + * + * Provides the command operserv/set. + * + * Used to set various settinsg such as superadmin, debug mode, etc. + */ +module { name = "os_set" } +command { service = "OperServ"; name = "SET"; command = "operserv/set"; permission = "operserv/set"; } + +/* + * os_shutdown + * + * Provides the commands operserv/quit, operserv/restart, and operserv/shutdown. + * + * Used to quit, restart, or shutdown services. + */ +module { name = "os_shutdown" } +command { service = "OperServ"; name = "QUIT"; command = "operserv/quit"; permission = "operserv/quit"; } +command { service = "OperServ"; name = "RESTART"; command = "operserv/restart"; permission = "operserv/restart"; } +command { service = "OperServ"; name = "SHUTDOWN"; command = "operserv/shutdown"; permission = "operserv/shutdown"; } + +/* + * os_stats + * + * Provides the operserv/stats command. + * + * Used to show statistics about services. + */ +module { name = "os_stats" } +command { service = "OperServ"; name = "STATS"; command = "operserv/stats"; permission = "operserv/stats"; } + +/* + * os_svsnick + * + * Provides the operserv/svsnick command. + * + * Used to force change user's nicks. + */ +module { name = "os_svsnick" } +command { service = "OperServ"; name = "SVSNICK"; command = "operserv/svsnick"; permission = "operserv/svsnick"; } + +/* + * os_sxline + * + * Provides the operserv/snline, operserv/sqline, and operserv/szline commands. + * + * Used to ban different things such as realnames, nicknames, and IPs. + */ +module { name = "os_sxline" } +command { service = "OperServ"; name = "SNLINE"; command = "operserv/snline"; permission = "operserv/snline"; } +command { service = "OperServ"; name = "SQLINE"; command = "operserv/sqline"; permission = "operserv/sqline"; } +command { service = "OperServ"; name = "SZLINE"; command = "operserv/szline"; permission = "operserv/szline"; } + +/* + * os_update + * + * Provides the opersev/update command. + * + * Use to immediately update the databases. + */ +module { name = "os_update" } +command { service = "OperServ"; name = "UPDATE"; command = "operserv/update"; permission = "operserv/update"; } + diff --git a/data/tables.sql b/data/tables.sql new file mode 100644 index 000000000..4f9665869 --- /dev/null +++ b/data/tables.sql @@ -0,0 +1,425 @@ +-- phpMyAdmin SQL Dump +-- version 3.3.5 +-- http://www.phpmyadmin.net +-- +-- Host: localhost +-- Generation Time: Apr 28, 2011 at 07:08 PM +-- Server version: 5.1.50 +-- PHP Version: 5.3.3-pl1-gentoo + +SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO"; + +-- +-- Database: `anope` +-- + +-- -------------------------------------------------------- + +-- +-- Table structure for table `anope_bs_badwords` +-- + +CREATE TABLE IF NOT EXISTS `anope_bs_badwords` ( + `channel` varchar(255) NOT NULL DEFAULT '', + `word` varchar(255) NOT NULL, + `type` varchar(50) NOT NULL, + UNIQUE KEY `channel` (`channel`,`word`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `anope_bs_core` +-- + +CREATE TABLE IF NOT EXISTS `anope_bs_core` ( + `nick` varchar(255) NOT NULL DEFAULT '', + `user` varchar(255) NOT NULL DEFAULT '', + `host` text NOT NULL, + `rname` text NOT NULL, + `flags` text NOT NULL, + `created` int(10) unsigned NOT NULL DEFAULT '0', + `chancount` int(11) NOT NULL DEFAULT '0', + PRIMARY KEY (`nick`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `anope_bs_info_metadata` +-- + +CREATE TABLE IF NOT EXISTS `anope_bs_info_metadata` ( + `botname` varchar(255) NOT NULL DEFAULT '', + `name` varchar(255) NOT NULL DEFAULT '', + `value` text NOT NULL, + KEY `FK_anope_bs_info_metadata_botname` (`botname`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `anope_cs_access` +-- + +CREATE TABLE IF NOT EXISTS `anope_cs_access` ( + `provider` varchar(255) NOT NULL DEFAULT '', + `data` varchar(255) NOT NULL DEFAULT '', + `mask` varchar(255) NOT NULL DEFAULT '', + `channel` varchar(255) NOT NULL DEFAULT '', + `last_seen` int(10) unsigned NOT NULL DEFAULT '0', + `creator` varchar(255) NOT NULL DEFAULT '', + `created` int(11) unsigned NOT NULL DEFAULT '0', + UNIQUE KEY `channel` (`channel`,`display`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `anope_cs_akick` +-- + +CREATE TABLE IF NOT EXISTS `anope_cs_akick` ( + `channel` varchar(255) NOT NULL DEFAULT '', + `flags` varchar(255) NOT NULL DEFAULT '', + `mask` varchar(255) NOT NULL DEFAULT '', + `reason` text NOT NULL, + `creator` varchar(255) NOT NULL DEFAULT '', + `created` int(10) unsigned NOT NULL DEFAULT '0', + `last_used` int(10) unsigned NOT NULL DEFAULT '0', + UNIQUE KEY `channel` (`channel`,`mask`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `anope_cs_info` +-- + +CREATE TABLE IF NOT EXISTS `anope_cs_info` ( + `name` varchar(255) NOT NULL DEFAULT '', + `founder` text NOT NULL, + `successor` text NOT NULL, + `descr` text NOT NULL, + `time_registered` int(10) unsigned NOT NULL DEFAULT '0', + `last_used` int(10) unsigned NOT NULL DEFAULT '0', + `last_topic` text NOT NULL, + `last_topic_setter` text NOT NULL, + `last_topic_time` int(10) unsigned NOT NULL DEFAULT '0', + `flags` text NOT NULL, + `bantype` smallint(6) NOT NULL DEFAULT '0', + `memomax` smallint(5) unsigned NOT NULL DEFAULT '0', + `botnick` varchar(255) NOT NULL DEFAULT '', + `botflags` text NOT NULL, + `capsmin` smallint(6) NOT NULL DEFAULT '0', + `capspercent` smallint(6) NOT NULL DEFAULT '0', + `floodlines` smallint(6) NOT NULL DEFAULT '0', + `floodsecs` smallint(6) NOT NULL DEFAULT '0', + `repeattimes` smallint(6) NOT NULL DEFAULT '0', + PRIMARY KEY (`name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `anope_cs_info_metadata` +-- + +CREATE TABLE IF NOT EXISTS `anope_cs_info_metadata` ( + `channel` varchar(255) NOT NULL DEFAULT '', + `name` varchar(255) NOT NULL DEFAULT '', + `value` text NOT NULL, + KEY `FK_anope_cs_info_metadata_channel` (`channel`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `anope_cs_levels` +-- + +CREATE TABLE IF NOT EXISTS `anope_cs_levels` ( + `channel` varchar(255) NOT NULL DEFAULT '', + `position` int(11) NOT NULL DEFAULT '0', + `level` int(11) NOT NULL DEFAULT '0', + UNIQUE KEY `channel` (`channel`,`position`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `anope_cs_mlock` +-- + +CREATE TABLE IF NOT EXISTS `anope_cs_mlock` ( + `channel` varchar(255) NOT NULL, + `mode` varchar(127) NOT NULL, + `status` int(11) NOT NULL, + `setter` varchar(255) NOT NULL, + `created` int(11) NOT NULL, + `param` varchar(255) NOT NULL, + UNIQUE KEY `entry` (`channel`,`mode`,`status`,`setter`,`param`), + KEY `FK_anope_cs_mlock` (`channel`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `anope_cs_ttb` +-- + +CREATE TABLE IF NOT EXISTS `anope_cs_ttb` ( + `channel` varchar(255) NOT NULL DEFAULT '', + `ttb_id` int(11) NOT NULL DEFAULT '0', + `value` int(11) NOT NULL DEFAULT '0', + UNIQUE KEY `channel` (`channel`,`ttb_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `anope_extra` +-- + +CREATE TABLE IF NOT EXISTS `anope_extra` ( + `data` text NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `anope_hs_core` +-- + +CREATE TABLE IF NOT EXISTS `anope_hs_core` ( + `nick` varchar(255) NOT NULL, + `vident` varchar(64) NOT NULL, + `vhost` varchar(255) NOT NULL, + `creator` varchar(255) NOT NULL, + `time` int(11) NOT NULL, + KEY `FK_anope_hs_core_nick` (`nick`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `anope_info` +-- + +CREATE TABLE IF NOT EXISTS `anope_info` ( + `version` int(11) DEFAULT NULL, + `date` datetime DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `anope_ms_info` +-- + +CREATE TABLE IF NOT EXISTS `anope_ms_info` ( + `receiver` varchar(255) NOT NULL, + `flags` int(11) NOT NULL DEFAULT '0', + `time` int(10) unsigned NOT NULL DEFAULT '0', + `sender` text NOT NULL, + `text` blob NOT NULL, + `serv` enum('NICK','CHAN') NOT NULL DEFAULT 'NICK', + KEY `FK_anope_ms_info_receiver` (`receiver`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `anope_ns_access` +-- + +CREATE TABLE IF NOT EXISTS `anope_ns_access` ( + `display` varchar(255) NOT NULL DEFAULT '', + `access` varchar(160) NOT NULL, + KEY `FK_anope_ns_access_display` (`display`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `anope_ns_alias` +-- + +CREATE TABLE IF NOT EXISTS `anope_ns_alias` ( + `nick` varchar(255) NOT NULL DEFAULT '', + `last_quit` text NOT NULL, + `last_realname` text NOT NULL, + `last_usermask` text NOT NULL, + `time_registered` int(10) unsigned NOT NULL DEFAULT '0', + `last_seen` int(10) unsigned NOT NULL DEFAULT '0', + `flags` text NOT NULL, + `display` varchar(255) NOT NULL DEFAULT '', + PRIMARY KEY (`nick`), + KEY `FK_anope_ns_alias_display` (`display`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `anope_ns_alias_metadata` +-- + +CREATE TABLE IF NOT EXISTS `anope_ns_alias_metadata` ( + `nick` varchar(255) NOT NULL DEFAULT '', + `name` varchar(255) NOT NULL DEFAULT '', + `value` text NOT NULL, + KEY `FK_anope_ns_alias_metadata_nick` (`nick`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `anope_ns_core` +-- + +CREATE TABLE IF NOT EXISTS `anope_ns_core` ( + `display` varchar(255) NOT NULL DEFAULT '', + `pass` text NOT NULL, + `email` text NOT NULL, + `greet` text NOT NULL, + `flags` text NOT NULL, + `language` varchar(5) NOT NULL DEFAULT '', + `memomax` smallint(5) unsigned NOT NULL DEFAULT '0', + PRIMARY KEY (`display`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `anope_ns_core_metadata` +-- + +CREATE TABLE IF NOT EXISTS `anope_ns_core_metadata` ( + `nick` varchar(255) NOT NULL DEFAULT '', + `name` varchar(255) NOT NULL DEFAULT '', + `value` text NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `anope_os_core` +-- + +CREATE TABLE IF NOT EXISTS `anope_os_core` ( + `maxusercnt` int(11) NOT NULL DEFAULT '0', + `maxusertime` int(10) unsigned NOT NULL DEFAULT '0', +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `anope_os_exceptions` +-- + +CREATE TABLE IF NOT EXISTS `anope_os_exceptions` ( + `mask` varchar(255) NOT NULL, + `slimit` int(11) NOT NULL DEFAULT '0', + `who` text NOT NULL, + `reason` text NOT NULL, + `time` int(10) unsigned NOT NULL DEFAULT '0', + `expires` int(10) unsigned NOT NULL DEFAULT '0' +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `anope_os_xlines` +-- + +CREATE TABLE IF NOT EXISTS `anope_os_xlines` ( + `type` varchar(1) NOT NULL, + `mask` varchar(255) NOT NULL, + `xby` text NOT NULL, + `reason` text NOT NULL, + `seton` int(10) unsigned NOT NULL DEFAULT '0', + `expire` int(10) unsigned NOT NULL DEFAULT '0' +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- +-- Constraints for dumped tables +-- + +-- +-- Constraints for table `anope_bs_badwords` +-- +ALTER TABLE `anope_bs_badwords` + ADD CONSTRAINT `FK_anope_bs_badwords_channel` FOREIGN KEY (`channel`) REFERENCES `anope_cs_info` (`name`) ON DELETE CASCADE ON UPDATE CASCADE; + +-- +-- Constraints for table `anope_bs_info_metadata` +-- +ALTER TABLE `anope_bs_info_metadata` + ADD CONSTRAINT `FK_anope_bs_info_metadata_botname` FOREIGN KEY (`botname`) REFERENCES `anope_bs_core` (`nick`) ON DELETE CASCADE ON UPDATE CASCADE; + +-- +-- Constraints for table `anope_cs_access` +-- +ALTER TABLE `anope_cs_access` + ADD CONSTRAINT `FK_anope_cs_access_channel` FOREIGN KEY (`channel`) REFERENCES `anope_cs_info` (`name`) ON DELETE CASCADE ON UPDATE CASCADE; + +-- +-- Constraints for table `anope_cs_akick` +-- +ALTER TABLE `anope_cs_akick` + ADD CONSTRAINT `FK_anope_cs_akick_channel` FOREIGN KEY (`channel`) REFERENCES `anope_cs_info` (`name`) ON DELETE CASCADE ON UPDATE CASCADE; + +-- +-- Constraints for table `anope_cs_info_metadata` +-- +ALTER TABLE `anope_cs_info_metadata` + ADD CONSTRAINT `FK_anope_cs_info_metadata_channel` FOREIGN KEY (`channel`) REFERENCES `anope_cs_info` (`name`) ON DELETE CASCADE ON UPDATE CASCADE; + +-- +-- Constraints for table `anope_cs_levels` +-- +ALTER TABLE `anope_cs_levels` + ADD CONSTRAINT `FK_anope_cs_levels_channel` FOREIGN KEY (`channel`) REFERENCES `anope_cs_info` (`name`) ON DELETE CASCADE ON UPDATE CASCADE; + +-- +-- Constraints for table `anope_cs_mlock` +-- +ALTER TABLE `anope_cs_mlock` + ADD CONSTRAINT `FK_anope_cs_mlock_channel` FOREIGN KEY (`channel`) REFERENCES `anope_cs_info` (`name`) ON DELETE CASCADE ON UPDATE CASCADE; + +-- +-- Constraints for table `anope_cs_ttb` +-- +ALTER TABLE `anope_cs_ttb` + ADD CONSTRAINT `FK_anope_cs_ttb_channel` FOREIGN KEY (`channel`) REFERENCES `anope_cs_info` (`name`) ON DELETE CASCADE ON UPDATE CASCADE; + +-- +-- Constraints for table `anope_hs_core` +-- +ALTER TABLE `anope_hs_core` + ADD CONSTRAINT `FK_anope_hs_core_nick` FOREIGN KEY (`nick`) REFERENCES `anope_ns_alias` (`nick`) ON DELETE CASCADE ON UPDATE CASCADE; + +-- +-- Constraints for table `anope_ms_info` +-- +ALTER TABLE `anope_ms_info` + ADD CONSTRAINT `FK_anope_ms_info_receiver` FOREIGN KEY (`receiver`) REFERENCES `anope_ns_alias` (`nick`) ON DELETE CASCADE ON UPDATE CASCADE; + +-- +-- Constraints for table `anope_ns_access` +-- +ALTER TABLE `anope_ns_access` + ADD CONSTRAINT `FK_anope_ns_access_display` FOREIGN KEY (`display`) REFERENCES `anope_ns_core` (`display`) ON DELETE CASCADE ON UPDATE CASCADE; + +-- +-- Constraints for table `anope_ns_alias` +-- +ALTER TABLE `anope_ns_alias` + ADD CONSTRAINT `FK_anope_ns_alias_display` FOREIGN KEY (`display`) REFERENCES `anope_ns_core` (`display`) ON DELETE CASCADE ON UPDATE CASCADE; + +-- +-- Constraints for table `anope_ns_alias_metadata` +-- +ALTER TABLE `anope_ns_alias_metadata` + ADD CONSTRAINT `FK_anope_ns_alias_metadata_nick` FOREIGN KEY (`nick`) REFERENCES `anope_ns_alias` (`nick`) ON DELETE CASCADE ON UPDATE CASCADE; + diff --git a/docs/TODO b/docs/TODO index a69b3cfb2..4914ffbc0 100644 --- a/docs/TODO +++ b/docs/TODO @@ -32,3 +32,4 @@ Future [ ] Unique IDs on each AKILL/blah so that networks may use them as ticket IDs [ ] HS ACTIVATE -ALL (rob sez this all needs reviewing) [ ] No nickname ownership? +[ ] More commands need to be split up such as /bs bot, /ms set, /bs kick, /bs set, /os set? etc. diff --git a/language/update.sh b/language/update.sh index 8302d813c..28aa39f7d 100755 --- a/language/update.sh +++ b/language/update.sh @@ -4,7 +4,7 @@ rm anope.pot touch anope.pot cd .. -FILES=`find ./ -name *.cpp -o -name *.h | grep -v /modules/extra/` +FILES=`find ./ -name *.cpp -o -name *.h | grep -v /modules/third/` for f in $FILES do xgettext -C -s -d Anope -j -o language/anope.pot --from-code=utf-8 --keyword --keyword=_ $f diff --git a/modules/CMakeLists.txt b/modules/CMakeLists.txt index 724899041..d4bd3f7a1 100644 --- a/modules/CMakeLists.txt +++ b/modules/CMakeLists.txt @@ -1,4 +1,4 @@ -add_subdirectory("extra/language") +add_subdirectory("third/language") # Get a list of ALL files and directories within the current directory file(GLOB MODULES_FOLDERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*") @@ -96,7 +96,7 @@ foreach(MODULE_FOLDER ${MODULES_FOLDERS}) # Get a list of ALL files and directories within this modules directory file(GLOB SUBMODULE_DIRS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "${MODULE_FOLDER}/*") remove_item_from_list(SUBMODULE_DIRS "CMakeFiles") - remove_item_from_list(SUBMODULE_DIRS "extra/language") + remove_item_from_list(SUBMODULE_DIRS "third/language") foreach(SUBDIR ${SUBMODULE_DIRS}) if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIR}") diff --git a/modules/commands/bs_assign.cpp b/modules/commands/bs_assign.cpp new file mode 100644 index 000000000..803368994 --- /dev/null +++ b/modules/commands/bs_assign.cpp @@ -0,0 +1,168 @@ +/* BotServ core functions + * + * (C) 2003-2011 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 ¶ms) + { + const Anope::string &chan = params[0]; + const Anope::string &nick = params[1]; + + User *u = source.u; + + if (readonly) + { + source.Reply(BOT_ASSIGN_READONLY); + return; + } + + ChannelInfo *ci = cs_findchan(params[0]); + if (ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); + return; + } + + BotInfo *bi = findbot(nick); + if (!bi) + { + source.Reply(BOT_DOES_NOT_EXIST, nick.c_str()); + return; + } + + if (ci->botflags.HasFlag(BS_NOBOT) || (!ci->HasPriv(u, CA_ASSIGN) && !u->HasPriv("botserv/administration"))) + { + source.Reply(ACCESS_DENIED); + return; + } + + if (bi->HasFlag(BI_PRIVATE) && !u->HasCommand("botserv/botserv/assign/private")) + { + 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 = !ci->HasPriv(u, CA_ASSIGN); + Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "for " << bi->nick; + + bi->Assign(u, 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) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Assigns a bot pointed out by nick to the channel chan. 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 \037nick\037")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + + if (readonly) + { + source.Reply(BOT_ASSIGN_READONLY); + return; + } + + ChannelInfo *ci = cs_findchan(params[0]); + if (ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); + return; + } + + if (!u->HasPriv("botserv/administration") && !ci->HasPriv(u, CA_ASSIGN)) + { + source.Reply(ACCESS_DENIED); + return; + } + + if (!ci->bi) + { + source.Reply(BOT_NOT_ASSIGNED); + return; + } + + if (ci->HasFlag(CI_PERSIST) && !ModeManager::FindChannelModeByName(CMODE_PERM)) + { + source.Reply(_("You can not unassign bots while persist is set on the channel.")); + return; + } + + bool override = !ci->HasPriv(u, CA_ASSIGN); + Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "for " << ci->bi->nick; + + ci->bi->UnAssign(u, ci); + source.Reply(_("There is no bot assigned to %s anymore."), ci->name.c_str()); + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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 have to reconfigure\n" + "it entirely.")); + return true; + } +}; + +class BSAssign : public Module +{ + CommandBSAssign commandbsassign; + CommandBSUnassign commandbsunassign; + + public: + BSAssign(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandbsassign(this), commandbsunassign(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandbsassign); + ModuleManager::RegisterService(&commandbsunassign); + } +}; + +MODULE_INIT(BSAssign) diff --git a/modules/commands/bs_badwords.cpp b/modules/commands/bs_badwords.cpp new file mode 100644 index 000000000..a27dca2d4 --- /dev/null +++ b/modules/commands/bs_badwords.cpp @@ -0,0 +1,331 @@ +/* BotServ core functions + * + * (C) 2003-2011 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 BadwordsListCallback : public NumberList +{ + CommandSource &source; + ChannelInfo *ci; + bool SentHeader; + public: + BadwordsListCallback(CommandSource &_source, ChannelInfo *_ci, const Anope::string &list) : NumberList(list, false), source(_source), ci(_ci), SentHeader(false) + { + } + + ~BadwordsListCallback() + { + if (!SentHeader) + source.Reply(_("No matching entries on %s bad words list."), ci->name.c_str()); + } + + void HandleNumber(unsigned Number) + { + if (!Number || Number > ci->GetBadWordCount()) + return; + + if (!SentHeader) + { + SentHeader = true; + source.Reply(_("Bad words list for %s:\n" + " Num Word Type"), ci->name.c_str()); + } + + DoList(source, Number - 1, ci->GetBadWord(Number - 1)); + } + + static void DoList(CommandSource &source, unsigned Number, BadWord *bw) + { + source.Reply(_(" %3d %-30s %s"), Number + 1, bw->word.c_str(), bw->type == BW_SINGLE ? "(SINGLE)" : (bw->type == BW_START ? "(START)" : (bw->type == BW_END ? "(END)" : ""))); + } +}; + +class BadwordsDelCallback : public NumberList +{ + CommandSource &source; + ChannelInfo *ci; + 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 (!ci->HasPriv(source.u, CA_BADWORDS) && source.u->HasPriv("botserv/administration")) + this->override = true; + } + + ~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) + { + if (!Number || Number > ci->GetBadWordCount()) + return; + + Log(override ? LOG_OVERRIDE : LOG_COMMAND, source.u, c, ci) << "DEL " << ci->GetBadWord(Number - 1)->word; + ++Deleted; + ci->EraseBadWord(Number - 1); + } +}; + +class CommandBSBadwords : public Command +{ + private: + void DoList(CommandSource &source, ChannelInfo *ci, const Anope::string &word) + { + bool override = !ci->HasPriv(source.u, CA_BADWORDS); + Log(override ? LOG_OVERRIDE : LOG_COMMAND, source.u, this, ci) << "LIST"; + + if (!ci->GetBadWordCount()) + source.Reply(_("%s bad words list is empty."), ci->name.c_str()); + else if (!word.empty() && word.find_first_not_of("1234567890,-") == Anope::string::npos) + { + BadwordsListCallback list(source, ci, word); + list.Process(); + } + else + { + bool SentHeader = false; + + for (unsigned i = 0, end = ci->GetBadWordCount(); i < end; ++i) + { + BadWord *bw = ci->GetBadWord(i); + + if (!word.empty() && !Anope::Match(bw->word, word)) + continue; + + if (!SentHeader) + { + SentHeader = true; + source.Reply(_("Bad words list for %s:\n" + " Num Word Type"), ci->name.c_str()); + + } + + BadwordsListCallback::DoList(source, i, bw); + } + + if (!SentHeader) + source.Reply(_("No matching entries on %s bad words list."), ci->name.c_str()); + } + + return; + } + + void DoAdd(CommandSource &source, ChannelInfo *ci, const Anope::string &word) + { + size_t pos = word.rfind(' '); + BadWordType type = 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")) + type = BW_SINGLE; + else if (opt.equals_ci("START")) + type = BW_START; + else if (opt.equals_ci("END")) + type = BW_END; + } + realword = word.substr(0, pos); + } + + if (ci->GetBadWordCount() >= Config->BSBadWordsMax) + { + source.Reply(_("Sorry, you can only have %d bad words entries on a channel."), Config->BSBadWordsMax); + return; + } + + for (unsigned i = 0, end = ci->GetBadWordCount(); i < end; ++i) + { + BadWord *bw = ci->GetBadWord(i); + + if (!bw->word.empty() && ((Config->BSCaseSensitive && realword.equals_cs(bw->word)) || (!Config->BSCaseSensitive && 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 = !ci->HasPriv(source.u, CA_BADWORDS); + Log(override ? LOG_OVERRIDE : LOG_COMMAND, source.u, this, ci) << "ADD " << realword; + ci->AddBadWord(realword, type); + + source.Reply(_("\002%s\002 added to %s bad words list."), realword.c_str(), ci->name.c_str()); + + return; + } + + void DoDelete(CommandSource &source, ChannelInfo *ci, const Anope::string &word) + { + /* 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; + BadWord *badword; + + for (i = 0, end = ci->GetBadWordCount(); i < end; ++i) + { + badword = ci->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 = !ci->HasPriv(source.u, CA_BADWORDS); + Log(override ? LOG_OVERRIDE : LOG_COMMAND, source.u, this, ci) << "DEL " << badword->word; + + source.Reply(_("\002%s\002 deleted from %s bad words list."), badword->word.c_str(), ci->name.c_str()); + + ci->EraseBadWord(i); + } + + return; + } + + void DoClear(CommandSource &source, ChannelInfo *ci) + { + bool override = !ci->HasPriv(source.u, CA_BADWORDS); + Log(override ? LOG_OVERRIDE : LOG_COMMAND, source.u, this, ci) << "CLEAR"; + + ci->ClearBadWords(); + source.Reply(_("Bad words list is now empty.")); + return; + } + public: + CommandBSBadwords(Module *creator) : Command(creator, "botserv/badwords", 2, 3) + { + this->SetDesc(_("Maintains 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 ¶ms) + { + const Anope::string &cmd = params[1]; + const Anope::string &word = params.size() > 2 ? params[2] : ""; + User *u = source.u; + bool need_args = cmd.equals_ci("LIST") || cmd.equals_ci("CLEAR"); + + if (!need_args && word.empty()) + { + this->OnSyntaxError(source, cmd); + return; + } + + ChannelInfo *ci = cs_findchan(params[0]); + if (ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); + return; + } + + + if (!ci->HasPriv(u, CA_BADWORDS) && (!need_args || !u->HasPriv("botserv/administration"))) + { + source.Reply(ACCESS_DENIED); + return; + } + + if (readonly) + { + source.Reply(_("Sorry, channel 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) + { + 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" + "badword 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->UseStrictPrivMsgString.c_str(), source.owner->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 of the\n" + "bad words list.")); + return true; + } +}; + +class BSBadwords : public Module +{ + CommandBSBadwords commandbsbadwords; + + public: + BSBadwords(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandbsbadwords(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandbsbadwords); + } +}; + +MODULE_INIT(BSBadwords) diff --git a/modules/commands/bs_bot.cpp b/modules/commands/bs_bot.cpp new file mode 100644 index 000000000..7500d1926 --- /dev/null +++ b/modules/commands/bs_bot.cpp @@ -0,0 +1,414 @@ +/* BotServ core functions + * + * (C) 2003-2011 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 ¶ms) + { + const Anope::string &nick = params[1]; + const Anope::string &user = params[2]; + const Anope::string &host = params[3]; + const Anope::string &real = params[4]; + BotInfo *bi; + + if (findbot(nick)) + { + source.Reply(_("Bot \002%s\002 already exists."), nick.c_str()); + return; + } + + if (nick.length() > Config->NickLen) + { + source.Reply(_("Bot Nicks may only contain valid nick characters.")); + return; + } + + if (user.length() > Config->UserLen) + { + source.Reply(_("Bot Idents may only contain %d characters."), Config->UserLen); + return; + } + + if (host.length() > Config->HostLen) + { + source.Reply(_("Bot Hosts may only contain %d characters."), Config->HostLen); + return; + } + + /* Check the nick is valid re RFC 2812 */ + if (isdigit(nick[0]) || nick[0] == '-') + { + source.Reply(_("Bot Nicks may only contain valid nick characters.")); + return; + } + + for (unsigned i = 0, end = nick.length(); i < end && i < Config->NickLen; ++i) + if (!isvalidnick(nick[i])) + { + source.Reply(_("Bot Nicks may only contain valid nick characters.")); + return; + } + + /* check for hardcored ircd forbidden nicks */ + if (!ircdproto->IsNickValid(nick)) + { + source.Reply(_("Bot Nicks may only contain valid nick characters.")); + return; + } + + /* Check the host is valid re RFC 2812 */ + if (!isValidHost(host, 3)) + { + source.Reply(_("Bot Hosts may only contain valid host characters.")); + return; + } + + for (unsigned i = 0, end = user.length(); i < end && i < Config->UserLen; ++i) + if (!isalnum(user[i])) + { + source.Reply(_("Bot Idents may only contain valid characters."), Config->UserLen); + 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 (findnick(nick)) + { + source.Reply(NICK_ALREADY_REGISTERED, nick.c_str()); + return; + } + + bi = new BotInfo(nick, user, host, real); + + Log(LOG_ADMIN, source.u, 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(I_OnBotCreate, OnBotCreate(bi)); + return; + } + + void DoChange(CommandSource &source, const std::vector ¶ms) + { + 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] : ""; + BotInfo *bi; + + if (oldnick.empty() || nick.empty()) + { + this->OnSyntaxError(source, "CHANGE"); + return; + } + + if (!(bi = findbot(oldnick))) + { + source.Reply(BOT_DOES_NOT_EXIST, oldnick.c_str()); + return; + } + + if (!oldnick.equals_ci(nick) && nickIsServices(oldnick, false)) + { + source.Reply(BOT_DOES_NOT_EXIST, oldnick.c_str()); + return; + } + + if (nick.length() > Config->NickLen) + { + source.Reply(_("Bot Nicks may only contain valid nick characters.")); + return; + } + + if (!user.empty() && user.length() > Config->UserLen) + { + source.Reply(_("Bot Idents may only contain %d characters."), Config->UserLen); + return; + } + + if (!host.empty() && host.length() > Config->HostLen) + { + source.Reply(_("Bot Hosts may only contain %d characters."), Config->HostLen); + return; + } + + if (!oldnick.equals_ci(nick) && nickIsServices(nick, false)) + { + source.Reply(BOT_DOES_NOT_EXIST, oldnick.c_str()); + 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(_("Old info is equal to the new one.")); + return; + } + + /* Check the nick is valid re RFC 2812 */ + if (isdigit(nick[0]) || nick[0] == '-') + { + source.Reply(_("Bot Nicks may only contain valid nick characters.")); + return; + } + + for (unsigned i = 0, end = nick.length(); i < end && i < Config->NickLen; ++i) + if (!isvalidnick(nick[i])) + { + source.Reply(_("Bot Nicks may only contain valid nick characters.")); + return; + } + + /* check for hardcored ircd forbidden nicks */ + if (!ircdproto->IsNickValid(nick)) + { + source.Reply(_("Bot Nicks may only contain valid nick characters.")); + return; + } + + if (!host.empty() && !isValidHost(host, 3)) + { + source.Reply(_("Bot Hosts may only contain valid host characters.")); + return; + } + + if (!user.empty()) + for (unsigned i = 0, end = user.length(); i < end && i < Config->UserLen; ++i) + if (!isalnum(user[i])) + { + source.Reply(_("Bot Idents may only contain valid characters."), Config->UserLen); + return; + } + + if (!nick.equals_ci(bi->nick) && findbot(nick)) + { + source.Reply(_("Bot \002%s\002 already exists."), 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 (findnick(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. */ + if (ircd->sqline) + { + XLine x(bi->nick); + ircdproto->SendSQLineDel(&x); + } + + /* Add a Q line for the new nick */ + XLine x(nick, "Reserved for services"); + ircdproto->SendSQLine(NULL, &x); + } + + if (!user.empty()) + ircdproto->SendQuit(bi, "Quit: Be right back"); + else + { + ircdproto->SendChangeBotNick(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()) + { + ircdproto->SendClientIntroduction(bi, ircd->pseudoclient_mode); + bi->RejoinAll(); + } + + 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.u, this) << "CHANGE " << oldnick << " to " << bi->GetMask() << " " << bi->realname; + + FOREACH_MOD(I_OnBotChange, OnBotChange(bi)); + return; + } + + void DoDel(CommandSource &source, const std::vector ¶ms) + { + const Anope::string &nick = params[1]; + BotInfo *bi; + + if (nick.empty()) + { + this->OnSyntaxError(source, "DEL"); + return; + } + + if (!(bi = findbot(nick))) + { + source.Reply(BOT_DOES_NOT_EXIST, nick.c_str()); + return; + } + + if (nickIsServices(nick, false)) + { + source.Reply(BOT_DOES_NOT_EXIST, nick.c_str()); + return; + } + + FOREACH_MOD(I_OnBotDelete, OnBotDelete(bi)); + + Log(LOG_ADMIN, source.u, 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 ¶ms) + { + const Anope::string &cmd = params[0]; + User *u = source.u; + + if (readonly) + { + source.Reply(_("Sorry, bot modification is temporarily disabled.")); + return; + } + + if (cmd.equals_ci("ADD")) + { + // ADD nick user host real - 5 + if (!u->HasCommand("botserv/bot/add")) + { + source.Reply(ACCESS_DENIED); + return; + } + + if (params.size() < 5) + { + this->OnSyntaxError(source, "ADD"); + return; + } + + std::vector 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 (!u->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 (!u->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) + { + 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" + "\002BOT CHANGE\002 allows to change nickname, username, hostname\n" + "or realname of a bot without actually delete it (and all\n" + "the data associated with it).\n" + "\002BOT DEL\002 removes the given bot from the bot list. \n" + " \n" + "\002Note\002: you cannot create a bot that has 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, CORE), + commandbsbot(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandbsbot); + } +}; + +MODULE_INIT(BSBot) diff --git a/modules/commands/bs_botlist.cpp b/modules/commands/bs_botlist.cpp new file mode 100644 index 000000000..ee7e4d2d8 --- /dev/null +++ b/modules/commands/bs_botlist.cpp @@ -0,0 +1,91 @@ +/* BotServ core functions + * + * (C) 2003-2011 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")); + this->SetSyntax(""); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + unsigned count = 0; + + for (Anope::insensitive_map::const_iterator it = BotListByNick.begin(), it_end = BotListByNick.end(); it != it_end; ++it) + { + BotInfo *bi = it->second; + + if (!bi->HasFlag(BI_PRIVATE)) + { + if (!count) + source.Reply(_("Bot list:")); + ++count; + source.Reply(" %-15s (%s@%s)", bi->nick.c_str(), bi->GetIdent().c_str(), bi->host.c_str()); + } + } + + if (u->HasCommand("botserv/botserv/botlist") && count < BotListByNick.size()) + { + source.Reply(_("Bots reserved to IRC operators:")); + + for (Anope::insensitive_map::const_iterator it = BotListByNick.begin(), it_end = BotListByNick.end(); it != it_end; ++it) + { + BotInfo *bi = it->second; + + if (bi->HasFlag(BI_PRIVATE)) + { + source.Reply(" %-15s (%s@%s)", bi->nick.c_str(), bi->GetIdent().c_str(), bi->host.c_str()); + ++count; + } + } + } + + if (!count) + source.Reply(_("There are no bots available at this time.\n" + "Ask a Services Operator to create one!")); + else + source.Reply(_("%d bots available."), count); + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Lists all available bots on this network.")); + return true; + } +}; + +class BSBotList : public Module +{ + CommandBSBotList commandbsbotlist; + + public: + BSBotList(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandbsbotlist(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandbsbotlist); + } +}; + +MODULE_INIT(BSBotList) diff --git a/modules/commands/bs_control.cpp b/modules/commands/bs_control.cpp new file mode 100644 index 000000000..16a5b372a --- /dev/null +++ b/modules/commands/bs_control.cpp @@ -0,0 +1,158 @@ +/* BotServ core functions + * + * (C) 2003-2011 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 given text on the given channel")); + this->SetSyntax(_("\037channel\037 \037text\037")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + const Anope::string &text = params[1]; + + User *u = source.u; + + ChannelInfo *ci = cs_findchan(params[0]); + if (ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); + return; + } + + if (!ci->HasPriv(u, CA_SAY)) + { + 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; + } + + ircdproto->SendPrivmsg(ci->bi, ci->name, "%s", text.c_str()); + ci->bi->lastmsg = Anope::CurTime; + + // XXX need a way to find if someone is overriding this + Log(LOG_COMMAND, u, this, ci) << text; + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Makes the bot say the given text on the given 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 ¶ms) + { + User *u = source.u; + Anope::string message = params[1]; + + ChannelInfo *ci = cs_findchan(params[0]); + if (ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); + return; + } + + if (!ci->HasPriv(u, CA_SAY)) + { + 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; + } + + size_t i = 0; + while ((i = message.find(1)) && i != Anope::string::npos) + message.erase(i, 1); + + ircdproto->SendAction(ci->bi, ci->name, "%s", message.c_str()); + ci->bi->lastmsg = Anope::CurTime; + + // XXX Need to be able to find if someone is overriding this. + Log(LOG_COMMAND, u, this, ci) << message; + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Makes the bot do the equivalent of a \"/me\" command\n" + "on the given channel using the given text.")); + return true; + } +}; + +class BSControl : public Module +{ + CommandBSSay commandbssay; + CommandBSAct commandbsact; + + public: + BSControl(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandbssay(this), commandbsact(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandbssay); + ModuleManager::RegisterService(&commandbsact); + } +}; + +MODULE_INIT(BSControl) diff --git a/modules/commands/bs_info.cpp b/modules/commands/bs_info.cpp new file mode 100644 index 000000000..b48a0b296 --- /dev/null +++ b/modules/commands/bs_info.cpp @@ -0,0 +1,244 @@ +/* BotServ core functions + * + * (C) 2003-2011 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(CommandSource &source, BotInfo *bi) + { + Anope::string buf; + for (registered_channel_map::const_iterator it = RegisteredChannelList.begin(), it_end = RegisteredChannelList.end(); it != it_end; ++it) + { + ChannelInfo *ci = it->second; + + if (ci->bi == bi) + { + if (buf.length() + ci->name.length() > 300) + { + source.Reply("%s", buf.c_str()); + buf.clear(); + } + buf += " " + ci->name + " "; + } + } + + if (!buf.empty()) + source.Reply("%s", buf.c_str()); + return; + } + public: + CommandBSInfo(Module *creator) : Command(creator, "botserv/info", 1, 1) + { + this->SetDesc(_("Allows you to see BotServ information about a channel or a bot")); + this->SetSyntax(_("\002INFO {\037chan\037 | \037nick\037}\002")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + const Anope::string &query = params[0]; + + bool need_comma = false; + char buf[BUFSIZE], *end; + + User *u = source.u; + BotInfo *bi = findbot(query); + ChannelInfo *ci; + if (bi) + { + source.Reply(_("Information for bot \002%s\002:"), bi->nick.c_str()); + source.Reply(_(" Mask : %s@%s"), bi->GetIdent().c_str(), bi->host.c_str()); + source.Reply(_(" Real name : %s"), bi->realname.c_str()); + source.Reply(_(" Created : %s"), do_strftime(bi->created).c_str()); + source.Reply(_(" Options : %s"), bi->HasFlag(BI_PRIVATE) ? _("Private") : _("None")); + source.Reply(_(" Used on : %d channel(s)"), bi->chancount); + + if (u->HasPriv("botserv/administration")) + this->send_bot_channels(source, bi); + } + else if ((ci = cs_findchan(query))) + { + if (!ci->HasPriv(u, CA_FOUNDER) && !u->HasPriv("botserv/administration")) + { + source.Reply(ACCESS_DENIED); + return; + } + + source.Reply(CHAN_INFO_HEADER, ci->name.c_str()); + if (ci->bi) + source.Reply(_(" Bot nick : %s"), ci->bi->nick.c_str()); + else + source.Reply(_(" Bot nick : not assigned yet.")); + + if (ci->botflags.HasFlag(BS_KICK_BADWORDS)) + { + if (ci->ttb[TTB_BADWORDS]) + source.Reply(_(" Bad words kicker : %s (%d kick(s) to ban)"), ENABLED, ci->ttb[TTB_BADWORDS]); + else + source.Reply(_(" Bad words kicker : %s"), ENABLED); + } + else + source.Reply(_(" Bad words kicker : %s"), DISABLED); + if (ci->botflags.HasFlag(BS_KICK_BOLDS)) + { + if (ci->ttb[TTB_BOLDS]) + source.Reply(_(" Bolds kicker : %s (%d kick(s) to ban)"), ENABLED, ci->ttb[TTB_BOLDS]); + else + source.Reply(_(" Bolds kicker : %s"), ENABLED); + } + else + source.Reply(_(" Bolds kicker : %s"), DISABLED); + if (ci->botflags.HasFlag(BS_KICK_CAPS)) + { + if (ci->ttb[TTB_CAPS]) + source.Reply(_(" Caps kicker : %s (%d kick(s) to ban; minimum %d/%d%%)"), ENABLED, ci->ttb[TTB_CAPS], ci->capsmin, ci->capspercent); + else + source.Reply(_(" Caps kicker : %s (minimum %d/%d%%)"), ENABLED, ci->capsmin, ci->capspercent); + } + else + source.Reply(_(" Caps kicker : %s"), DISABLED); + if (ci->botflags.HasFlag(BS_KICK_COLORS)) + { + if (ci->ttb[TTB_COLORS]) + source.Reply(_(" Colors kicker : %s (%d kick(s) to ban)"), ENABLED, ci->ttb[TTB_COLORS]); + else + source.Reply(_(" Colors kicker : %s"), ENABLED); + } + else + source.Reply(_(" Colors kicker : %s"), DISABLED); + if (ci->botflags.HasFlag(BS_KICK_FLOOD)) + { + if (ci->ttb[TTB_FLOOD]) + source.Reply(_(" Flood kicker : %s (%d kick(s) to ban; %d lines in %ds)"), ENABLED, ci->ttb[TTB_FLOOD], ci->floodlines, ci->floodsecs); + else + source.Reply(_(" Flood kicker : %s (%d lines in %ds)"), ENABLED, ci->floodlines, ci->floodsecs); + } + else + source.Reply(_(" Flood kicker : %s"), DISABLED); + if (ci->botflags.HasFlag(BS_KICK_REPEAT)) + { + if (ci->ttb[TTB_REPEAT]) + source.Reply(_(" Repeat kicker : %s (%d kick(s) to ban; %d times)"), ENABLED, ci->ttb[TTB_REPEAT], ci->repeattimes); + else + source.Reply(_(" Repeat kicker : %s (%d times)"), ENABLED, ci->repeattimes); + } + else + source.Reply(_(" Repeat kicker : %s"), DISABLED); + if (ci->botflags.HasFlag(BS_KICK_REVERSES)) + { + if (ci->ttb[TTB_REVERSES]) + source.Reply(_(" Reverses kicker : %s (%d kick(s) to ban)"), ENABLED, ci->ttb[TTB_REVERSES]); + else + source.Reply(_(" Reverses kicker : %s"), ENABLED); + } + else + source.Reply(_(" Reverses kicker : %s"), DISABLED); + if (ci->botflags.HasFlag(BS_KICK_UNDERLINES)) + { + if (ci->ttb[TTB_UNDERLINES]) + source.Reply(_(" Underlines kicker : %s (%d kick(s) to ban)"), ENABLED, ci->ttb[TTB_UNDERLINES]); + else + source.Reply(_(" Underlines kicker : %s"), ENABLED); + } + else + source.Reply(_(" Underlines kicker : %s"), DISABLED); + if (ci->botflags.HasFlag(BS_KICK_ITALICS)) + { + if (ci->ttb[TTB_ITALICS]) + source.Reply(_(" Italics kicker : %s (%d kick(s) to ban)"), ENABLED, ci->ttb[TTB_ITALICS]); + else + source.Reply(_(" Italics kicker : %s"), ENABLED); + } + else + source.Reply(_(" Italics kicker : %s"), DISABLED); + if (ci->botflags.HasFlag(BS_KICK_AMSGS)) + { + if (ci->ttb[TTB_AMSGS]) + source.Reply(_(" AMSG kicker : %s (%d kick(s) to ban)"), ENABLED, ci->ttb[TTB_AMSGS]); + else + source.Reply(_(" AMSG kicker : %s"), ENABLED); + } + else + source.Reply(_(" AMSG kicker : %s"), DISABLED); + + if (ci->botflags.HasFlag(BS_MSG_PRIVMSG)) + source.Reply(_(" Fantasy reply : %s"), "PRIVMSG"); + else if (ci->botflags.HasFlag(BS_MSG_NOTICE)) + source.Reply(_(" Fantasy reply : %s"), "NOTICE"); + else if (ci->botflags.HasFlag(BS_MSG_NOTICEOPS)) + source.Reply(_(" Fantasy reply : %s"), "NOTICEOPS"); + + end = buf; + *end = 0; + if (ci->botflags.HasFlag(BS_DONTKICKOPS)) + { + end += snprintf(end, sizeof(buf) - (end - buf), "%s", _("Ops protection")); + need_comma = true; + } + if (ci->botflags.HasFlag(BS_DONTKICKVOICES)) + { + end += snprintf(end, sizeof(buf) - (end - buf), "%s%s", need_comma ? ", " : "", _("Voices protection")); + need_comma = true; + } + if (ci->botflags.HasFlag(BS_FANTASY)) + { + end += snprintf(end, sizeof(buf) - (end - buf), "%s%s", need_comma ? ", " : "", _("Fantasy")); + need_comma = true; + } + if (ci->botflags.HasFlag(BS_GREET)) + { + end += snprintf(end, sizeof(buf) - (end - buf), "%s%s", need_comma ? ", " : "", _("Greet")); + need_comma = true; + } + if (ci->botflags.HasFlag(BS_NOBOT)) + { + end += snprintf(end, sizeof(buf) - (end - buf), "%s%s", need_comma ? ", " : "", _("No bot")); + need_comma = true; + } + source.Reply(_(" Options : %s"), *buf ? buf : _("None")); + } + else + source.Reply(_("\002%s\002 is not a valid bot or registered channel."), query.c_str()); + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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.owner->nick.c_str()); + return true; + } +}; + +class BSInfo : public Module +{ + CommandBSInfo commandbsinfo; + + public: + BSInfo(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandbsinfo(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandbsinfo); + } +}; + +MODULE_INIT(BSInfo) diff --git a/modules/commands/bs_kick.cpp b/modules/commands/bs_kick.cpp new file mode 100644 index 000000000..3f433f773 --- /dev/null +++ b/modules/commands/bs_kick.cpp @@ -0,0 +1,1011 @@ +/* BotServ core functions + * + * (C) 2003-2011 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 CommandBSKick : public Command +{ + public: + CommandBSKick(Module *creator) : Command(creator, "botserv/kick", 3, 6) + { + this->SetDesc(_("Configures kickers")); + this->SetSyntax(_("\037channel\037 \037option\037 {\037ON|\037} [\037settings\037]")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + const Anope::string &chan = params[0]; + const Anope::string &option = params[1]; + const Anope::string &value = params[2]; + const Anope::string &ttb = params.size() > 3 ? params[3] : ""; + + User *u = source.u; + ChannelInfo *ci = cs_findchan(params[0]); + + if (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 (chan.empty() || option.empty() || value.empty()) + this->OnSyntaxError(source, ""); + else if (!value.equals_ci("ON") && !value.equals_ci("OFF")) + this->OnSyntaxError(source, ""); + else if (!ci->HasPriv(u, CA_SET) && !u->HasPriv("botserv/administration")) + source.Reply(ACCESS_DENIED); + else if (!ci->bi) + source.Reply(BOT_NOT_ASSIGNED); + else + { + bool override = !ci->HasPriv(u, CA_SET); + Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << option << " " << value; + + if (option.equals_ci("BADWORDS")) + { + if (value.equals_ci("ON")) + { + if (!ttb.empty()) + { + try + { + ci->ttb[TTB_BADWORDS] = convertTo(ttb); + if (ci->ttb[TTB_BADWORDS] < 0) + throw ConvertException(); + } + catch (const ConvertException &) + { + /* reset the value back to 0 - TSL */ + ci->ttb[TTB_BADWORDS] = 0; + source.Reply(_("\002%s\002 cannot be taken as times to ban."), ttb.c_str()); + return; + } + } + else + ci->ttb[TTB_BADWORDS] = 0; + + ci->botflags.SetFlag(BS_KICK_BADWORDS); + if (ci->ttb[TTB_BADWORDS]) + source.Reply(_("Bot will now kick \002bad words\002, and will place a ban after \n" + "%d kicks for the same user. Use the BADWORDS command\n" + "to add or remove a bad word."), ci->ttb[TTB_BADWORDS]); + else + source.Reply(_("Bot will now kick \002bad words\002. Use the BADWORDS command\n" + "to add or remove a bad word.")); + } + else + { + ci->botflags.UnsetFlag(BS_KICK_BADWORDS); + source.Reply(_("Bot won't kick \002bad words\002 anymore.")); + } + } + else if (option.equals_ci("BOLDS")) + { + if (value.equals_ci("ON")) + { + if (!ttb.empty()) + { + try + { + ci->ttb[TTB_BOLDS] = convertTo(ttb); + if (ci->ttb[TTB_BOLDS] < 0) + throw ConvertException(); + } + catch (const ConvertException &) + { + ci->ttb[TTB_BOLDS] = 0; + source.Reply(_("\002%s\002 cannot be taken as times to ban."), ttb.c_str()); + return; + } + } + else + ci->ttb[TTB_BOLDS] = 0; + ci->botflags.SetFlag(BS_KICK_BOLDS); + if (ci->ttb[TTB_BOLDS]) + source.Reply(_("Bot will now kick \002bolds\002, and will place a ban after\n%d kicks to the same user."), ci->ttb[TTB_BOLDS]); + else + source.Reply(_("Bot will now kick \002bolds\002.")); + } + else + { + ci->botflags.UnsetFlag(BS_KICK_BOLDS); + source.Reply(_("Bot won't kick \002bolds\002 anymore.")); + } + } + else if (option.equals_ci("CAPS")) + { + if (value.equals_ci("ON")) + { + Anope::string min = params.size() > 4 ? params[4] : ""; + Anope::string percent = params.size() > 5 ? params[5] : ""; + + if (!ttb.empty()) + { + try + { + ci->ttb[TTB_CAPS] = convertTo(ttb); + if (ci->ttb[TTB_CAPS] < 0) + throw ConvertException(); + } + catch (const ConvertException &) + { + ci->ttb[TTB_CAPS] = 0; + source.Reply(_("\002%s\002 cannot be taken as times to ban."), ttb.c_str()); + return; + } + } + else + ci->ttb[TTB_CAPS] = 0; + + ci->capsmin = 10; + try + { + ci->capsmin = convertTo(min); + } + catch (const ConvertException &) { } + if (ci->capsmin < 1) + ci->capsmin = 10; + + ci->capspercent = 25; + try + { + ci->capspercent = convertTo(percent); + } + catch (const ConvertException &) { } + if (ci->capspercent < 1 || ci->capspercent > 100) + ci->capspercent = 25; + + ci->botflags.SetFlag(BS_KICK_CAPS); + if (ci->ttb[TTB_CAPS]) + source.Reply(_("Bot will now kick \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."), ci->capsmin, ci->capspercent, ci->ttb[TTB_CAPS]); + else + source.Reply(_("Bot will now kick \002caps\002 (they must constitute at least\n" + "%d characters and %d%% of the entire message)."), ci->capsmin, ci->capspercent); + } + else + { + ci->botflags.UnsetFlag(BS_KICK_CAPS); + source.Reply(_("Bot won't kick \002caps\002 anymore.")); + } + } + else if (option.equals_ci("COLORS")) + { + if (value.equals_ci("ON")) + { + if (!ttb.empty()) + { + try + { + ci->ttb[TTB_COLORS] = convertTo(ttb); + if (ci->ttb[TTB_COLORS] < 1) + throw ConvertException(); + } + catch (const ConvertException &) + { + ci->ttb[TTB_COLORS] = 0; + source.Reply(_("\002%s\002 cannot be taken as times to ban."), ttb.c_str()); + return; + } + } + else + ci->ttb[TTB_COLORS] = 0; + + ci->botflags.SetFlag(BS_KICK_COLORS); + if (ci->ttb[TTB_COLORS]) + source.Reply(_("Bot will now kick \002colors\002, and will place a ban after %d\nkicks for the same user."), ci->ttb[TTB_COLORS]); + else + source.Reply(_("Bot will now kick \002colors\002.")); + } + else + { + ci->botflags.UnsetFlag(BS_KICK_COLORS); + source.Reply(_("Bot won't kick \002colors\002 anymore.")); + } + } + else if (option.equals_ci("FLOOD")) + { + if (value.equals_ci("ON")) + { + Anope::string lines = params.size() > 4 ? params[4] : ""; + Anope::string secs = params.size() > 5 ? params[5] : ""; + + if (!ttb.empty()) + { + try + { + ci->ttb[TTB_FLOOD] = convertTo(ttb); + if (ci->ttb[TTB_FLOOD] < 1) + throw ConvertException(); + } + catch (const ConvertException &) + { + ci->ttb[TTB_FLOOD] = 0; + source.Reply(_("\002%s\002 cannot be taken as times to ban."), ttb.c_str()); + return; + } + } + else + ci->ttb[TTB_FLOOD] = 0; + + ci->floodlines = 6; + try + { + ci->floodlines = convertTo(lines); + } + catch (const ConvertException &) { } + if (ci->floodlines < 2) + ci->floodlines = 6; + + ci->floodsecs = 10; + try + { + ci->floodsecs = convertTo(secs); + } + catch (const ConvertException &) { } + if (ci->floodsecs < 1) + ci->floodsecs = 10; + if (ci->floodsecs > Config->BSKeepData) + ci->floodsecs = Config->BSKeepData; + + ci->botflags.SetFlag(BS_KICK_FLOOD); + if (ci->ttb[TTB_FLOOD]) + source.Reply(_("Bot will now kick \002flood\002 (%d lines in %d seconds and\nwill place a ban after %d kicks for the same user."), ci->floodlines, ci->floodsecs, ci->ttb[TTB_FLOOD]); + else + source.Reply(_("Bot will now kick \002flood\002 (%d lines in %d seconds)."), ci->floodlines, ci->floodsecs); + } + else + { + ci->botflags.UnsetFlag(BS_KICK_FLOOD); + source.Reply(_("Bot won't kick \002flood\002 anymore.")); + } + } + else if (option.equals_ci("REPEAT")) + { + if (value.equals_ci("ON")) + { + Anope::string times = params.size() > 4 ? params[4] : ""; + + if (!ttb.empty()) + { + try + { + ci->ttb[TTB_REPEAT] = convertTo(ttb); + if (ci->ttb[TTB_REPEAT] < 0) + throw ConvertException(); + } + catch (const ConvertException &) + { + ci->ttb[TTB_REPEAT] = 0; + source.Reply(_("\002%s\002 cannot be taken as times to ban."), ttb.c_str()); + return; + } + } + else + ci->ttb[TTB_REPEAT] = 0; + + ci->repeattimes = 3; + try + { + ci->repeattimes = convertTo(times); + } + catch (const ConvertException &) { } + if (ci->repeattimes < 2) + ci->repeattimes = 3; + + ci->botflags.SetFlag(BS_KICK_REPEAT); + if (ci->ttb[TTB_REPEAT]) + source.Reply(_("Bot will now kick \002repeats\002 (users that say %d times\n" + "the same thing), and will place a ban after %d \n" + "kicks for the same user."), ci->repeattimes, ci->ttb[TTB_REPEAT]); + else + source.Reply(_("Bot will now kick \002repeats\002 (users that say %d times\n" + "the same thing)."), ci->repeattimes); + } + else + { + ci->botflags.UnsetFlag(BS_KICK_REPEAT); + source.Reply(_("Bot won't kick \002repeats\002 anymore.")); + } + } + else if (option.equals_ci("REVERSES")) + { + if (value.equals_ci("ON")) + { + if (!ttb.empty()) + { + try + { + ci->ttb[TTB_REVERSES] = convertTo(ttb); + if (ci->ttb[TTB_REVERSES] < 0) + throw ConvertException(); + } + catch (const ConvertException &) + { + ci->ttb[TTB_REVERSES] = 0; + source.Reply(_("\002%s\002 cannot be taken as times to ban."), ttb.c_str()); + return; + } + } + else + ci->ttb[TTB_REVERSES] = 0; + ci->botflags.SetFlag(BS_KICK_REVERSES); + if (ci->ttb[TTB_REVERSES]) + source.Reply(_("Bot will now kick \002reverses\002, and will place a ban after %d\nkicks for the same user."), ci->ttb[TTB_REVERSES]); + else + source.Reply(_("Bot will now kick \002reverses\002.")); + } + else + { + ci->botflags.UnsetFlag(BS_KICK_REVERSES); + source.Reply(_("Bot won't kick \002reverses\002 anymore.")); + } + } + else if (option.equals_ci("UNDERLINES")) + { + if (value.equals_ci("ON")) + { + if (!ttb.empty()) + { + try + { + ci->ttb[TTB_UNDERLINES] = convertTo(ttb); + if (ci->ttb[TTB_REVERSES] < 0) + throw ConvertException(); + } + catch (const ConvertException &) + { + ci->ttb[TTB_UNDERLINES] = 0; + source.Reply(_("\002%s\002 cannot be taken as times to ban."), ttb.c_str()); + return; + } + } + else + ci->ttb[TTB_UNDERLINES] = 0; + + ci->botflags.SetFlag(BS_KICK_UNDERLINES); + if (ci->ttb[TTB_UNDERLINES]) + source.Reply(_("Bot will now kick \002underlines\002, and will place a ban after %d\nkicks for the same user."), ci->ttb[TTB_UNDERLINES]); + else + source.Reply(_("Bot will now kick \002underlines\002.")); + } + else + { + ci->botflags.UnsetFlag(BS_KICK_UNDERLINES); + source.Reply(_("Bot won't kick \002underlines\002 anymore.")); + } + } + else if (option.equals_ci("ITALICS")) + { + if (value.equals_ci("ON")) + { + if (!ttb.empty()) + { + try + { + ci->ttb[TTB_ITALICS] = convertTo(ttb); + if (ci->ttb[TTB_ITALICS] < 0) + throw ConvertException(); + } + catch (const ConvertException &) + { + ci->ttb[TTB_ITALICS] = 0; + source.Reply(_("\002%s\002 cannot be taken as times to ban."), ttb.c_str()); + return; + } + } + else + ci->ttb[TTB_ITALICS] = 0; + + ci->botflags.SetFlag(BS_KICK_ITALICS); + if (ci->ttb[TTB_ITALICS]) + source.Reply(_("Bot will now kick \002italics\002, and will place a ban after\n%d kicks for the same user."), ci->ttb[TTB_ITALICS]); + else + source.Reply(_("Bot will now kick \002italics\002.")); + } + else + { + ci->botflags.UnsetFlag(BS_KICK_ITALICS); + source.Reply(_("Bot won't kick \002italics\002 anymore.")); + } + } + else if (option.equals_ci("AMSGS")) + { + if (value.equals_ci("ON")) + { + if (!ttb.empty()) + { + try + { + ci->ttb[TTB_AMSGS] = convertTo(ttb); + if (ci->ttb[TTB_AMSGS] < 0) + throw ConvertException(); + } + catch (const ConvertException &) + { + ci->ttb[TTB_AMSGS] = 0; + source.Reply(_("\002%s\002 cannot be taken as times to ban."), ttb.c_str()); + return; + } + } + else + ci->ttb[TTB_AMSGS] = 0; + + ci->botflags.SetFlag(BS_KICK_AMSGS); + if (ci->ttb[TTB_AMSGS]) + source.Reply(_("Bot will now kick for \002amsgs\002, and will place a ban after %d\nkicks for the same user."), ci->ttb[TTB_AMSGS]); + else + source.Reply(_("Bot will now kick for \002amsgs\002")); + } + else + { + ci->botflags.UnsetFlag(BS_KICK_AMSGS); + source.Reply(_("Bot won't kick for \002amsgs\002 anymore.")); + } + } + else + source.Reply(UNKNOWN_OPTION, Config->UseStrictPrivMsgString.c_str(), option.c_str(), this->name.c_str()); + } + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + if (subcommand.empty()) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Configures bot kickers. \037option\037 can be one of:\n" + " \n" + " AMSGS Sets if the bot kicks for amsgs\n" + " BOLDS Sets if the bot kicks bolds\n" + " BADWORDS Sets if the bot kicks bad words\n" + " CAPS Sets if the bot kicks caps\n" + " COLORS Sets if the bot kicks colors\n" + " FLOOD Sets if the bot kicks flooding users\n" + " REPEAT Sets if the bot kicks users who repeat\n" + " themselves\n" + " REVERSES Sets if the bot kicks reverses\n" + " UNDERLINES Sets if the bot kicks underlines\n" + " ITALICS Sets if the bot kicks italics\n" + " \n" + "Type \002%s%s HELP KICK \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->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str()); + } + else if (subcommand.equals_ci("BADWORDS")) + source.Reply(_("Syntax: \002\037#channel\037 BADWORDS {\037ON|OFF\037} [\037ttb\037]\002\n" + "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" + "ttb is the number of times a user can be kicked\n" + "before it get banned. Don't give ttb to disable\n" + "the ban system once activated."), Config->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str()); + else if (subcommand.equals_ci("BOLDS")) + source.Reply(_("Syntax: \002\037channel\037 BOLDS {\037ON|OFF\037} [\037ttb\037]\002\n" + "Sets the bolds kicker on or off. When enabled, this\n" + "option tells the bot to kick users who use bolds.\n" + "ttb is the number of times a user can be kicked\n" + "before it get banned. Don't give ttb to disable\n" + "the ban system once activated.")); + else if (subcommand.equals_ci("CAPS")) + source.Reply(_("Syntax: \002\037channel\037 CAPS {\037ON|OFF\037} [\037ttb\037 [\037min\037 [\037percent\037]]]\002\n" + "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" + "ttb is the number of times a user can be kicked\n" + "before it get banned. Don't give ttb to disable\n" + "the ban system once activated.")); + else if (subcommand.equals_ci("COLORS")) + source.Reply(_("Syntax: \002\037channel\037 COLORS {\037ON|OFF\037} [\037ttb\037]\002\n" + "Sets the colors kicker on or off. When enabled, this\n" + "option tells the bot to kick users who use colors.\n" + "ttb is the number of times a user can be kicked\n" + "before it get banned. Don't give ttb to disable\n" + "the ban system once activated.")); + else if (subcommand.equals_ci("FLOOD")) + source.Reply(_("Syntax: \002\037channel\037 FLOOD {\037ON|OFF\037} [\037ttb\037 [\037ln\037 [\037secs\037]]]\002\n" + "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" + "ttb is the number of times a user can be kicked\n" + "before it get banned. Don't give ttb to disable\n" + "the ban system once activated.")); + else if (subcommand.equals_ci("REPEAT")) + source.Reply(_("Syntax: \002\037#channel\037 REPEAT {\037ON|OFF\037} [\037ttb\037 [\037num\037]]\002\n" + "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" + "ttb is the number of times a user can be kicked\n" + "before it get banned. Don't give ttb to disable\n" + "the ban system once activated.")); + else if (subcommand.equals_ci("REVERSES")) + source.Reply(_("Syntax: \002\037channel\037 REVERSES {\037ON|OFF\037} [\037ttb\037]\002\n" + "Sets the reverses kicker on or off. When enabled, this\n" + "option tells the bot to kick users who use reverses.\n" + "ttb is the number of times a user can be kicked\n" + "before it get banned. Don't give ttb to disable\n" + "the ban system once activated.")); + else if (subcommand.equals_ci("UNDERLINES")) + source.Reply(_("Syntax: \002\037channel\037 UNDERLINES {\037ON|OFF\037} [\037ttb\037]\002\n" + "Sets the underlines kicker on or off. When enabled, this\n" + "option tells the bot to kick users who use underlines.\n" + "ttb is the number of times a user can be kicked\n" + "before it get banned. Don't give ttb to disable\n" + "the ban system once activated.")); + else if (subcommand.equals_ci("ITALICS")) + source.Reply(_("Syntax: \002\037channel\037 ITALICS {\037ON|OFF\037} [\037ttb\037]\002\n" + "Sets the italics kicker on or off. When enabled, this\n" + "option tells the bot to kick users who use italics.\n" + "ttb is the number of times a user can be kicked\n" + "before it get banned. Don't give ttb to disable\n" + "the ban system once activated.")); + else if (subcommand.equals_ci("AMSGS")) + source.Reply(_("Syntax: \002\037channel\037 AMSGS {\037ON|OFF\037} [\037ttb\037]\002\n" + "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 BotServ bots are.\n" + "Ttb is the number of times a user can be kicked\n" + "before it get banned. Don't give ttb to disable\n" + "the ban system once activated.")); + else + return false; + + return true; + } +}; + +struct BanData +{ + Anope::string mask; + time_t last_use; + int16 ttb[TTB_SIZE]; + + BanData() + { + this->Clear(); + } + + void Clear() + { + last_use = 0; + for (int i = 0; i < TTB_SIZE; ++i) + this->ttb[i] = 0; + } +}; + +struct UserData +{ + UserData() + { + this->Clear(); + } + + void Clear() + { + last_use = last_start = Anope::CurTime; + lines = times = 0; + lastline.clear(); + } + + /* Data validity */ + time_t last_use; + + /* for flood kicker */ + int16 lines; + time_t last_start; + + /* for repeat kicker */ + Anope::string lastline; + Anope::string lasttarget; + int16 times; +}; + + +class BanDataPurger : public CallBack +{ + public: + BanDataPurger(Module *owner) : CallBack(owner, 300, Anope::CurTime, true) { } + + void Tick(time_t) + { + 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; + + std::map bandata; + if (c->GetExtRegular("bs_main_bandata", bandata)) + { + for (std::map::iterator it2 = bandata.begin(), it2_end = bandata.end(); it2 != it2_end;) + { + const Anope::string &user = it->first; + BanData *bd = &it2->second; + ++it2; + + if (Anope::CurTime - bd->last_use > Config->BSKeepData) + { + bandata.erase(user); + continue; + } + } + + if (bandata.empty()) + c->Shrink("bs_main_bandata"); + } + } + } +}; + +class BSKick : public Module +{ + CommandBSKick commandbskick; + BanDataPurger purger; + + BanData *GetBanData(User *u, Channel *c) + { + std::map bandatamap; + if (!c->GetExtRegular("bs_main_bandata", bandatamap)); + c->Extend("bs_main_bandata", new ExtensibleItemRegular >(bandatamap)); + c->GetExtRegular("bs_main_bandata", bandatamap); + + BanData *bd = &bandatamap[u->GetMask()]; + if (bd->last_use && Anope::CurTime - bd->last_use > Config->BSKeepData) + bd->Clear(); + bd->last_use = Anope::CurTime; + return bd; + } + + UserData *GetUserData(User *u, Channel *c) + { + UserData *ud = NULL; + UserContainer *uc = c->FindUser(u); + if (uc != NULL && !uc->GetExtPointer("bs_main_userdata", ud)) + { + ud = new UserData(); + uc->Extend("bs_main_userdata", new ExtensibleItemPointer(ud)); + } + return ud; + } + + void check_ban(ChannelInfo *ci, User *u, int ttbtype) + { + /* Don't ban ulines */ + if (u->server->IsULined()) + return; + + BanData *bd = this->GetBanData(u, ci->c); + + ++bd->ttb[ttbtype]; + if (ci->ttb[ttbtype] && bd->ttb[ttbtype] >= ci->ttb[ttbtype]) + { + /* Should not use == here because bd->ttb[ttbtype] could possibly be > ci->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 */ + Anope::string mask; + + bd->ttb[ttbtype] = 0; + + get_idealban(ci, u, mask); + + if (ci->c) + ci->c->SetMode(NULL, CMODE_BAN, mask); + FOREACH_MOD(I_OnBotBan, 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->server->IsULined()) + return; + + Anope::string fmt = 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, CORE), + commandbskick(this), purger(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandbskick); + + ModuleManager::Attach(I_OnPrivmsg, this); + } + + ~BSKick() + { + for (channel_map::const_iterator cit = ChannelList.begin(), cit_end = ChannelList.end(); cit != cit_end; ++cit) + { + cit->second->Shrink("bs_main_userdata"); + cit->second->Shrink("bs_main_bandata"); + } + } + + void OnPrivmsg(User *u, Channel *c, Anope::string &msg) + { + /* 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; + + bool Allow = true; + if (ci->HasPriv(u, CA_NOKICK)) + Allow = false; + else if (ci->botflags.HasFlag(BS_DONTKICKOPS) && (c->HasUserStatus(u, CMODE_HALFOP) || c->HasUserStatus(u, CMODE_OP) || c->HasUserStatus(u, CMODE_PROTECT) || c->HasUserStatus(u, CMODE_OWNER))) + Allow = false; + else if (ci->botflags.HasFlag(BS_DONTKICKVOICES) && c->HasUserStatus(u, CMODE_VOICE)) + Allow = false; + + if (Allow) + { + 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 (ci->botflags.HasFlag(BS_KICK_BOLDS) && realbuf.find(2) != Anope::string::npos) + { + check_ban(ci, u, TTB_BOLDS); + bot_kick(ci, u, _("Don't use bolds on this channel!")); + return; + } + + /* Color kicker */ + if (ci->botflags.HasFlag(BS_KICK_COLORS) && realbuf.find(3) != Anope::string::npos) + { + check_ban(ci, u, TTB_COLORS); + bot_kick(ci, u, _("Don't use colors on this channel!")); + return; + } + + /* Reverses kicker */ + if (ci->botflags.HasFlag(BS_KICK_REVERSES) && realbuf.find(22) != Anope::string::npos) + { + check_ban(ci, u, TTB_REVERSES); + bot_kick(ci, u, _("Don't use reverses on this channel!")); + return; + } + + /* Italics kicker */ + if (ci->botflags.HasFlag(BS_KICK_ITALICS) && realbuf.find(29) != Anope::string::npos) + { + check_ban(ci, u, TTB_ITALICS); + bot_kick(ci, u, _("Don't use italics on this channel!")); + return; + } + + /* Underlines kicker */ + if (ci->botflags.HasFlag(BS_KICK_UNDERLINES) && realbuf.find(31) != Anope::string::npos) + { + check_ban(ci, u, TTB_UNDERLINES); + bot_kick(ci, u, _("Don't use underlines on this channel!")); + return; + } + + /* Caps kicker */ + if (ci->botflags.HasFlag(BS_KICK_CAPS) && realbuf.length() >= ci->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 >= ci->capsmin && i * 100 / (i + l) >= ci->capspercent) + { + check_ban(ci, u, TTB_CAPS); + bot_kick(ci, u, _("Turn caps lock OFF!")); + return; + } + } + + /* Bad words kicker */ + if (ci->botflags.HasFlag(BS_KICK_BADWORDS)) + { + bool mustkick = false; + + /* Normalize the buffer */ + Anope::string nbuf = normalizeBuffer(realbuf); + + for (unsigned i = 0, end = ci->GetBadWordCount(); i < end; ++i) + { + BadWord *bw = ci->GetBadWord(i); + + if (bw->type == BW_ANY && ((Config->BSCaseSensitive && nbuf.find(bw->word) != Anope::string::npos) || (!Config->BSCaseSensitive && nbuf.find_ci(bw->word) != Anope::string::npos))) + mustkick = true; + else if (bw->type == BW_SINGLE) + { + size_t len = bw->word.length(); + + if ((Config->BSCaseSensitive && bw->word.equals_cs(nbuf)) || (!Config->BSCaseSensitive && bw->word.equals_ci(nbuf))) + mustkick = true; + else if (nbuf.find(' ') == len && ((Config->BSCaseSensitive && bw->word.equals_cs(nbuf)) || (!Config->BSCaseSensitive && bw->word.equals_ci(nbuf)))) + mustkick = true; + else + { + if (nbuf.rfind(' ') == nbuf.length() - len - 1 && ((Config->BSCaseSensitive && nbuf.find(bw->word) == nbuf.length() - len) || (!Config->BSCaseSensitive && nbuf.find_ci(bw->word) == nbuf.length() - len))) + mustkick = true; + else + { + Anope::string wordbuf = " " + bw->word + " "; + + if ((Config->BSCaseSensitive && nbuf.find(wordbuf) != Anope::string::npos) || (!Config->BSCaseSensitive && nbuf.find_ci(wordbuf) != Anope::string::npos)) + mustkick = true; + } + } + } + else if (bw->type == BW_START) + { + size_t len = bw->word.length(); + + if ((Config->BSCaseSensitive && nbuf.substr(0, len).equals_cs(bw->word)) || (!Config->BSCaseSensitive && nbuf.substr(0, len).equals_ci(bw->word))) + mustkick = true; + else + { + Anope::string wordbuf = " " + bw->word; + + if ((Config->BSCaseSensitive && nbuf.find(wordbuf) != Anope::string::npos) || (!Config->BSCaseSensitive && nbuf.find_ci(wordbuf) != Anope::string::npos)) + mustkick = true; + } + } + else if (bw->type == BW_END) + { + size_t len = bw->word.length(); + + if ((Config->BSCaseSensitive && nbuf.substr(nbuf.length() - len).equals_cs(bw->word)) || (!Config->BSCaseSensitive && nbuf.substr(nbuf.length() - len).equals_ci(bw->word))) + mustkick = true; + else + { + Anope::string wordbuf = bw->word + " "; + + if ((Config->BSCaseSensitive && nbuf.find(wordbuf) != Anope::string::npos) || (!Config->BSCaseSensitive && nbuf.find_ci(wordbuf) != Anope::string::npos)) + mustkick = true; + } + } + + if (mustkick) + { + check_ban(ci, u, TTB_BADWORDS); + if (Config->BSGentleBWReason) + 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; + } + } + + UserData *ud = NULL; + + /* Flood kicker */ + if (ci->botflags.HasFlag(BS_KICK_FLOOD)) + { + ud = GetUserData(u, c); + if (ud) + { + if (Anope::CurTime - ud->last_start > ci->floodsecs) + { + ud->last_start = Anope::CurTime; + ud->lines = 0; + } + + ++ud->lines; + if (ud->lines >= ci->floodlines) + { + check_ban(ci, u, TTB_FLOOD); + bot_kick(ci, u, _("Stop flooding!")); + return; + } + } + } + + /* Repeat kicker */ + if (ci->botflags.HasFlag(BS_KICK_REPEAT)) + { + if (!ud) + ud = GetUserData(u, c); + if (ud) + { + + if (!ud->lastline.empty() && !ud->lastline.equals_ci(realbuf)) + { + ud->lastline = realbuf; + ud->times = 0; + } + else + { + if (ud->lastline.empty()) + ud->lastline = realbuf; + ++ud->times; + } + + if (ud->times >= ci->repeattimes) + { + check_ban(ci, u, TTB_REPEAT); + bot_kick(ci, u, _("Stop repeating yourself!")); + return; + } + } + } + + if (ud && ud->lastline.equals_ci(realbuf) && !ud->lasttarget.empty() && !ud->lasttarget.equals_ci(ci->name)) + { + for (UChannelList::iterator it = u->chans.begin(); it != u->chans.end();) + { + Channel *chan = (*it)->chan; + ++it; + + if (chan->ci != NULL && chan->ci->botflags.HasFlag(BS_KICK_AMSGS) && !chan->ci->HasPriv(u, CA_NOKICK)) + { + check_ban(chan->ci, u, TTB_AMSGS); + bot_kick(chan->ci, u, _("Don't use AMSGs!")); + } + } + } + + if (ud) + ud->lasttarget = ci->name; + } + } + } +}; + +MODULE_INIT(BSKick) diff --git a/modules/commands/bs_set.cpp b/modules/commands/bs_set.cpp new file mode 100644 index 000000000..ccd1bb615 --- /dev/null +++ b/modules/commands/bs_set.cpp @@ -0,0 +1,307 @@ +/* BotServ core functions + * + * (C) 2003-2011 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 CommandBSSet : public Command +{ + public: + CommandBSSet(Module *creator) : Command(creator, "botserv/set", 3, 3) + { + this->SetDesc(_("Configures bot options")); + this->SetSyntax(_("\037(channel | bot)\037 \037option\037 \037settings\037")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + const Anope::string &chan = params[0]; + const Anope::string &option = params[1]; + const Anope::string &value = params[2]; + + User *u = source.u; + ChannelInfo *ci; + + if (readonly) + source.Reply(_("Sorry, bot option setting is temporarily disabled.")); + else if (u->HasCommand("botserv/botserv/set/private") && option.equals_ci("PRIVATE")) + { + BotInfo *bi; + + if (!(bi = findbot(chan))) + { + source.Reply(BOT_DOES_NOT_EXIST, chan.c_str()); + return; + } + + if (value.equals_ci("ON")) + { + bi->SetFlag(BI_PRIVATE); + source.Reply(_("Private mode of bot %s is now \002on\002."), bi->nick.c_str()); + } + else if (value.equals_ci("OFF")) + { + bi->UnsetFlag(BI_PRIVATE); + source.Reply(_("Private mode of bot %s is now \002off\002."), bi->nick.c_str()); + } + else + this->OnSyntaxError(source, "PRIVATE"); + return; + } + else if (!(ci = cs_findchan(chan))) + source.Reply(CHAN_X_NOT_REGISTERED, chan.c_str()); + else if (!u->HasPriv("botserv/administration") && !ci->HasPriv(u, CA_SET)) + source.Reply(ACCESS_DENIED); + else + { + bool override = !ci->HasPriv(u, CA_SET); + Log(override ? LOG_ADMIN : LOG_COMMAND, u, this, ci) << option << " " << value; + + if (option.equals_ci("DONTKICKOPS")) + { + if (value.equals_ci("ON")) + { + ci->botflags.SetFlag(BS_DONTKICKOPS); + source.Reply(_("Bot \002won't kick ops\002 on channel %s."), ci->name.c_str()); + } + else if (value.equals_ci("OFF")) + { + ci->botflags.UnsetFlag(BS_DONTKICKOPS); + source.Reply(_("Bot \002will kick ops\002 on channel %s."), ci->name.c_str()); + } + else + this->OnSyntaxError(source, "DONTKICKOPS"); + } + else if (option.equals_ci("DONTKICKVOICES")) + { + if (value.equals_ci("ON")) + { + ci->botflags.SetFlag(BS_DONTKICKVOICES); + source.Reply(_("Bot \002won't kick voices\002 on channel %s."), ci->name.c_str()); + } + else if (value.equals_ci("OFF")) + { + ci->botflags.UnsetFlag(BS_DONTKICKVOICES); + source.Reply(_("Bot \002will kick voices\002 on channel %s."), ci->name.c_str()); + } + else + this->OnSyntaxError(source, "DONTKICKVOICE"); + } + else if (option.equals_ci("FANTASY")) + { + if (value.equals_ci("ON")) + { + ci->botflags.SetFlag(BS_FANTASY); + source.Reply(_("Fantasy mode is now \002on\002 on channel %s."), ci->name.c_str()); + } + else if (value.equals_ci("OFF")) + { + ci->botflags.UnsetFlag(BS_FANTASY); + source.Reply(_("Fantasy mode is now \002off\002 on channel %s."), ci->name.c_str()); + } + else + this->OnSyntaxError(source, "FANTASY"); + } + else if (option.equals_ci("GREET")) + { + if (value.equals_ci("ON")) + { + ci->botflags.SetFlag(BS_GREET); + source.Reply(_("Greet mode is now \002on\002 on channel %s."), ci->name.c_str()); + } + else if (value.equals_ci("OFF")) + { + ci->botflags.UnsetFlag(BS_GREET); + source.Reply(_("Greet mode is now \002off\002 on channel %s."), ci->name.c_str()); + } + else + this->OnSyntaxError(source, "GREET"); + } + else if (u->HasCommand("botserv/botserv/set/nobot") && option.equals_ci("NOBOT")) + { + if (value.equals_ci("ON")) + { + ci->botflags.SetFlag(BS_NOBOT); + if (ci->bi) + ci->bi->UnAssign(u, ci); + source.Reply(_("No Bot mode is now \002on\002 on channel %s."), ci->name.c_str()); + } + else if (value.equals_ci("OFF")) + { + ci->botflags.UnsetFlag(BS_NOBOT); + source.Reply(_("No Bot mode is now \002off\002 on channel %s."), ci->name.c_str()); + } + else + this->OnSyntaxError(source, "NOBOT"); + } + else if (option.equals_ci("MSG")) + { + if (value.equals_ci("OFF")) + { + ci->botflags.UnsetFlag(BS_MSG_PRIVMSG); + ci->botflags.UnsetFlag(BS_MSG_NOTICE); + ci->botflags.UnsetFlag(BS_MSG_NOTICEOPS); + source.Reply(_("Fantasy replies will no longer be sent to %s."), ci->name.c_str()); + } + else if (value.equals_ci("PRIVMSG")) + { + ci->botflags.SetFlag(BS_MSG_PRIVMSG); + ci->botflags.UnsetFlag(BS_MSG_NOTICE); + ci->botflags.UnsetFlag(BS_MSG_NOTICEOPS); + source.Reply(_("Fantasy replies will be sent via PRIVMSG to %s."), ci->name.c_str()); + } + else if (value.equals_ci("NOTICE")) + { + ci->botflags.UnsetFlag(BS_MSG_PRIVMSG); + ci->botflags.SetFlag(BS_MSG_NOTICE); + ci->botflags.UnsetFlag(BS_MSG_NOTICEOPS); + source.Reply(_("Fantasy replies will be sent via NOTICE to %s."), ci->name.c_str()); + } + else if (value.equals_ci("NOTICEOPS")) + { + ci->botflags.UnsetFlag(BS_MSG_PRIVMSG); + ci->botflags.UnsetFlag(BS_MSG_NOTICE); + ci->botflags.SetFlag(BS_MSG_NOTICEOPS); + source.Reply(_("Fantasy replies will be sent via NOTICE to channel ops on %s."), ci->name.c_str()); + } + else + this->OnSyntaxError(source, "MSG"); + } + else + source.Reply(UNKNOWN_OPTION, option.c_str(), Config->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str(), this->name.c_str()); + } + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + if (subcommand.empty()) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Configures bot options. \037option\037 can be one of:\n" + " \n" + " DONTKICKOPS To protect ops against bot kicks\n" + " DONTKICKVOICES To protect voices against bot kicks\n" + " GREET Enable greet messages\n" + " FANTASY Enable fantaisist commands\n" + " MSG Configure how fantasy commands should be replied to\n" + " \n" + "Type \002%s%s HELP SET \037option\037\002 for more information\n" + "on a specific option.\n" + "Note: access to this command is controlled by the\n" + "level SET."), Config->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str()); + User *u = source.u; + if (u->IsServicesOper()) + source.Reply(_("These options are reserved to Services Operators:\n" + " \n" + " NOBOT Prevent a bot from being assigned to \n" + " a channel\n" + " PRIVATE Prevent a bot from being assigned by\n" + " non IRC operators")); + } + else if (subcommand.equals_ci("DONTKICKOPS")) + source.Reply(_("Syntax: \002SET \037channel\037 DONTKICKOPS {\037ON|OFF\037}\n" + " \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.")); + else if (subcommand.equals_ci("DONTKICKVOICES")) + source.Reply(_("Syntax: \002SET \037channel\037 DONTKICKVOICES {\037ON|OFF\037}\n" + " \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.")); + else if (subcommand.equals_ci("FANTASY")) + source.Reply(_("Syntax: \002SET \037channel\037 FANTASY {\037ON|OFF\037}\n" + "Enables or disables \002fantasy\002 mode on a channel.\n" + "When it is enabled, users will be able to use\n" + "commands !op, !deop, !voice, !devoice,\n" + "!kick, !kb, !unban, !seen on a channel (find how \n" + "to use them; try with or without nick for each, \n" + "and with a reason for some?).\n" + " \n" + "Note that users wanting to use fantaisist\n" + "commands MUST have enough level for both\n" + "the FANTASIA and another level depending\n" + "of the command if required (for example, to use \n" + "!op, user must have enough access for the OPDEOP\n" + "level).")); + else if (subcommand.equals_ci("GREET")) + source.Reply(_("Syntax: \002SET \037channel\037 GREET {\037ON|OFF\037}\n" + " \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.")); + else if (subcommand.equals_ci("NOBOT")) + source.Reply(_("Syntax: \002SET \037channel\037 NOBOT {\037ON|OFF\037}\002\n" + " \n" + "This option makes a channel be unassignable. If a bot \n" + "is already assigned to the channel, it is unassigned\n" + "automatically when you enable the option.")); + else if (subcommand.equals_ci("PRIVATE")) + source.Reply(_("Syntax: \002SET \037bot-nick\037 PRIVATE {\037ON|OFF\037}\002\n" + "This option prevents a bot from being assigned to a\n" + "channel by users that aren't IRC operators.")); + else if (subcommand.equals_ci("MSG")) + source.Reply(_("Syntax: \002SET \037channel\037 MSG {\037OFF|PRIVMSG|NOTICE|NOTICEOPS\037}\002\n" + " \n" + "Configures how fantasy commands should be returned to the channel. Off disables\n" + "fantasy from replying to the channel. Privmsg, notice, and noticeops message the\n" + "channel, notice the channel, and notice the channel ops respectively.\n" + " \n" + "Note that replies over one line will not use this setting to prevent spam, and will\n" + "go directly to the user who executed it.")); + else + return false; + + return true; + } + + void OnSyntaxError(CommandSource &source, const Anope::string &subcommand) + { + if (subcommand.empty()) + Command::OnSyntaxError(source, ""); + else if (subcommand.equals_ci("PRIVATE")) + this->SendSyntax(source, "\037botname\037 PRIVATE {\037ON|OFF\037}"); + else if (subcommand.equals_ci("DONTKICKOPS")) + this->SendSyntax(source, "\037channel\037 DONTKICKOPS {\037ON|OFF\037}"); + else if (subcommand.equals_ci("DONTKICKVOICES")) + this->SendSyntax(source, "\037channel\037 DONTKICKVOICES {\037ON|OFF\037}"); + else if (subcommand.equals_ci("FANTASY")) + this->SendSyntax(source, "\037channel\037 FANTASY {\037ON|OFF\037}"); + else if (subcommand.equals_ci("GREET")) + this->SendSyntax(source, "\037channel\037 GREET {\037ON|OFF\037}"); + else if (subcommand.equals_ci("MSG")) + this->SendSyntax(source, "\037botname\037 NOBOT {\037ON|OFF\037}"); + else if (subcommand.equals_ci("NOBOT")) + this->SendSyntax(source, "\037botname\037 NOBOT {\037ON|OFF\037}"); + } +}; + +class BSSet : public Module +{ + CommandBSSet commandbsset; + + public: + BSSet(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandbsset(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandbsset); + } +}; + +MODULE_INIT(BSSet) diff --git a/modules/commands/cs_access.cpp b/modules/commands/cs_access.cpp new file mode 100644 index 000000000..777b0c176 --- /dev/null +++ b/modules/commands/cs_access.cpp @@ -0,0 +1,915 @@ +/* ChanServ core functions + * + * (C) 2003-2011 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" + +enum +{ + ACCESS_INVALID = -10000, + ACCESS_FOUNDER = 10001 +}; + +static struct AccessLevels +{ + ChannelAccess priv; + int default_level; + Anope::string config_name; + Anope::string name; + Anope::string desc; +} defaultLevels[] = { + { CA_ACCESS_CHANGE, 10, "level_change", "ACC-CHANGE", _("Allowed to modify the access list") }, + { CA_ACCESS_LIST, 1, "level_list", "ACC-LIST", _("Allowed to view the access list") }, + { CA_AKICK, 10, "level_akick", "AKICK", _("Allowed to use AKICK command") }, + { CA_ASSIGN, ACCESS_FOUNDER, "level_assign", "ASSIGN", _("Allowed to assign/unassign a bot") }, + { CA_AUTOHALFOP, 4, "level_autohalfop", "AUTOHALFOP", _("Automatic mode +h") }, + { CA_AUTOOP, 5, "level_autoop", "AUTOOP", _("Automatic channel operator status") }, + { CA_AUTOOWNER, 10000, "level_autoowner", "AUTOOWNER", _("Automatic mode +q") }, + { CA_AUTOPROTECT, 10, "level_autoprotect", "AUTOPROTECT", _("Automatic mode +a") }, + { CA_AUTOVOICE, 3, "level_autovoice", "AUTOVOICE", _("Automatic mode +v") }, + { CA_BADWORDS, 10, "level_badwords", "BADWORDS", _("Allowed to modify channel badwords list") }, + { CA_BAN, 4, "level_ban", "BAN", _("Allowed to use ban users") }, + { CA_FANTASIA, 3, "level_fantasia", "FANTASIA", _("Allowed to use fantaisist commands") }, + { CA_FOUNDER, ACCESS_FOUNDER, "level_founder", "FOUNDER", _("Allowed to issue commands restricted to channel founders") }, + { CA_GETKEY, 5, "level_getkey", "GETKEY", _("Allowed to use GETKEY command") }, + { CA_GREET, 5, "level_greet", "GREET", _("Greet message displayed") }, + { CA_HALFOP, 5, "level_halfop", "HALFOP", _("Allowed to (de)halfop users") }, + { CA_HALFOPME, 4, "level_halfopme", "HALFOPME", _("Allowed to (de)halfop him/herself") }, + { CA_INFO, 10000, "level_info", "INFO", _("Allowed to use INFO command with ALL option") }, + { CA_INVITE, 5, "level_invite", "INVITE", _("Allowed to use the INVITE command") }, + { CA_KICK, 4, "level_kick", "KICK", _("Allowed to use the KICK command") }, + { CA_MEMO, 10, "level_memo", "MEMO", _("Allowed to read channel memos") }, + { CA_MODE, 5, "level_mode", "MODE", _("Allowed to change channel modes") }, + { CA_NOKICK, 1, "level_nokick", "NOKICK", _("Never kicked by the bot's kickers") }, + { CA_OPDEOP, 5, "level_opdeop", "OPDEOP", _("Allowed to (de)op users") }, + { CA_OPDEOPME, 5, "level_opdeopme", "OPDEOPME", _("Allowed to (de)op him/herself") }, + { CA_OWNER, ACCESS_FOUNDER, "level_owner", "OWNER", _("Allowed to use (de)owner users") }, + { CA_OWNERME, 10000, "level_ownerme", "OWNERME", _("Allowed to (de)owner him/herself") }, + { CA_PROTECT, 10000, "level_protect", "PROTECT", _("Allowed to (de)protect users") }, + { CA_PROTECTME, 10, "level_protectme", "PROTECTME", _("Allowed to (de)protect him/herself"), }, + { CA_SAY, 5, "level_say", "SAY", _("Allowed to use SAY and ACT commands") }, + { CA_SIGNKICK, ACCESS_FOUNDER, "level_signkick", "SIGNKICK", _("No signed kick when SIGNKICK LEVEL is used") }, + { CA_SET, 10000, "level_set", "SET", _("Allowed to set channel settings") }, + { CA_TOPIC, 5, "level_topic", "TOPIC", _("Allowed to change channel topics") }, + { CA_UNBAN, 4, "level_unban", "UNBAN", _("Allowed to unban users") }, + { CA_VOICE, 4, "level_voice", "VOICE", _("Allowed to (de)voice users") }, + { CA_VOICEME, 3, "level_voiceme", "VOICEME", _("Allowed to (de)voice him/herself") }, + { CA_SIZE, -1, "", "", "" } +}; + +static void reset_levels(ChannelInfo *ci) +{ + for (int i = 0; defaultLevels[i].priv != CA_SIZE; ++i) + ci->levels[defaultLevels[i].priv] = defaultLevels[i].default_level; +} + +class AccessChanAccess : public ChanAccess +{ + public: + int level; + + AccessChanAccess(AccessProvider *p) : ChanAccess(p) + { + } + + bool Matches(User *u, NickCore *nc) + { + if (u && (Anope::Match(u->nick, this->mask) || Anope::Match(u->GetMask(), this->mask))) + return true; + else if (nc && Anope::Match(nc->display, this->mask)) + return true; + return false; + } + + bool HasPriv(ChannelAccess priv) + { + for (int i = 0; defaultLevels[i].priv != CA_SIZE; ++i) + if (defaultLevels[i].priv == priv) + return DetermineLevel(this) >= this->ci->levels[priv]; + return false; + } + + Anope::string Serialize() + { + return stringify(this->level); + } + + void Unserialize(const Anope::string &data) + { + this->level = convertTo(data); + } + + static int DetermineLevel(ChanAccess *access) + { + if (access->provider->name == "access/access") + { + AccessChanAccess *aaccess = debug_cast(access); + return aaccess->level; + } + else + { + int highest = 1; + for (int i = 0; defaultLevels[i].priv != CA_SIZE; ++i) + if (access->ci->levels[defaultLevels[i].priv] > highest && access->HasPriv(defaultLevels[i].priv)) + highest = access->ci->levels[defaultLevels[i].priv]; + return highest; + } + } +}; + +class AccessAccessProvider : public AccessProvider +{ + public: + AccessAccessProvider(Module *o) : AccessProvider(o, "access/access") + { + } + + ChanAccess *Create() + { + return new AccessChanAccess(this); + } +}; + +class AccessListCallback : public NumberList +{ + protected: + CommandSource &source; + ChannelInfo *ci; + bool SentHeader; + public: + AccessListCallback(CommandSource &_source, ChannelInfo *_ci, const Anope::string &numlist) : NumberList(numlist, false), source(_source), ci(_ci), SentHeader(false) + { + } + + ~AccessListCallback() + { + if (SentHeader) + source.Reply(_("End of access list.")); + else + source.Reply(_("No matching entries on %s access list."), ci->name.c_str()); + } + + virtual void HandleNumber(unsigned Number) + { + if (!Number || Number > ci->GetAccessCount()) + return; + + if (!SentHeader) + { + SentHeader = true; + source.Reply(CHAN_ACCESS_LIST_HEADER, ci->name.c_str()); + } + + DoList(source, ci, Number - 1, ci->GetAccess(Number - 1)); + } + + static void DoList(CommandSource &source, ChannelInfo *ci, unsigned Number, ChanAccess *access) + { + source.Reply(_(" %3d %4d %s"), Number + 1, AccessChanAccess::DetermineLevel(access), access->mask.c_str()); + } +}; + +class AccessViewCallback : public AccessListCallback +{ + public: + AccessViewCallback(CommandSource &_source, ChannelInfo *_ci, const Anope::string &numlist) : AccessListCallback(_source, _ci, numlist) + { + } + + void HandleNumber(unsigned Number) + { + if (!Number || Number > ci->GetAccessCount()) + return; + + if (!SentHeader) + { + SentHeader = true; + source.Reply(CHAN_ACCESS_LIST_HEADER, ci->name.c_str()); + } + + DoList(source, ci, Number - 1, ci->GetAccess(Number - 1)); + } + + static void DoList(CommandSource &source, ChannelInfo *ci, unsigned Number, ChanAccess *access) + { + Anope::string timebuf; + if (ci->c) + for (CUserList::const_iterator cit = ci->c->users.begin(), cit_end = ci->c->users.end(); cit != cit_end; ++cit) + if (access->Matches((*cit)->user, (*cit)->user->Account())) + timebuf = "Now"; + if (timebuf.empty()) + { + if (access->last_seen == 0) + timebuf = "Never"; + else + timebuf = do_strftime(access->last_seen); + } + + source.Reply(CHAN_ACCESS_VIEW_AXS_FORMAT, Number + 1, AccessChanAccess::DetermineLevel(access), access->mask.c_str(), access->creator.c_str(), timebuf.c_str()); + } +}; + +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) + { + if (!ci->HasPriv(source.u, CA_ACCESS_CHANGE) && source.u->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.u, c, ci) << "for user" << (Deleted == 1 ? " " : "s ") << 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) + { + if (!Number || Number > ci->GetAccessCount()) + return; + + User *u = source.u; + + ChanAccess *access = ci->GetAccess(Number - 1); + + AccessGroup u_access = ci->AccessFor(u); + ChanAccess *u_highest = u_access.Highest(); + + if (u_highest ? AccessChanAccess::DetermineLevel(u_highest) : 0 <= AccessChanAccess::DetermineLevel(access) && !u->HasPriv("chanserv/access/modify")) + { + Denied = true; + return; + } + + ++Deleted; + if (!Nicks.empty()) + Nicks += ", " + access->mask; + else + Nicks = access->mask; + + FOREACH_MOD(I_OnAccessDel, OnAccessDel(ci, u, access)); + + ci->EraseAccess(Number - 1); + } +}; + +class CommandCSAccess : public Command +{ + void DoAdd(CommandSource &source, ChannelInfo *ci, const std::vector ¶ms) + { + User *u = source.u; + + Anope::string mask = params[2]; + int level = ACCESS_INVALID; + + try + { + level = convertTo(params[3]); + } + catch (const ConvertException &) { } + + if (!level) + { + source.Reply(_("Access level must be non-zero.")); + return; + } + + AccessGroup u_access = ci->AccessFor(u); + ChanAccess *highest = u_access.Highest(); + int u_level = (highest ? AccessChanAccess::DetermineLevel(highest) : 0); + if (!ci->HasPriv(u, CA_FOUNDER) && level >= u_level && !u->HasPriv("chanserv/access/modify")) + { + source.Reply(ACCESS_DENIED); + return; + } + else if (level <= ACCESS_INVALID || level >= ACCESS_FOUNDER) + { + source.Reply(CHAN_ACCESS_LEVEL_RANGE, ACCESS_INVALID + 1, ACCESS_FOUNDER - 1); + return; + } + + bool override = !ci->HasPriv(u, CA_ACCESS_CHANGE) || level >= u_level; + + for (unsigned i = ci->GetAccessCount(); i > 0; --i) + { + ChanAccess *access = ci->GetAccess(i - 1); + if (mask.equals_ci(access->mask)) + { + /* Don't allow lowering from a level >= u_level */ + if (AccessChanAccess::DetermineLevel(access) >= u_level && !u->HasPriv("chanserv/access/modify")) + { + source.Reply(ACCESS_DENIED); + return; + } + ci->EraseAccess(i - 1); + break; + } + } + + if (ci->GetAccessCount() >= Config->CSAccessMax) + { + source.Reply(_("Sorry, you can only have %d access entries on a channel."), Config->CSAccessMax); + return; + } + + if (mask.find_first_of("!*@") == Anope::string::npos && findnick(mask) == NULL) + mask += "!*@*"; + + service_reference provider("access/access"); + if (!provider) + return; + AccessChanAccess *access = debug_cast(provider->Create()); + access->ci = ci; + access->mask = mask; + access->creator = u->nick; + access->level = level; + access->last_seen = 0; + access->created = Anope::CurTime; + ci->AddAccess(access); + + FOREACH_MOD(I_OnAccessAdd, OnAccessAdd(ci, u, access)); + + Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "ADD " << mask << " (level: " << level << ") as level " << u_level; + source.Reply(_("\002%s\002 added to %s access list at level \002%d\002."), access->mask.c_str(), ci->name.c_str(), level); + + return; + } + + void DoDel(CommandSource &source, ChannelInfo *ci, const std::vector ¶ms) + { + User *u = source.u; + + const Anope::string &mask = params[2]; + + 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) + { + AccessDelCallback list(source, ci, this, mask); + list.Process(); + } + else + { + AccessGroup u_access = ci->AccessFor(u); + ChanAccess *highest = u_access.Highest(); + int u_level = (highest ? AccessChanAccess::DetermineLevel(highest) : 0); + + for (unsigned i = ci->GetAccessCount(); i > 0; --i) + { + ChanAccess *access = ci->GetAccess(i - 1); + if (mask.equals_ci(access->mask)) + { + int access_level = AccessChanAccess::DetermineLevel(access); + if (!access->mask.equals_ci(u->Account()->display) && u_level <= access_level && !u->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 = !ci->HasPriv(u, CA_ACCESS_CHANGE) && !access->mask.equals_ci(u->Account()->display); + Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "DEL " << access->mask; + + FOREACH_MOD(I_OnAccessDel, OnAccessDel(ci, u, access)); + ci->EraseAccess(access); + } + return; + } + } + + source.Reply(_("\002%s\002 not found on %s access list."), mask.c_str(), ci->name.c_str()); + } + + return; + } + + void DoList(CommandSource &source, ChannelInfo *ci, const std::vector ¶ms) + { + 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) + { + AccessListCallback list(source, ci, nick); + list.Process(); + } + else + { + bool SentHeader = false; + + for (unsigned i = 0, end = ci->GetAccessCount(); i < end; ++i) + { + ChanAccess *access = ci->GetAccess(i); + + if (!nick.empty() && !Anope::Match(access->mask, nick)) + continue; + + if (!SentHeader) + { + SentHeader = true; + source.Reply(CHAN_ACCESS_LIST_HEADER, ci->name.c_str()); + } + + AccessListCallback::DoList(source, ci, i, access); + } + + if (SentHeader) + source.Reply(_("End of access list.")); + else + source.Reply(_("No matching entries on %s access list."), ci->name.c_str()); + } + + return; + } + + void DoView(CommandSource &source, ChannelInfo *ci, const std::vector ¶ms) + { + 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) + { + AccessViewCallback list(source, ci, nick); + list.Process(); + } + else + { + bool SentHeader = false; + + for (unsigned i = 0, end = ci->GetAccessCount(); i < end; ++i) + { + ChanAccess *access = ci->GetAccess(i); + + if (!nick.empty() && !Anope::Match(access->mask, nick)) + continue; + + if (!SentHeader) + { + SentHeader = true; + source.Reply(CHAN_ACCESS_LIST_HEADER, ci->name.c_str()); + } + + AccessViewCallback::DoList(source, ci, i, access); + } + + if (SentHeader) + source.Reply(_("End of access list.")); + else + source.Reply(_("No matching entries on %s access list."), ci->name.c_str()); + } + + return; + } + + void DoClear(CommandSource &source, ChannelInfo *ci) + { + User *u = source.u; + + if (!IsFounder(u, ci) && !u->HasPriv("chanserv/access/modify")) + source.Reply(ACCESS_DENIED); + else + { + ci->ClearAccess(); + + FOREACH_MOD(I_OnAccessClear, OnAccessClear(ci, u)); + + source.Reply(_("Channel %s access list has been cleared."), ci->name.c_str()); + + bool override = !IsFounder(u, ci); + Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "CLEAR"; + } + + 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\002")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + const Anope::string &cmd = params[1]; + const Anope::string &nick = params.size() > 2 ? params[2] : ""; + const Anope::string &s = params.size() > 3 ? params[3] : ""; + + User *u = source.u; + ChannelInfo *ci = cs_findchan(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 (u->HasPriv("chanserv/access/modify")) + has_access = true; + else if (is_list && ci->HasPriv(u, CA_ACCESS_LIST)) + has_access = true; + else if (ci->HasPriv(u, CA_ACCESS_CHANGE)) + has_access = true; + else if (is_del) + { + NickAlias *na = findnick(nick); + if (na && na->nc == u->Account()) + 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 (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) + { + 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 nick not on the access list has\n" + "a user level of 0."), source.owner->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" + "must be less than that of the user giving the command, and\n" + "if the \037mask\037 is already on the access list, the current\n" + "access level of that nick must be less than the access level\n" + "of the user giving the command. When a user joins the channel\n" + "the access they receive is from the highest level entry in the\n" + "access list.")); + 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(_("\002User access levels\002\n" + " \n" + "By default, the following access levels are defined:\n" + " \n" + " \002Founder\002 Full access to %s functions; automatic\n" + " opping upon entering channel. Note\n" + " that only one person may have founder\n" + " status (it cannot be given using the\n" + " \002ACCESS\002 command).\n" + " \002 10\002 Access to AKICK command; automatic opping.\n" + " \002 5\002 Automatic opping.\n" + " \002 3\002 Automatic voicing.\n" + " \002 0\002 No special privileges; can be opped by other\n" + " ops (unless \002secure-ops\002 is set).\n" + " \002 <0\002 May not be opped.\n" + " \n" + "These levels may be changed, or new ones added, using the\n" + "\002LEVELS\002 command; type \002%s%s HELP LEVELS\002 for\n" + "information."), source.owner->nick.c_str(), Config->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str()); + return true; + } +}; + +class CommandCSLevels : public Command +{ + int levelinfo_maxwidth; + + void DoSet(CommandSource &source, ChannelInfo *ci, const std::vector ¶ms) + { + User *u = source.u; + + 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(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 + { + for (int i = 0; defaultLevels[i].priv != CA_SIZE; ++i) + { + AccessLevels &l = defaultLevels[i]; + + if (what.equals_ci(l.name)) + { + ci->levels[l.priv] = level; + FOREACH_MOD(I_OnLevelChange, OnLevelChange(u, ci, i, level)); + + bool override = !ci->HasPriv(u, CA_FOUNDER); + Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "SET " << l.name << " to " << level; + + if (level == ACCESS_FOUNDER) + source.Reply(_("Level for %s on channel %s changed to founder only."), l.name.c_str(), ci->name.c_str()); + else + source.Reply(_("Level for \002%s\002 on channel %s changed to \002%d\002."), l.name.c_str(), ci->name.c_str(), level); + 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->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str()); + } + + return; + } + + void DoDisable(CommandSource &source, ChannelInfo *ci, const std::vector ¶ms) + { + User *u = source.u; + + const Anope::string &what = params[2]; + + /* Don't allow disabling of the founder level. It would be hard to change it back if you dont have access to use this command */ + if (!what.equals_ci("FOUNDER")) + for (int i = 0; defaultLevels[i].priv != CA_SIZE; ++i) + { + AccessLevels &l = defaultLevels[i]; + + if (what.equals_ci(l.name)) + { + ci->levels[l.priv] = ACCESS_INVALID; + FOREACH_MOD(I_OnLevelChange, OnLevelChange(u, ci, i, l.priv)); + + bool override = !ci->HasPriv(u, CA_FOUNDER); + Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "DISABLE " << l.name; + + source.Reply(_("\002%s\002 disabled on channel %s."), l.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->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str()); + + return; + } + + void DoList(CommandSource &source, ChannelInfo *ci) + { + source.Reply(_("Access level settings for channel %s:"), ci->name.c_str()); + + if (!levelinfo_maxwidth) + for (int i = 0; defaultLevels[i].priv != CA_SIZE; ++i) + { + AccessLevels &l = defaultLevels[i]; + + int len = l.name.length(); + if (len > levelinfo_maxwidth) + levelinfo_maxwidth = len; + } + + for (int i = 0; defaultLevels[i].priv != CA_SIZE; ++i) + { + AccessLevels &l = defaultLevels[i]; + int j = ci->levels[l.priv]; + + if (j == ACCESS_INVALID) + source.Reply(_(" %-*s (disabled)"), levelinfo_maxwidth, l.name.c_str()); + else if (j == ACCESS_FOUNDER) + source.Reply(_(" %-*s (founder only)"), levelinfo_maxwidth, l.name.c_str()); + else + source.Reply(_(" %-*s %d"), levelinfo_maxwidth, l.name.c_str(), j); + } + + return; + } + + void DoReset(CommandSource &source, ChannelInfo *ci) + { + User *u = source.u; + + reset_levels(ci); + FOREACH_MOD(I_OnLevelChange, OnLevelChange(u, ci, -1, 0)); + + bool override = !ci->HasPriv(u, CA_FOUNDER); + Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "RESET"; + + 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), levelinfo_maxwidth(0) + { + 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 ¶ms) + { + const Anope::string &cmd = params[1]; + const Anope::string &what = params.size() > 2 ? params[2] : ""; + const Anope::string &s = params.size() > 3 ? params[3] : ""; + + User *u = source.u; + + ChannelInfo *ci = cs_findchan(params[0]); + if (ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); + return; + } + + /* 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 (!ci->HasPriv(u, CA_FOUNDER) && !u->HasPriv("chanserv/access/modify")) + source.Reply(ACCESS_DENIED); + 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) + { + if (subcommand.equals_ci("DESC")) + { + source.Reply(_("The following feature/function names are understood.")); + if (!levelinfo_maxwidth) + for (int i = 0; defaultLevels[i].priv != CA_SIZE; ++i) + { + AccessLevels &l = defaultLevels[i]; + + int len = l.name.length(); + if (len > levelinfo_maxwidth) + levelinfo_maxwidth = len; + } + for (int i = 0; defaultLevels[i].priv != CA_SIZE; ++i) + { + AccessLevels &l = defaultLevels[i]; + source.Reply(_(" %-*s %s"), levelinfo_maxwidth, l.name.c_str(), translate(source.u, l.desc.c_str())); + } + } + 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).\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 (see\n" + "\002HELP ACCESS LEVELS\002).\n" + " \n" + "For a list of the features and functions whose levels can be\n" + "set, see \002HELP LEVELS DESC\002."), source.owner->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, CORE), + accessprovider(this), commandcsaccess(this), commandcslevels(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&accessprovider); + ModuleManager::RegisterService(&commandcsaccess); + ModuleManager::RegisterService(&commandcslevels); + + Implementation i[] = { I_OnReload, I_OnChanRegistered }; + ModuleManager::Attach(i, this, 2); + + this->OnReload(); + } + + void OnReload() + { + ConfigReader config; + + for (int i = 0; defaultLevels[i].priv != CA_SIZE; ++i) + { + AccessLevels &l = defaultLevels[i]; + + const Anope::string &value = config.ReadValue("chanserv", l.config_name, "", 0); + if (value.equals_ci("founder")) + l.default_level = ACCESS_FOUNDER; + else + l.default_level = config.ReadInteger("chanserv", l.config_name, 0, false); + } + } + + void OnChanRegistered(ChannelInfo *ci) + { + reset_levels(ci); + } +}; + +MODULE_INIT(CSAccess) diff --git a/modules/commands/cs_akick.cpp b/modules/commands/cs_akick.cpp new file mode 100644 index 000000000..437bf5572 --- /dev/null +++ b/modules/commands/cs_akick.cpp @@ -0,0 +1,564 @@ +/* ChanServ core functions + * + * (C) 2003-2011 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 void split_usermask(const Anope::string &mask, Anope::string &nick, Anope::string &user, Anope::string &host) +{ + size_t ex = mask.find('!'), at = mask.find('@', ex == Anope::string::npos ? 0 : ex + 1); + if (ex == Anope::string::npos) + { + if (at == Anope::string::npos) + { + nick = mask; + user = host = "*"; + } + else + { + nick = "*"; + user = mask.substr(0, at); + host = mask.substr(at + 1); + } + } + else + { + nick = mask.substr(0, ex); + if (at == Anope::string::npos) + { + user = mask.substr(ex + 1); + host = "*"; + } + else + { + user = mask.substr(ex + 1, at - ex - 1); + host = mask.substr(at + 1); + } + } +} + +class AkickListCallback : public NumberList +{ + protected: + CommandSource &source; + ChannelInfo *ci; + bool SentHeader; + public: + AkickListCallback(CommandSource &_source, ChannelInfo *_ci, const Anope::string &numlist) : NumberList(numlist, false), source(_source), ci(_ci), SentHeader(false) + { + } + + ~AkickListCallback() + { + if (!SentHeader) + source.Reply(_("No matching entries on %s autokick list."), ci->name.c_str()); + } + + virtual void HandleNumber(unsigned Number) + { + if (!Number || Number > ci->GetAkickCount()) + return; + + if (!SentHeader) + { + SentHeader = true; + source.Reply(_("Autokick list for %s:"), ci->name.c_str()); + } + + DoList(source, ci, Number - 1, ci->GetAkick(Number - 1)); + } + + static void DoList(CommandSource &source, ChannelInfo *ci, unsigned index, AutoKick *akick) + { + source.Reply(_(" %3d %s (%s)"), index + 1, akick->HasFlag(AK_ISNICK) ? akick->nc->display.c_str() : akick->mask.c_str(), !akick->reason.empty() ? akick->reason.c_str() : NO_REASON); + } +}; + +class AkickViewCallback : public AkickListCallback +{ + public: + AkickViewCallback(CommandSource &_source, ChannelInfo *_ci, const Anope::string &numlist) : AkickListCallback(_source, _ci, numlist) + { + } + + void HandleNumber(unsigned Number) + { + if (!Number || Number > ci->GetAkickCount()) + return; + + if (!SentHeader) + { + SentHeader = true; + source.Reply(_("Autokick list for %s:"), ci->name.c_str()); + } + + DoList(source, ci, Number - 1, ci->GetAkick(Number - 1)); + } + + static void DoList(CommandSource &source, ChannelInfo *ci, unsigned index, AutoKick *akick) + { + Anope::string timebuf; + + if (akick->addtime) + timebuf = do_strftime(akick->addtime); + else + timebuf = UNKNOWN; + + source.Reply(CHAN_AKICK_VIEW_FORMAT, index + 1, akick->HasFlag(AK_ISNICK) ? akick->nc->display.c_str() : akick->mask.c_str(), !akick->creator.empty() ? akick->creator.c_str() : UNKNOWN, timebuf.c_str(), !akick->reason.empty() ? akick->reason.c_str() : _(NO_REASON)); + + if (akick->last_used) + source.Reply(_(" Last used %s"), do_strftime(akick->last_used).c_str()); + } +}; + +class AkickDelCallback : public NumberList +{ + CommandSource &source; + ChannelInfo *ci; + Command *c; + unsigned Deleted; + public: + AkickDelCallback(CommandSource &_source, ChannelInfo *_ci, Command *_c, const Anope::string &list) : NumberList(list, true), source(_source), ci(_ci), c(_c), Deleted(0) + { + } + + ~AkickDelCallback() + { + User *u = source.u; + bool override = !ci->HasPriv(u, CA_AKICK); + Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, c, ci) << "DEL on " << Deleted << " users"; + + 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) + { + if (!Number || Number > ci->GetAkickCount()) + return; + + ++Deleted; + ci->EraseAkick(Number - 1); + } +}; + +class CommandCSAKick : public Command +{ + void DoAdd(CommandSource &source, ChannelInfo *ci, const std::vector ¶ms) + { + User *u = source.u; + + Anope::string mask = params[2]; + Anope::string reason = params.size() > 3 ? params[3] : ""; + NickAlias *na = findnick(mask); + NickCore *nc = NULL; + AutoKick *akick; + + if (!na) + { + Anope::string nick, user, host; + + split_usermask(mask, nick, user, host); + mask = nick + "!" + user + "@" + host; + } + else + nc = na->nc; + + /* Check excepts BEFORE we get this far */ + if (ci->c) + { + std::pair modes = ci->c->GetModeList(CMODE_EXCEPT); + for (; modes.first != modes.second; ++modes.first) + { + if (Anope::Match(modes.first->second, mask)) + { + source.Reply(CHAN_EXCEPTED, mask.c_str(), ci->name.c_str()); + return; + } + } + } + + /* Check whether target nick has equal/higher access + * or whether the mask matches a user with higher/equal access - Viper */ + if (ci->HasFlag(CI_PEACE) && nc) + { + AccessGroup nc_access = ci->AccessFor(nc), u_access = ci->AccessFor(u); + if (nc == ci->GetFounder() || nc_access >= u_access) + { + source.Reply(ACCESS_DENIED); + return; + } + } + else if (ci->HasFlag(CI_PEACE)) + { + /* Match against all currently online users with equal or + * higher access. - Viper */ + for (Anope::insensitive_map::iterator it = UserListByNick.begin(), it_end = UserListByNick.end(); it != it_end; ++it) + { + User *u2 = it->second; + + AccessGroup nc_access = ci->AccessFor(nc), u_access = ci->AccessFor(u); + Entry entry_mask(CMODE_BEGIN, mask); + + if ((ci->HasPriv(u2, CA_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 = ci->AccessFor(u); + 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->HasFlag(AK_ISNICK) ? akick->nc == nc : mask.equals_ci(akick->mask)) + { + source.Reply(_("\002%s\002 already exists on %s autokick list."), akick->HasFlag(AK_ISNICK) ? akick->nc->display.c_str() : akick->mask.c_str(), ci->name.c_str()); + return; + } + } + + if (ci->GetAkickCount() >= Config->CSAutokickMax) + { + source.Reply(_("Sorry, you can only have %d autokick masks on a channel."), Config->CSAutokickMax); + return; + } + + if (nc) + akick = ci->AddAkick(u->nick, nc, reason); + else + akick = ci->AddAkick(u->nick, mask, reason); + + bool override = !ci->HasPriv(u, CA_AKICK); + Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "ADD " << mask << ": " << reason; + + FOREACH_MOD(I_OnAkickAdd, OnAkickAdd(u, 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 ¶ms) + { + User *u = source.u; + + const Anope::string &mask = params[2]; + AutoKick *akick; + 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) + { + AkickDelCallback list(source, ci, this, mask); + list.Process(); + } + else + { + NickAlias *na = findnick(mask); + NickCore *nc = na ? na->nc : NULL; + + for (i = 0, end = ci->GetAkickCount(); i < end; ++i) + { + akick = ci->GetAkick(i); + + if ((akick->HasFlag(AK_ISNICK) && akick->nc == nc) || (!akick->HasFlag(AK_ISNICK) && 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 = !ci->HasPriv(u, CA_AKICK); + Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "DEL " << mask; + + ci->EraseAkick(i); + + source.Reply(_("\002%s\002 deleted from %s autokick list."), mask.c_str(), ci->name.c_str()); + } + } + + void DoList(CommandSource &source, ChannelInfo *ci, const std::vector ¶ms) + { + User *u = source.u; + + const Anope::string &mask = params.size() > 2 ? params[2] : ""; + + bool override = !ci->HasPriv(u, CA_AKICK); + Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "LIST"; + + if (!ci->GetAkickCount()) + { + source.Reply(_("%s autokick list is empty."), ci->name.c_str()); + return; + } + + if (!mask.empty() && isdigit(mask[0]) && mask.find_first_not_of("1234567890,-") == Anope::string::npos) + { + AkickListCallback list(source, ci, mask); + list.Process(); + } + else + { + bool SentHeader = false; + + for (unsigned i = 0, end = ci->GetAkickCount(); i < end; ++i) + { + AutoKick *akick = ci->GetAkick(i); + + if (!mask.empty()) + { + if (!akick->HasFlag(AK_ISNICK) && !Anope::Match(akick->mask, mask)) + continue; + if (akick->HasFlag(AK_ISNICK) && !Anope::Match(akick->nc->display, mask)) + continue; + } + + if (!SentHeader) + { + SentHeader = true; + source.Reply(_("Autokick list for %s:"), ci->name.c_str()); + } + + AkickListCallback::DoList(source, ci, i, akick); + } + + if (!SentHeader) + source.Reply(_("No matching entries on %s autokick list."), ci->name.c_str()); + } + } + + void DoView(CommandSource &source, ChannelInfo *ci, const std::vector ¶ms) + { + User *u = source.u; + + const Anope::string &mask = params.size() > 2 ? params[2] : ""; + + bool override = !ci->HasPriv(u, CA_AKICK); + Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "VIEW"; + + if (!ci->GetAkickCount()) + { + source.Reply(_("%s autokick list is empty."), ci->name.c_str()); + return; + } + + if (!mask.empty() && isdigit(mask[0]) && mask.find_first_not_of("1234567890,-") == Anope::string::npos) + { + AkickViewCallback list(source, ci, mask); + list.Process(); + } + else + { + bool SentHeader = false; + + for (unsigned i = 0, end = ci->GetAkickCount(); i < end; ++i) + { + AutoKick *akick = ci->GetAkick(i); + + if (!mask.empty()) + { + if (!akick->HasFlag(AK_ISNICK) && !Anope::Match(akick->mask, mask)) + continue; + if (akick->HasFlag(AK_ISNICK) && !Anope::Match(akick->nc->display, mask)) + continue; + } + + if (!SentHeader) + { + SentHeader = true; + source.Reply(_("Autokick list for %s:"), ci->name.c_str()); + } + + AkickViewCallback::DoList(source, ci, i, akick); + } + + if (!SentHeader) + source.Reply(_("No matching entries on %s autokick list."), ci->name.c_str()); + } + } + + void DoEnforce(CommandSource &source, ChannelInfo *ci) + { + User *u = source.u; + Channel *c = ci->c; + int count = 0; + + if (!c) + { + source.Reply(CHAN_X_NOT_IN_USE, ci->name.c_str()); + return; + } + + for (CUserList::iterator it = c->users.begin(), it_end = c->users.end(); it != it_end; ) + { + UserContainer *uc = *it++; + + if (ci->CheckKick(uc->user)) + ++count; + } + + bool override = !ci->HasPriv(u, CA_AKICK); + Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, 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) + { + User *u = source.u; + bool override = !ci->HasPriv(u, CA_AKICK); + Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "CLEAR"; + + 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(_("\002AKICK \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 ¶ms) + { + Anope::string chan = params[0]; + Anope::string cmd = params[1]; + Anope::string mask = params.size() > 2 ? params[2] : ""; + + User *u = source.u; + + ChannelInfo *ci = cs_findchan(params[0]); + if (ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); + return; + } + + if (mask.empty() && (cmd.equals_ci("ADD") || cmd.equals_ci("DEL"))) + this->OnSyntaxError(source, cmd); + else if (!ci->HasPriv(u, CA_AKICK) && !u->HasPriv("chanserv/access/modify")) + source.Reply(ACCESS_DENIED); + else if (!cmd.equals_ci("LIST") && !cmd.equals_ci("VIEW") && !cmd.equals_ci("ENFORCE") && 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) + { + 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 \"You have been\n" + "banned from the channel\".\n" + "When akicking a \037registered nick\037 the nickserv 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.owner->nick.c_str()); + 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\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.owner->nick.c_str()); + return true; + } +}; + +class CSAKick : public Module +{ + CommandCSAKick commandcsakick; + + public: + CSAKick(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandcsakick(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandcsakick); + } +}; + +MODULE_INIT(CSAKick) diff --git a/modules/commands/cs_appendtopic.cpp b/modules/commands/cs_appendtopic.cpp new file mode 100644 index 000000000..c9af50613 --- /dev/null +++ b/modules/commands/cs_appendtopic.cpp @@ -0,0 +1,117 @@ +/* cs_appendtopic.c - Add text to a channels topic + * + * (C) 2003-2011 Anope Team + * Contact us at team@anope.org + * + * Based on the original module by SGR + * Included in the Anope module pack since Anope 1.7.9 + * Anope Coder: GeniusDex + * + * 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" + +/* ------------------------------------------------------------ + * Name: cs_appendtopic + * Author: SGR + * Date: 31/08/2003 + * ------------------------------------------------------------ + * + * This module has no configurable options. For information on + * this module, load it and refer to /ChanServ APPENDTOPIC HELP + * + * Thanks to dengel, Rob and Certus for all there support. + * Especially Rob, who always manages to show me where I have + * not allocated any memory. Even if it takes a few weeks of + * pestering to get him to look at it. + * + * ------------------------------------------------------------ + */ + +/* ---------------------------------------------------------------------- */ +/* DO NOT EDIT BELOW THIS LINE UNLESS YOU KNOW WHAT YOU ARE DOING */ +/* ---------------------------------------------------------------------- */ + +class CommandCSAppendTopic : public Command +{ + public: + CommandCSAppendTopic(Module *creator) : Command(creator, "chanserv/appendtopic", 2, 2) + { + this->SetDesc(_("Add text to a channels topic")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + const Anope::string &newtopic = params[1]; + + User *u = source.u; + Channel *c = findchan(params[0]);; + + if (!c) + source.Reply(CHAN_X_NOT_IN_USE, params[0].c_str()); + else if (!c->ci) + source.Reply(CHAN_X_NOT_REGISTERED, c->name.c_str()); + else if (!c->ci->HasPriv(u, CA_TOPIC)) + source.Reply(ACCESS_DENIED); + else + { + Anope::string topic; + if (!c->ci->last_topic.empty()) + { + topic = c->ci->last_topic + " " + newtopic; + c->ci->last_topic.clear(); + } + else + topic = newtopic; + + bool has_topiclock = c->ci->HasFlag(CI_TOPICLOCK); + c->ci->UnsetFlag(CI_TOPICLOCK); + c->ChangeTopic(u->nick, topic, Anope::CurTime); + if (has_topiclock) + c->ci->SetFlag(CI_TOPICLOCK); + + bool override = c->ci->HasPriv(u, CA_TOPIC); + Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, c->ci) << "changed topic to " << topic; + } + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + source.Reply(_("Syntax: APPENDTOPIC channel text")); + source.Reply(" "); + source.Reply(("This command allows users to append text to a currently set\n" + "channel topic. When TOPICLOCK is on, the topic is updated and\n" + "the new, updated topic is locked.")); + + return true; + } + + void OnSyntaxError(CommandSource &source, const Anope::string &subcommand) + { + source.Reply(_("Syntax: APPENDTOPIC channel text")); + } +}; + +class CSAppendTopic : public Module +{ + CommandCSAppendTopic commandcsappendtopic; + + public: + CSAppendTopic(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, SUPPORTED), + commandcsappendtopic(this) + { + this->SetAuthor("SGR"); + + ModuleManager::RegisterService(&commandcsappendtopic); + } +}; + +MODULE_INIT(CSAppendTopic) diff --git a/modules/commands/cs_ban.cpp b/modules/commands/cs_ban.cpp new file mode 100644 index 000000000..759649662 --- /dev/null +++ b/modules/commands/cs_ban.cpp @@ -0,0 +1,109 @@ +/* ChanServ core functions + * + * (C) 2003-2011 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 CommandCSBan : public Command +{ + public: + CommandCSBan(Module *creator) : Command(creator, "chanserv/ban", 2, 3) + { + this->SetDesc(_("Bans a selected nick on a channel")); + this->SetSyntax(_("\037#channel\037 \037nick\037 [\037reason\037]")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + const Anope::string &chan = params[0]; + const Anope::string &target = params[1]; + const Anope::string &reason = params.size() > 2 ? params[2] : "Requested"; + + ChannelInfo *ci = cs_findchan(params[0]); + if (ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); + return; + } + + User *u = source.u; + Channel *c = ci->c; + bool is_same = target.equals_ci(u->nick); + User *u2 = is_same ? u : finduser(target); + + AccessGroup u_access = ci->AccessFor(u), u2_access = ci->AccessFor(u2); + + if (!c) + source.Reply(CHAN_X_NOT_IN_USE, chan.c_str()); + else if (!u2) + source.Reply(NICK_X_NOT_IN_USE, target.c_str()); + else if (!ci->HasPriv(u, CA_BAN)) + source.Reply(ACCESS_DENIED); + else if (!is_same && ci->HasFlag(CI_PEACE) && u2_access >= u_access) + source.Reply(ACCESS_DENIED); + /* + * Dont ban/kick the user on channels where he is excepted + * to prevent services <-> server wars. + */ + else if (matches_list(ci->c, u2, CMODE_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; + get_idealban(ci, u2, mask); + + // XXX need a way to detect if someone is overriding + Log(LOG_COMMAND, u, this, ci) << "for " << mask; + + c->SetMode(NULL, CMODE_BAN, mask); + + /* We still allow host banning while not allowing to kick */ + if (!c->FindUser(u2)) + return; + + if (ci->HasFlag(CI_SIGNKICK) || (ci->HasFlag(CI_SIGNKICK_LEVEL) && !ci->HasPriv(u, CA_SIGNKICK))) + c->Kick(ci->WhoSends(), u2, "%s (%s)", reason.c_str(), u->nick.c_str()); + else + c->Kick(ci->WhoSends(), u2, "%s", reason.c_str()); + } + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Bans a selected nick on a channel.\n" + " \n" + "By default, limited to AOPs or those with level 5 access \n" + "and above on the channel.")); + return true; + } +}; + +class CSBan : public Module +{ + CommandCSBan commandcsban; + + public: + CSBan(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), commandcsban(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandcsban); + } +}; + +MODULE_INIT(CSBan) diff --git a/modules/commands/cs_clearusers.cpp b/modules/commands/cs_clearusers.cpp new file mode 100644 index 000000000..18dc75479 --- /dev/null +++ b/modules/commands/cs_clearusers.cpp @@ -0,0 +1,83 @@ +/* ChanServ core functions + * + * (C) 2003-2011 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 CommandCSClearUsers : public Command +{ + public: + CommandCSClearUsers(Module *creator) : Command(creator, "chanserv/clearusers", 1, 1) + { + this->SetDesc(_("Kicks all users on a channel")); + this->SetSyntax(_("\037channel\037")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + const Anope::string &chan = params[0]; + + User *u = source.u; + Channel *c = findchan(chan); + Anope::string modebuf; + + if (!c) + source.Reply(CHAN_X_NOT_IN_USE, chan.c_str()); + else if (!c->ci) + source.Reply(CHAN_X_NOT_REGISTERED, c->name.c_str()); + else if (!c->ci->HasPriv(u, CA_FOUNDER)) + { + source.Reply(ACCESS_DENIED); + return; + } + + Anope::string buf = "CLEARUSERS command from " + u->nick + " (" + u->Account()->display + ")"; + + for (CUserList::iterator it = c->users.begin(), it_end = c->users.end(); it != it_end; ) + { + UserContainer *uc = *it++; + + c->Kick(NULL, uc->user, "%s", buf.c_str()); + } + + source.Reply(_("All users have been kicked from \2%s\2."), chan.c_str()); + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Tells %s to clear (kick) all users certain settings on a channel." + " \n" + "By default, limited to those with founder access on the\n" + "channel."), source.owner->nick.c_str()); + return true; + } +}; + +class CSClearUsers : public Module +{ + CommandCSClearUsers commandcsclearusers; + + public: + CSClearUsers(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandcsclearusers(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandcsclearusers); + } +}; + +MODULE_INIT(CSClearUsers) diff --git a/modules/commands/cs_clone.cpp b/modules/commands/cs_clone.cpp new file mode 100644 index 000000000..5ede6a9e4 --- /dev/null +++ b/modules/commands/cs_clone.cpp @@ -0,0 +1,186 @@ +/* ChanServ core functions + * + * (C) 2003-2011 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 CommandCSClone : public Command +{ +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")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + const Anope::string &channel = params[0]; + const Anope::string &target = params[1]; + Anope::string what = params.size() > 2 ? params[2] : ""; + + User *u = source.u; + ChannelInfo *ci = cs_findchan(params[0]); + if (ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); + return; + } + + if (!ci->HasPriv(u, CA_SET)) + { + source.Reply(ACCESS_DENIED); + return; + } + ChannelInfo *target_ci = cs_findchan(target); + if (!target_ci) + { + source.Reply(CHAN_X_NOT_REGISTERED, target.c_str()); + return; + } + if (!IsFounder(u, ci) || !IsFounder(u, target_ci)) + { + source.Reply(ACCESS_DENIED); + return; + } + + if (Config->CSMaxReg && u->Account()->channelcount >= Config->CSMaxReg && !u->HasPriv("chanserv/no-register-limit")) + { + source.Reply(u->Account()->channelcount > Config->CSMaxReg ? CHAN_EXCEEDED_CHANNEL_LIMIT : _(CHAN_REACHED_CHANNEL_LIMIT), Config->CSMaxReg); + return; + } + + if (what.equals_ci("ALL")) + what.clear(); + + if (what.empty()) + { + delete target_ci; + target_ci = new ChannelInfo(*ci); + target_ci->name = target; + RegisteredChannelList[target_ci->name] = target_ci; + target_ci->c = findchan(target_ci->name); + if (target_ci->c) + { + target_ci->c->ci = target_ci; + + check_modes(target_ci->c); + + ChannelMode *cm; + if (u->FindChannel(target_ci->c) != NULL) + { + /* On most ircds you do not receive the admin/owner mode till its registered */ + if ((cm = ModeManager::FindChannelModeByName(CMODE_OWNER))) + target_ci->c->SetMode(NULL, cm, u->nick); + else if ((cm = ModeManager::FindChannelModeByName(CMODE_PROTECT))) + target_ci->c->RemoveMode(NULL, cm, u->nick); + } + + /* Mark the channel as persistant */ + if (target_ci->c->HasMode(CMODE_PERM)) + target_ci->SetFlag(CI_PERSIST); + /* Persist may be in def cflags, set it here */ + else if (target_ci->HasFlag(CI_PERSIST) && (cm = ModeManager::FindChannelModeByName(CMODE_PERM))) + target_ci->c->SetMode(NULL, CMODE_PERM); + + if (target_ci->bi && target_ci->c->FindUser(target_ci->bi) == NULL) + target_ci->bi->Join(target_ci->c); + } + + 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.owner->nick; + + FOREACH_MOD(I_OnChanRegistered, OnChanRegistered(target_ci)); + + source.Reply(_("All settings from \002%s\002 have been transferred to \002%s\002"), channel.c_str(), target.c_str()); + } + else if (what.equals_ci("ACCESS")) + { + /*target_ci->ClearAccess(); + for (unsigned i = 0; i < ci->GetAccessCount(); ++i) + { + ChanAccess *access = ci->GetAccess(i); + target_ci->AddAccess(access->GetMask(), access->level, access->creator, access->last_seen); + } + + source.Reply(_("All access entries from \002%s\002 have been transferred to \002%s\002"), channel.c_str(), target.c_str()); + XXX + */ + } + else if (what.equals_ci("AKICK")) + { + target_ci->ClearAkick(); + for (unsigned i = 0; i < ci->GetAkickCount(); ++i) + { + AutoKick *akick = ci->GetAkick(i); + if (akick->HasFlag(AK_ISNICK)) + 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 transferred to \002%s\002"), channel.c_str(), target.c_str()); + } + else if (what.equals_ci("BADWORDS")) + { + target_ci->ClearBadWords(); + for (unsigned i = 0; i < ci->GetBadWordCount(); ++i) + { + BadWord *bw = ci->GetBadWord(i); + target_ci->AddBadWord(bw->word, bw->type); + } + + source.Reply(_("All badword entries from \002%s\002 have been transferred to \002%s\002"), channel.c_str(), target.c_str()); + } + else + { + this->OnSyntaxError(source, ""); + return; + } + + Log(LOG_COMMAND, u, this, ci) << "to clone it to " << target_ci->name; + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Copies all settings, access, akicks, etc from channel to the\n" + "target channel. If access, akick, or badwords is specified then only\n" + "the respective settings are transferred. You must have founder level\n" + "access to \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, CORE), commandcsclone(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandcsclone); + } +}; + +MODULE_INIT(CSClone) diff --git a/modules/commands/cs_drop.cpp b/modules/commands/cs_drop.cpp new file mode 100644 index 000000000..eb01875db --- /dev/null +++ b/modules/commands/cs_drop.cpp @@ -0,0 +1,102 @@ +/* ChanServ core functions + * + * (C) 2003-2011 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, 1) + { + this->SetDesc(_("Cancel the registration of a channel")); + this->SetSyntax(_("\037channel\037")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + const Anope::string &chan = params[0]; + + User *u = source.u; + + if (readonly) + { + source.Reply(_("Sorry, channel de-registration is temporarily disabled.")); // XXX: READ_ONLY_MODE? + return; + } + + ChannelInfo *ci = cs_findchan(params[0]); + if (ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); + return; + } + + ci = cs_findchan(chan); + + if (ci->HasFlag(CI_SUSPENDED) && !u->HasCommand("chanserv/chanserv/drop")) + { + source.Reply(CHAN_X_SUSPENDED, chan.c_str()); + return; + } + + if ((ci->HasFlag(CI_SECUREFOUNDER) ? !IsFounder(u, ci) : !ci->HasPriv(u, CA_FOUNDER)) && !u->HasCommand("chanserv/chanserv/drop")) + { + source.Reply(ACCESS_DENIED); + return; + } + + if (ci->c && ModeManager::FindChannelModeByName(CMODE_REGISTERED)) + ci->c->RemoveMode(NULL, CMODE_REGISTERED, "", false); + + bool override = (ci->HasFlag(CI_SECUREFOUNDER) ? !IsFounder(u, ci) : !ci->HasPriv(u, CA_FOUNDER)); + Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "founder: " << (ci->GetFounder() ? ci->GetFounder()->display : "none"); + + delete ci; + + source.Reply(_("Channel \002%s\002 has been dropped."), chan.c_str()); + + FOREACH_MOD(I_OnChanDrop, OnChanDrop(chan)); + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + User *u = source.u; + this->SendSyntax(source); + source.Reply(" "); + if (u->IsServicesOper()) + source.Reply(_("Unregisters the named channel. Only \002Services Operators\002\n" + "can drop a channel for which they have not identified.")); + else + source.Reply(_("Unregisters the named channel. Can only be used by\n" + "\002channel founder\002.")); + + return true; + } +}; + +class CSDrop : public Module +{ + CommandCSDrop commandcsdrop; + + public: + CSDrop(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), commandcsdrop(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandcsdrop); + } +}; + +MODULE_INIT(CSDrop) diff --git a/modules/commands/cs_enforce.cpp b/modules/commands/cs_enforce.cpp new file mode 100644 index 000000000..26885c9b0 --- /dev/null +++ b/modules/commands/cs_enforce.cpp @@ -0,0 +1,215 @@ +/* cs_enforce - Add a /cs ENFORCE command to enforce various set + * options and channelmodes on a channel. + * + * (C) 2003-2011 Anope Team + * Contact us at team@anope.org + * + * Included in the Anope module pack since Anope 1.7.9 + * Anope Coder: GeniusDex + * + * Please read COPYING and README for further details. + * + * Send any bug reports to the Anope Coder, as he will be able + * to deal with it best. + */ + +#include "module.h" + +class CommandCSEnforce : public Command +{ + private: + void DoSet(Channel *c) + { + ChannelInfo *ci; + + if (!(ci = c->ci)) + return; + + if (ci->HasFlag(CI_SECUREOPS)) + this->DoSecureOps(c); + if (ci->HasFlag(CI_RESTRICTED)) + this->DoRestricted(c); + } + + void DoModes(Channel *c) + { + if (c->HasMode(CMODE_REGISTEREDONLY)) + this->DoCModeR(c); + } + + void DoSecureOps(Channel *c) + { + ChannelInfo *ci; + bool hadsecureops = false; + + if (!(ci = c->ci)) + return; + + /* Dirty hack to allow chan_set_correct_modes to work ok. + * We pretend like SECUREOPS is on so it doesn't ignore that + * part of the code. This way we can enforce SECUREOPS even + * if it's off. + */ + if (!ci->HasFlag(CI_SECUREOPS)) + { + ci->SetFlag(CI_SECUREOPS); + hadsecureops = true; + } + + for (CUserList::iterator it = c->users.begin(), it_end = c->users.end(); it != it_end; ++it) + { + UserContainer *uc = *it; + + chan_set_correct_modes(uc->user, c, 0); + } + + if (hadsecureops) + ci->UnsetFlag(CI_SECUREOPS); + } + + void DoRestricted(Channel *c) + { + ChannelInfo *ci = c->ci; + if (ci == NULL) + return; + + for (CUserList::iterator it = c->users.begin(), it_end = c->users.end(); it != it_end; ) + { + UserContainer *uc = *it++; + User *user = uc->user; + + if (ci->AccessFor(user).empty()) + { + Anope::string mask; + get_idealban(ci, user, mask); + Anope::string reason = translate(user, CHAN_NOT_ALLOWED_TO_JOIN); + c->SetMode(NULL, CMODE_BAN, mask); + c->Kick(NULL, user, "%s", reason.c_str()); + } + } + } + + void DoCModeR(Channel *c) + { + ChannelInfo *ci; + Anope::string mask; + + if (!(ci = c->ci)) + return; + + for (CUserList::iterator it = c->users.begin(), it_end = c->users.end(); it != it_end; ) + { + UserContainer *uc = *it++; + + if (!uc->user->IsIdentified()) + { + get_idealban(ci, uc->user, mask); + Anope::string reason = translate(uc->user, CHAN_NOT_ALLOWED_TO_JOIN); + if (!c->HasMode(CMODE_REGISTEREDONLY)) + c->SetMode(NULL, CMODE_BAN, mask); + c->Kick(NULL, uc->user, "%s", reason.c_str()); + } + } + } + public: + CommandCSEnforce(Module *creator) : Command(creator, "chanserv/enforce", 1, 2) + { + this->SetDesc(_("Enforce various channel modes and set options")); + this->SetSyntax(_("\037channel\037 [\037what\037]")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + const Anope::string &what = params.size() > 1 ? params[1] : ""; + + User *u = source.u; + Channel *c = findchan(params[0]); + + if (!c) + source.Reply(CHAN_X_NOT_IN_USE, params[0].c_str()); + else if (!c->ci) + source.Reply(CHAN_X_NOT_REGISTERED, c->name.c_str()); + else if (!c->ci->HasPriv(u, CA_AKICK)) + source.Reply(ACCESS_DENIED); + else + { + if (what.empty() || what.equals_ci("SET")) + { + this->DoSet(c); + source.Reply(_("Enforced %s"), !what.empty() ? what.c_str() : "SET"); + } + else if (what.equals_ci("MODES")) + { + this->DoModes(c); + source.Reply(_("Enforced %s"), what.c_str()); + } + else if (what.equals_ci("SECUREOPS")) + { + this->DoSecureOps(c); + source.Reply(_("Enforced %s"), what.c_str()); + } + else if (what.equals_ci("RESTRICTED")) + { + this->DoRestricted(c); + source.Reply(_("Enforced %s"), what.c_str()); + } + else if (what.equals_ci("+R")) + { + this->DoCModeR(c); + source.Reply(_("Enforced %s"), what.c_str()); + } + else + this->OnSyntaxError(source, ""); + } + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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 SET, SECUREOPS, RESTRICTED, MODES,\n" + "or +R. When left out, it defaults to SET.\n" + " \n" + "If \037what\037 is SET, it will enforce SECUREOPS and RESTRICTED\n" + "on the users currently in the channel, if they are set. Give\n" + "SECUREOPS to enforce the SECUREOPS option, even if it is not\n" + "enabled. Use RESTRICTED to enfore the RESTRICTED option, also\n" + "if it's not enabled.")); + source.Reply(" "); + if (ModeManager::FindChannelModeByName(CMODE_REGISTERED)) + source.Reply(_("If \037what\037 is MODES, it will enforce channelmode +R if it is\n" + "set. If +R is specified for \037what\037, the +R channelmode will\n" + "also be enforced, but even if it is not set. If it is not set,\n" + "users will be banned to ensure they don't just rejoin.")); + else + source.Reply(_("If \037what\037 is MODES, nothing will be enforced, since it would\n" + "enforce modes that the current ircd does not support. If +R is\n" + "specified for \037what\037, an equalivant of channelmode +R on\n" + "other ircds will be enforced. All users that are in the channel\n" + "but have not identified for their nickname will be kicked and\n" + "banned from the channel.")); + + return true; + } +}; + +class CSEnforce : public Module +{ + CommandCSEnforce commandcsenforce; + + public: + CSEnforce(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, SUPPORTED), + commandcsenforce(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandcsenforce); + } +}; + +MODULE_INIT(CSEnforce) diff --git a/modules/commands/cs_entrymsg.cpp b/modules/commands/cs_entrymsg.cpp new file mode 100644 index 000000000..3aa8ed862 --- /dev/null +++ b/modules/commands/cs_entrymsg.cpp @@ -0,0 +1,224 @@ +/* ChanServ core functions + * + * (C) 2003-2011 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 EntryMsg +{ + static unsigned MaxEntries; + + EntryMsg(const Anope::string &cname, const Anope::string &cmessage, time_t ct = Anope::CurTime) + { + this->creator = cname; + this->message = cmessage; + this->when = ct; + } + + Anope::string creator; + Anope::string message; + time_t when; +}; +unsigned EntryMsg::MaxEntries = 0; + +class CommandEntryMessage : public Command +{ + private: + void DoList(CommandSource &source, ChannelInfo *ci) + { + std::vector messages; + if (ci->GetExtRegular("cs_entrymsg", messages)) + { + source.Reply(_("Entry message list for \2%s\2:"), ci->name.c_str()); + for (unsigned i = 0; i < messages.size(); ++i) + source.Reply(CHAN_LIST_ENTRY, i + 1, messages[i].message.c_str(), messages[i].creator.c_str(), do_strftime(messages[i].when).c_str()); + source.Reply(_("End of entry message list.")); + } + else + source.Reply(_("Entry message list for \2%s\2 is empty."), ci->name.c_str()); + } + + void DoAdd(CommandSource &source, ChannelInfo *ci, const Anope::string &message) + { + std::vector messages; + ci->GetExtRegular("cs_entrymsg", messages); + + if (EntryMsg::MaxEntries && messages.size() >= EntryMsg::MaxEntries) + source.Reply(_("The entry message list for \2%s\2 is full."), ci->name.c_str()); + else + { + messages.push_back(EntryMsg(source.u->nick, message)); + ci->Extend("cs_entrymsg", new ExtensibleItemRegular >(messages)); + source.Reply(_("Entry message added to \2%s\2"), ci->name.c_str()); + } + } + + void DoDel(CommandSource &source, ChannelInfo *ci, const Anope::string &message) + { + std::vector messages; + 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 (ci->GetExtRegular("cs_entrymsg", messages)) + { + try + { + unsigned i = convertTo(message); + if (i > 0 && i <= messages.size()) + { + messages.erase(messages.begin() + i - 1); + if (!messages.empty()) + ci->Extend("cs_entrymsg", new ExtensibleItemRegular >(messages)); + else + ci->Shrink("cs_entrymsg"); + source.Reply(_("Entry message \2%i\2 for \2%s\2 deleted."), i, ci->name.c_str()); + } + else + throw ConvertException(); + } + catch (const ConvertException &) + { + source.Reply(_("Entry message \2%s\2 not found on channel \2%s\2."), message.c_str(), ci->name.c_str()); + } + } + else + source.Reply(_("Entry message list for \2%s\2 is empty."), ci->name.c_str()); + } + + void DoClear(CommandSource &source, ChannelInfo *ci) + { + ci->Shrink("cs_entrymsg"); + source.Reply(_("Entry messages for \2%s\2 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|DEL|LIST|CLEAR} [\037message\037|\037num\037]")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + + ChannelInfo *ci = cs_findchan(params[0]); + if (ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); + return; + } + + if (IsFounder(u, ci) || u->HasCommand("chanserv/entrymsg")) + { + bool success = true; + 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) + { + success = false; + 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 + { + success = false; + this->OnSyntaxError(source, ""); + } + if (success) + Log(IsFounder(u, ci) ? LOG_COMMAND : LOG_OVERRIDE, u, this, ci) << params[1]; + } + else + source.Reply(ACCESS_DENIED); + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Controls what messages will be sent to users when they join the channel.")); + return true; + } +}; + +class CSEntryMessage : public Module +{ + CommandEntryMessage commandentrymsg; + + public: + CSEntryMessage(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, SUPPORTED), commandentrymsg(this) + { + this->SetAuthor("Anope"); + + Implementation i[] = { I_OnJoinChannel, I_OnReload, I_OnDatabaseReadMetadata, I_OnDatabaseWriteMetadata }; + ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); + + ModuleManager::RegisterService(&commandentrymsg); + + this->OnReload(); + } + + void OnJoinChannel(User *u, Channel *c) + { + if (u && c && c->ci && u->server->IsSynced()) + { + std::vector messages; + + if (c->ci->GetExtRegular("cs_entrymsg", messages)) + for (unsigned i = 0; i < messages.size(); ++i) + u->SendMessage(c->ci->WhoSends(), "[%s] %s", c->ci->name.c_str(), messages[i].message.c_str()); + } + } + + void OnReload() + { + ConfigReader config; + EntryMsg::MaxEntries = config.ReadInteger("cs_entrymsg", "maxentries", "5", 0, true); + } + + EventReturn OnDatabaseReadMetadata(ChannelInfo *ci, const Anope::string &key, const std::vector ¶ms) + { + if (key.find("CS_ENTRYMSG_") == 0 && params.size() > 2) + { + Anope::string creator = params[0]; + time_t t = params[1].is_pos_number_only() ? convertTo(params[1]) : Anope::CurTime; + Anope::string message = params[2]; + for (unsigned j = 3; j < params.size(); ++j) + message += " " + params[j]; + + std::vector messages; + ci->GetExtRegular("cs_entrymsg", messages); + messages.push_back(EntryMsg(creator, message, t)); + ci->Extend("cs_entrymsg", new ExtensibleItemRegular >(messages)); + + return EVENT_STOP; + } + + return EVENT_CONTINUE; + } + + void OnDatabaseWriteMetadata(void (*WriteMetadata)(const Anope::string &, const Anope::string &), ChannelInfo *ci) + { + std::vector messages; + if (ci->GetExtRegular("cs_entrymsg", messages)) + for (unsigned i = 0; i < messages.size(); ++i) + WriteMetadata("CS_ENTRYMSG_" + stringify(i), messages[i].creator + " " + stringify(messages[i].when) + " " + messages[i].message); + } +}; + +MODULE_INIT(CSEntryMessage) diff --git a/modules/commands/cs_flags.cpp b/modules/commands/cs_flags.cpp new file mode 100644 index 000000000..48f1a22f1 --- /dev/null +++ b/modules/commands/cs_flags.cpp @@ -0,0 +1,455 @@ +/* ChanServ core functions + * + * (C) 2003-2011 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 struct FlagLevels +{ + ChannelAccess priv; + char default_char; + Anope::string config_name; + Anope::string desc; +} flagLevels[] = { + { CA_ACCESS_CHANGE, 'f', "flag_change", _("Allowed to modify the access list") }, + { CA_ACCESS_LIST, 'l', "flag_list", _("Allowed to view the access list") }, + { CA_AKICK, 'K', "flag_akick", _("Allowed to use AKICK command") }, + { CA_ASSIGN, 's', "flag_assign", _("Allowed to assign/unassign a bot") }, + { CA_AUTOHALFOP, 'H', "flag_autohalfop", _("Automatic mode +h") }, + { CA_AUTOOP, 'O', "flag_autoop", _("Automatic channel operator status") }, + { CA_AUTOOWNER, 'Q', "flag_autoowner", _("Automatic mode +q") }, + { CA_AUTOPROTECT, 'A', "flag_autoprotect", _("Automatic mode +a") }, + { CA_AUTOVOICE, 'V', "flag_autovoice", _("Automatic mode +v") }, + { CA_BADWORDS, 'k', "flag_badwords", _("Allowed to modify channel badwords list") }, + { CA_BAN, 'b', "flag_ban", _("Allowed to use ban users") }, + { CA_FANTASIA, 'c', "flag_fantasia", _("Allowed to use fantasy commands") }, + { CA_FOUNDER, 'F', "flag_founder", _("Allowed to issue commands restricted to channel founders") }, + { CA_GETKEY, 'G', "flag_getkey", _("Allowed to use GETKEY command") }, + { CA_GREET, 'g', "flag_greet", _("Greet message displayed") }, + { CA_HALFOP, 'h', "flag_halfop", _("Allowed to (de)halfop users") }, + { CA_HALFOPME, 'h', "flag_halfopme", _("Allowed to (de)halfop him/herself") }, + { CA_INFO, 'I', "flag_info", _("Allowed to use INFO command with ALL option") }, + { CA_INVITE, 'i', "flag_invite", _("Allowed to use the INVITE command") }, + { CA_KICK, 'k', "flag_kick", _("Allowed to use the KICK command") }, + { CA_MEMO, 'm', "flag_memo", _("Allowed to read channel memos") }, + { CA_MODE, 's', "flag_mode", _("Allowed to change channel modes") }, + { CA_NOKICK, 'N', "flag_nokick", _("Prevents users being kicked by Services") }, + { CA_OPDEOP, 'o', "flag_opdeop", _("Allowed to (de)op users") }, + { CA_OPDEOPME, 'o', "flag_opdeopme", _("Allowed to (de)op him/herself") }, + { CA_OWNER, 'q', "flag_owner", _("Allowed to use (de)owner users") }, + { CA_OWNERME, 'q', "flag_ownerme", _("Allowed to (de)owner him/herself") }, + { CA_PROTECT, 'a', "flag_protect", _("Allowed to (de)protect users") }, + { CA_PROTECTME, 'a', "flag_protectme", _("Allowed to (de)protect him/herself"), }, + { CA_SAY, 'B', "flag_say", _("Allowed to use SAY and ACT commands") }, + { CA_SET, 's', "flag_set", _("Allowed to set channel settings") }, + { CA_SIGNKICK, 'K', "flag_signkick", _("Prevents kicks from being signed") }, + { CA_TOPIC, 't', "flag_topic", _("Allowed to change channel topics") }, + { CA_UNBAN, 'u', "flag_unban", _("Allowed to unban users") }, + { CA_VOICE, 'v', "flag_voice", _("Allowed to (de)voice users") }, + { CA_VOICEME, 'v', "flag_voiceme", _("Allowed to (de)voice him/herself") }, + { CA_SIZE, -1, "", "" } +}; + +class FlagsChanAccess : public ChanAccess +{ + public: + std::set flags; + + FlagsChanAccess(AccessProvider *p) : ChanAccess(p) + { + } + + bool Matches(User *u, NickCore *nc) + { + if (u && (Anope::Match(u->nick, this->mask) || Anope::Match(u->GetMask(), this->mask))) + return true; + else if (nc && Anope::Match(nc->display, this->mask)) + return true; + return false; + } + + bool HasPriv(ChannelAccess priv) + { + for (int i = 0; flagLevels[i].priv != CA_SIZE; ++i) + if (flagLevels[i].priv == priv) + return this->flags.count(flagLevels[i].default_char); + + return false; + } + + Anope::string Serialize() + { + return Anope::string(this->flags.begin(), this->flags.end()); + } + + void Unserialize(const Anope::string &data) + { + for (unsigned i = data.length(); i > 0; --i) + this->flags.insert(data[i - 1]); + } + + static Anope::string DetermineFlags(ChanAccess *access) + { + if (access->provider->name == "access/flags") + return access->Serialize(); + + std::set buffer; + + for (int i = 0; flagLevels[i].priv != CA_SIZE; ++i) + { + FlagLevels &l = flagLevels[i]; + + if (access->HasPriv(l.priv)) + buffer.insert(l.default_char); + } + + return Anope::string(buffer.begin(), buffer.end()); + } +}; + +class FlagsAccessProvider : public AccessProvider +{ + public: + FlagsAccessProvider(Module *o) : AccessProvider(o, "access/flags") + { + } + + ChanAccess *Create() + { + return new FlagsChanAccess(this); + } +}; + +class CommandCSFlags : public Command +{ + void DoModify(CommandSource &source, ChannelInfo *ci, const std::vector ¶ms) + { + User *u = source.u; + + Anope::string mask = params.size() > 2 ? params[2] : ""; + Anope::string flags = params.size() > 3 ? params[3] : ""; + + if (flags.empty()) + { + this->OnSyntaxError(source, ""); + return; + } + + AccessGroup u_access = ci->AccessFor(u); + + if (mask.find_first_of("!*@") == Anope::string::npos && findnick(mask) == NULL) + mask += "!*@*"; + + ChanAccess *current = NULL; + std::set current_flags; + for (unsigned i = ci->GetAccessCount(); i > 0; --i) + { + ChanAccess *access = ci->GetAccess(i - 1); + if (mask.equals_ci(access->mask)) + { + 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; + } + } + + if (ci->GetAccessCount() >= Config->CSAccessMax) + { + source.Reply(_("Sorry, you can only have %d access entries on a channel."), Config->CSAccessMax); + return; + } + + bool override = false; + int add = -1; + for (size_t i = 0; i < flags.length(); ++i) + { + char f = flags[i]; + switch (f) + { + case '+': + add = 1; + break; + case '-': + add = 0; + break; + case '*': + if (add == -1) + break; + for (int j = 0; flagLevels[j].priv != CA_SIZE; ++j) + { + FlagLevels &l = flagLevels[j]; + if (!u_access.HasPriv(l.priv)) + { + if (u->HasPriv("chanserv/access/modify")) + override = true; + else + continue; + } + if (add == 1) + current_flags.insert(f); + else if (add == 0) + current_flags.erase(f); + } + break; + default: + if (add == -1) + break; + for (int j = 0; flagLevels[j].priv != CA_SIZE; ++j) + { + FlagLevels &l = flagLevels[j]; + if (f != l.default_char) + continue; + else if (!u_access.HasPriv(l.priv)) + { + if (u->HasPriv("chanserv/access/modify")) + override = true; + else + { + source.Reply(_("You can not set the \002%c\002 flag."), f); + continue; + } + } + if (add == 1) + current_flags.insert(f); + else if (add == 0) + current_flags.erase(f); + break; + } + } + } + if (current_flags.empty()) + { + if (current != NULL) + { + FOREACH_MOD(I_OnAccessDel, OnAccessDel(ci, u, current)); + ci->EraseAccess(current); + source.Reply(_("\002%s\002 removed from the %s access list."), mask.c_str(), ci->name.c_str()); + } + else + { + source.Reply(_("Insufficient flags given")); + } + return; + } + + service_reference provider("access/flags"); + if (!provider) + return; + FlagsChanAccess *access = debug_cast(provider->Create()); + access->ci = ci; + access->mask = mask; + access->creator = u->nick; + access->last_seen = current ? current->last_seen : 0; + access->created = Anope::CurTime; + access->flags = current_flags; + + if (current != NULL) + ci->EraseAccess(current); + + ci->AddAccess(access); + + FOREACH_MOD(I_OnAccessAdd, OnAccessAdd(ci, u, access)); + + Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "MODIFY " << mask << " with flags " << access->Serialize(); + source.Reply(_("Access for \002%s\002 on %s set to +\002%s\002"), access->mask.c_str(), ci->name.c_str(), access->Serialize().c_str()); + + return; + } + + void DoList(CommandSource &source, ChannelInfo *ci, const std::vector ¶ms) + { + const Anope::string &arg = params.size() > 2 ? params[2] : ""; + + if (!ci->GetAccessCount()) + source.Reply(_("%s access list is empty."), ci->name.c_str()); + else + { + unsigned total = 0; + + for (unsigned i = 0, end = ci->GetAccessCount(); i < end; ++i) + { + 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; + } + + if (++total == 1) + { + source.Reply(_("Flags list for %s"), ci->name.c_str()); + } + + source.Reply(_(" %3d %-10s +%-10s [last modified on %s by %s]"), i + 1, access->mask.c_str(), FlagsChanAccess::DetermineFlags(access).c_str(), do_strftime(access->created, source.u->Account(), true).c_str(), access->creator.c_str()); + } + + if (total == 0) + source.Reply(_("No matching entries on %s access list."), ci->name.c_str()); + else if (total == ci->GetAccessCount()) + source.Reply(_("End of access list.")); + else + source.Reply(_("End of access list - %d/%d entries shown."), total, ci->GetAccessCount()); + } + } + + void DoClear(CommandSource &source, ChannelInfo *ci) + { + User *u = source.u; + + if (!IsFounder(u, ci) && !u->HasPriv("chanserv/access/modify")) + source.Reply(ACCESS_DENIED); + else + { + ci->ClearAccess(); + + FOREACH_MOD(I_OnAccessClear, OnAccessClear(ci, u)); + + source.Reply(_("Channel %s access list has been cleared."), ci->name.c_str()); + + bool override = !IsFounder(u, ci); + Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "CLEAR"; + } + + return; + } + + public: + CommandCSFlags(Module *creator) : Command(creator, "chanserv/flags", 2, 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\002")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + const Anope::string &chan = params[0]; + const Anope::string &cmd = params[1]; + + User *u = source.u; + ChannelInfo *ci = cs_findchan(chan); + if (ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, chan.c_str()); + return; + } + + bool is_list = cmd.equals_ci("LIST"); + bool has_access = false; + if (u->HasPriv("chanserv/access/modify")) + has_access = true; + else if (is_list && ci->HasPriv(u, CA_ACCESS_LIST)) + has_access = true; + else if (ci->HasPriv(u, CA_ACCESS_CHANGE)) + has_access = true; + + if (!has_access) + source.Reply(ACCESS_DENIED); + else if (readonly && !is_list) + source.Reply(_("Sorry, channel access list modification is temporarily disabled.")); + else if (cmd.equals_ci("MODIFY")) + this->DoModify(source, ci, params); + else if (cmd.equals_ci("LIST")) + this->DoList(source, ci, params); + else if (cmd.equals_ci("CLEAR")) + this->DoClear(source, ci); + else + this->OnSyntaxError(source, cmd); + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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 MODIFY command allows you to modify the access list. If mask is\n" + "not already on the access list is it 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 up what you already have.")); + source.Reply(" "); + source.Reply(_("The LIST 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 CLEAR command clears the channel access list, which requires channel founder.")); + source.Reply(" "); + source.Reply(_("The available flags are:")); + + std::multimap > levels; + for (int i = 0; flagLevels[i].priv != CA_SIZE; ++i) + levels.insert(std::make_pair(flagLevels[i].default_char, &flagLevels[i])); + for (std::multimap >::iterator it = levels.begin(), it_end = levels.end(); it != it_end; ++it) + { + FlagLevels *l = it->second; + source.Reply(" %c - %s", l->default_char, translate(source.u->Account(), l->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, CORE), + accessprovider(this), commandcsflags(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&accessprovider); + ModuleManager::RegisterService(&commandcsflags); + + Implementation i[] = { I_OnReload }; + ModuleManager::Attach(i, this, 1); + + this->OnReload(); + } + + void OnReload() + { + ConfigReader config; + + for (int i = 0; flagLevels[i].priv != CA_SIZE; ++i) + { + FlagLevels &l = flagLevels[i]; + + const Anope::string &value = config.ReadValue("chanserv", l.config_name, "", 0); + if (value.empty()) + continue; + l.default_char = value[0]; + } + } +}; + +MODULE_INIT(CSFlags) diff --git a/modules/commands/cs_getkey.cpp b/modules/commands/cs_getkey.cpp new file mode 100644 index 000000000..4a6d62442 --- /dev/null +++ b/modules/commands/cs_getkey.cpp @@ -0,0 +1,80 @@ +/* ChanServ core functions + * + * (C) 2003-2011 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 ¶ms) + { + const Anope::string &chan = params[0]; + + User *u = source.u; + ChannelInfo *ci = cs_findchan(params[0]); + if (ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); + return; + } + + + if (!ci->HasPriv(u, CA_GETKEY) && !u->HasCommand("chanserv/chanserv/getkey")) + { + source.Reply(ACCESS_DENIED); + return; + } + + Anope::string key; + if (!ci->c || !ci->c->GetParam(CMODE_KEY, key)) + { + source.Reply(_("The channel \002%s\002 has no key."), chan.c_str()); + return; + } + + bool override = !ci->HasPriv(u, CA_GETKEY); + Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci); + + source.Reply(_("Key for channel \002%s\002 is \002%s\002."), chan.c_str(), key.c_str()); + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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, CORE), commandcsgetkey(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandcsgetkey); + } +}; + +MODULE_INIT(CSGetKey) diff --git a/modules/commands/cs_info.cpp b/modules/commands/cs_info.cpp new file mode 100644 index 000000000..ef026563e --- /dev/null +++ b/modules/commands/cs_info.cpp @@ -0,0 +1,142 @@ +/* ChanServ core functions + * + * (C) 2003-2011 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 +{ + void CheckOptStr(Anope::string &buf, ChannelInfoFlag opt, const Anope::string &str, ChannelInfo *ci, const NickCore *nc) + { + if (ci->HasFlag(opt)) + { + if (!buf.empty()) + buf += ", "; + + buf += str; + } + } + + public: + CommandCSInfo(Module *creator) : Command(creator, "chanserv/info", 1, 2) + { + this->SetFlag(CFLAG_ALLOW_UNREGISTERED); + this->SetDesc(_("Lists information about the named registered channel")); + this->SetSyntax(_("\037channel\037")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + const Anope::string &chan = params[0]; + + User *u = source.u; + ChannelInfo *ci = cs_findchan(params[0]); + if (ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); + return; + } + + bool has_auspex = u->IsIdentified() && u->HasPriv("chanserv/auspex"); + bool show_all = false; + + /* Should we show all fields? Only for sadmins and identified users */ + if (has_auspex || ci->HasPriv(u, CA_INFO)) + show_all = true; + + source.Reply(CHAN_INFO_HEADER, chan.c_str()); + if (ci->GetFounder()) + source.Reply(_(" Founder: %s"), ci->GetFounder()->display.c_str()); + + if (show_all && ci->successor) + source.Reply(_(" Successor: %s"), ci->successor->display.c_str()); + + if (!ci->desc.empty()) + source.Reply(_(" Description: %s"), ci->desc.c_str()); + source.Reply(_(" Registered: %s"), do_strftime(ci->time_registered).c_str()); + source.Reply(_(" Last used: %s"), do_strftime(ci->last_used).c_str()); + + ModeLock *secret = ci->GetMLock(CMODE_SECRET); + if (!ci->last_topic.empty() && (show_all || ((!secret || secret->set == false) && (!ci->c || !ci->c->HasMode(CMODE_SECRET))))) + { + source.Reply(_(" Last topic: %s"), ci->last_topic.c_str()); + source.Reply(_(" Topic set by: %s"), ci->last_topic_setter.c_str()); + } + + if (show_all) + { + source.Reply(_(" Ban type: %d"), ci->bantype); + Anope::string optbuf; + + CheckOptStr(optbuf, CI_KEEPTOPIC, _("Topic Retention"), ci, u->Account()); + CheckOptStr(optbuf, CI_OPNOTICE, _("OP Notice"), ci, u->Account()); + CheckOptStr(optbuf, CI_PEACE, _("Peace"), ci, u->Account()); + CheckOptStr(optbuf, CI_PRIVATE, _("Private"), ci, u->Account()); + CheckOptStr(optbuf, CI_RESTRICTED, _("Restricted Access"), ci, u->Account()); + CheckOptStr(optbuf, CI_SECURE, _("Secure"), ci, u->Account()); + CheckOptStr(optbuf, CI_SECUREFOUNDER, _("Secure Founder"), ci, u->Account()); + CheckOptStr(optbuf, CI_SECUREOPS, _("Secure Ops"), ci, u->Account()); + if (ci->HasFlag(CI_SIGNKICK)) + CheckOptStr(optbuf, CI_SIGNKICK, _("Signed kicks"), ci, u->Account()); + else + CheckOptStr(optbuf, CI_SIGNKICK_LEVEL, _("Signed kicks"), ci, u->Account()); + CheckOptStr(optbuf, CI_TOPICLOCK, _("Topic Lock"), ci, u->Account()); + CheckOptStr(optbuf, CI_PERSIST, _("Persistant"), ci, u->Account()); + CheckOptStr(optbuf, CI_NO_EXPIRE, _("No expire"), ci, u->Account()); + + source.Reply(NICK_INFO_OPTIONS, optbuf.empty() ? _("None") : optbuf.c_str()); + source.Reply(_(" Mode lock: %s"), ci->GetMLockAsString(true).c_str()); + + if (!ci->HasFlag(CI_NO_EXPIRE)) + source.Reply(_(" Expires on: %s"), do_strftime(ci->last_used + Config->CSExpire).c_str()); + } + if (ci->HasFlag(CI_SUSPENDED)) + { + Anope::string by, reason; + ci->GetExtRegular("suspend_by", by); + ci->GetExtRegular("suspend_reason", reason); + source.Reply(_(" Suspended: [%s] %s"), by.c_str(), !reason.empty() ? reason.c_str() : NO_REASON); + } + + FOREACH_MOD(I_OnChanInfo, OnChanInfo(source, ci, show_all)); + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Lists information about the named registered channel,\n" + "including its founder, time of registration, last time\n" + "used, description, and mode lock, if any. If \002ALL\002 is \n" + "specified, the entry message and successor will also \n" + "be displayed.")); + return true; + } +}; + +class CSInfo : public Module +{ + CommandCSInfo commandcsinfo; + + public: + CSInfo(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandcsinfo(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandcsinfo); + } +}; + +MODULE_INIT(CSInfo) diff --git a/modules/commands/cs_invite.cpp b/modules/commands/cs_invite.cpp new file mode 100644 index 000000000..27b4b03a3 --- /dev/null +++ b/modules/commands/cs_invite.cpp @@ -0,0 +1,102 @@ +/* ChanServ core functions + * + * (C) 2003-2011 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 into a channel")); + this->SetSyntax(_("\037channel\037")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + const Anope::string &chan = params[0]; + + User *u = source.u; + Channel *c = findchan(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 (!ci->HasPriv(u, CA_INVITE)) + { + source.Reply(ACCESS_DENIED); + return; + } + + User *u2; + if (params.size() == 1) + u2 = u; + else + { + if (!(u2 = finduser(params[1]))) + { + source.Reply(NICK_X_NOT_IN_USE, params[1].c_str()); + return; + } + } + + // XXX need a check for override... + Log(LOG_COMMAND, u, this, ci) << "for " << u2->nick; + + if (c->FindUser(u2)) + source.Reply(_("You are already in \002%s\002! "), c->name.c_str()); + else + { + ircdproto->SendInvite(ci->WhoSends(), chan, u2->nick); + 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."), c->name.c_str()); + } + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Tells %s to invite you into the given channel.\n" + " \n" + "By default, limited to AOPs or those with level 5 and above\n" + "on the channel."), source.owner->nick.c_str()); + return true; + } +}; + +class CSInvite : public Module +{ + CommandCSInvite commandcsinvite; + + public: + CSInvite(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), commandcsinvite(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandcsinvite); + } +}; + +MODULE_INIT(CSInvite) diff --git a/modules/commands/cs_kick.cpp b/modules/commands/cs_kick.cpp new file mode 100644 index 000000000..b98d0953d --- /dev/null +++ b/modules/commands/cs_kick.cpp @@ -0,0 +1,91 @@ +/* ChanServ core functions + * + * (C) 2003-2011 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 selected nick from a channel")); + this->SetSyntax(_("\037channel\037 \037nick\037 [\037reason\037]")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + const Anope::string &chan = params[0]; + const Anope::string &target = params[1]; + const Anope::string &reason = params.size() > 2 ? params[2] : "Requested"; + + User *u = source.u; + ChannelInfo *ci = cs_findchan(params[0]); + Channel *c = findchan(params[0]); + bool is_same = target.equals_ci(u->nick); + User *u2 = is_same ? u : finduser(target); + + AccessGroup u_access = ci->AccessFor(u), u2_access = ci->AccessFor(u2); + + if (!c) + source.Reply(CHAN_X_NOT_IN_USE, chan.c_str()); + else if (!ci) + source.Reply(CHAN_X_NOT_REGISTERED, chan.c_str()); + else if (!u2) + source.Reply(NICK_X_NOT_IN_USE, target.c_str()); + else if (!ci->HasPriv(u, CA_KICK)) + source.Reply(ACCESS_DENIED); + else if (!is_same && (ci->HasFlag(CI_PEACE)) && u2_access >= u_access) + 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 + { + // XXX + Log(LOG_COMMAND, u, this, ci) << "for " << u2->nick; + + if (ci->HasFlag(CI_SIGNKICK) || (ci->HasFlag(CI_SIGNKICK_LEVEL) && !ci->HasPriv(u, CA_SIGNKICK))) + ci->c->Kick(ci->WhoSends(), u2, "%s (%s)", reason.c_str(), u->nick.c_str()); + else + ci->c->Kick(ci->WhoSends(), u2, "%s", reason.c_str()); + } + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Kicks a selected nick on a channel.\n" + " \n" + "By default, limited to AOPs or those with level 5 access \n" + "and above on the channel.")); + return true; + } +}; + +class CSKick : public Module +{ + CommandCSKick commandcskick; + + public: + CSKick(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), commandcskick(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandcskick); + } +}; + +MODULE_INIT(CSKick) diff --git a/modules/commands/cs_list.cpp b/modules/commands/cs_list.cpp new file mode 100644 index 000000000..a79c047c4 --- /dev/null +++ b/modules/commands/cs_list.cpp @@ -0,0 +1,136 @@ +/* ChanServ core functions + * + * (C) 2003-2011 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 CommandCSList : public Command +{ + public: + CommandCSList(Module *creator) : Command(creator, "chanserv/list", 1, 2) + { + this->SetFlag(CFLAG_STRIP_CHANNEL); + this->SetDesc(_("Lists all registered channels matching the given pattern")); + this->SetSyntax(_("\037pattern\037")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + + Anope::string pattern = params[0]; + unsigned nchans; + bool is_servadmin = u->HasCommand("chanserv/chanserv/list"); + int count = 0, from = 0, to = 0; + bool suspended = false, channoexpire = false; + + if (pattern[0] == '#') + { + Anope::string n1 = myStrGetToken(pattern.substr(1), '-', 0), /* Read FROM out */ + n2 = myStrGetTokenRemainder(pattern, '-', 1); + + try + { + from = convertTo(n1); + to = convertTo(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; + + source.Reply(LIST_HEADER, pattern.c_str()); + + for (registered_channel_map::const_iterator it = RegisteredChannelList.begin(), it_end = RegisteredChannelList.end(); it != it_end; ++it) + { + ChannelInfo *ci = it->second; + + if (!is_servadmin && (ci->HasFlag(CI_PRIVATE) || ci->HasFlag(CI_SUSPENDED))) + continue; + else if (suspended && !ci->HasFlag(CI_SUSPENDED)) + continue; + else if (channoexpire && !ci->HasFlag(CI_NO_EXPIRE)) + continue; + + if (pattern.equals_ci(ci->name) || ci->name.equals_ci(spattern) || Anope::Match(ci->name, pattern) || Anope::Match(ci->name, spattern)) + { + if (((count + 1 >= from && count + 1 <= to) || (!from && !to)) && ++nchans <= Config->CSListMax) + { + char noexpire_char = ' '; + if (is_servadmin && (ci->HasFlag(CI_NO_EXPIRE))) + noexpire_char = '!'; + + Anope::string buf; + if (ci->HasFlag(CI_SUSPENDED)) + buf = Anope::printf("%-20s [Suspended]", ci->name.c_str()); + else + buf = Anope::printf("%-20s %s", ci->name.c_str(), !ci->desc.empty() ? ci->desc.c_str() : ""); + + source.Reply(" %c%s", noexpire_char, buf.c_str()); + } + ++count; + } + } + + source.Reply(_("End of list - %d/%d matches shown."), nchans > Config->CSListMax ? Config->CSListMax : nchans, nchans); + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Lists all registered channels matching the given pattern.\n" + "(Channels with the \002PRIVATE\002 option set are not listed.)\n" + "Note that a preceding '#' specifies a range, channel names\n" + "are to be written without '#'.")); + return true; + } +}; + +class CSList : public Module +{ + CommandCSList commandcslist; + + public: + CSList(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), commandcslist(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandcslist); + } +}; + +MODULE_INIT(CSList) diff --git a/modules/commands/cs_mode.cpp b/modules/commands/cs_mode.cpp new file mode 100644 index 000000000..e67abd85a --- /dev/null +++ b/modules/commands/cs_mode.cpp @@ -0,0 +1,364 @@ +/* ChanServ core functions + * + * (C) 2003-2011 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 CommandCSMode : public Command +{ + bool CanSet(User *u, ChannelInfo *ci, ChannelModeName mode) + { + switch (mode) + { + case CMODE_OWNER: + return ci->HasPriv(u, CA_OWNER); + case CMODE_PROTECT: + return ci->HasPriv(u, CA_PROTECT); + case CMODE_OP: + return ci->HasPriv(u, CA_OPDEOP); + case CMODE_HALFOP: + return ci->HasPriv(u, CA_HALFOP); + case CMODE_VOICE: + return ci->HasPriv(u, CA_VOICE); + default: + break; + } + + return false; + } + + void DoLock(CommandSource &source, ChannelInfo *ci, const std::vector ¶ms) + { + User *u = source.u; + const Anope::string &subcommand = params[2]; + const Anope::string ¶m = params.size() > 3 ? params[3] : ""; + + if (subcommand.equals_ci("ADD") && !param.empty()) + { + spacesepstream sep(param); + Anope::string modes; + + sep.GetToken(modes); + + int adding = -1; + for (size_t i = 0; i < modes.length(); ++i) + { + switch (modes[i]) + { + case '+': + adding = 1; + break; + case '-': + adding = 0; + break; + default: + if (adding == -1) + break; + ChannelMode *cm = ModeManager::FindChannelModeByChar(modes[i]); + if (!cm || !cm->CanSet(u)) + { + source.Reply(_("Unknown mode character %c ignored."), 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->ModeChar); + else + { + ci->SetMLock(cm, adding, mode_param, u->nick); + if (!mode_param.empty()) + mode_param = " " + mode_param; + source.Reply(_("%c%c%s locked on %s"), adding ? '+' : '-', cm->ModeChar, mode_param.c_str(), ci->name.c_str()); + } + } + } + + if (ci->c) + check_modes(ci->c); + } + else if (subcommand.equals_ci("DEL") && !param.empty()) + { + spacesepstream sep(param); + Anope::string modes; + + sep.GetToken(modes); + + int adding = -1; + for (size_t i = 0; i < modes.length(); ++i) + { + switch (modes[i]) + { + case '+': + adding = 1; + break; + case '-': + adding = 0; + break; + default: + if (adding == -1) + break; + ChannelMode *cm = ModeManager::FindChannelModeByChar(modes[i]); + if (!cm || !cm->CanSet(u)) + { + source.Reply(_("Unknown mode character %c ignored."), modes[i]); + break; + } + Anope::string mode_param; + if (!cm->Type == MODE_REGULAR && !sep.GetToken(mode_param)) + source.Reply(_("Missing parameter for mode %c."), cm->ModeChar); + else + { + if (ci->RemoveMLock(cm, mode_param)) + { + if (!mode_param.empty()) + mode_param = " " + mode_param; + source.Reply(_("%c%c%s has been unlocked from %s."), adding == 1 ? '+' : '-', cm->ModeChar, mode_param.c_str(), ci->name.c_str()); + } + else + source.Reply(_("%c is not locked on %s."), cm->ModeChar, ci->name.c_str()); + } + } + } + } + else if (subcommand.equals_ci("LIST")) + { + const std::multimap &mlocks = ci->GetMLock(); + if (mlocks.empty()) + { + source.Reply(_("Channel %s has no mode locks."), ci->name.c_str()); + } + else + { + source.Reply(_("Mode locks for %s:"), ci->name.c_str()); + for (std::multimap::const_iterator it = mlocks.begin(), it_end = mlocks.end(); it != it_end; ++it) + { + const ModeLock &ml = it->second; + ChannelMode *cm = ModeManager::FindChannelModeByName(ml.name); + if (!cm) + continue; + + Anope::string modeparam = ml.param; + if (!modeparam.empty()) + modeparam = " " + modeparam; + source.Reply(_("%c%c%s, by %s on %s"), ml.set ? '+' : '-', cm->ModeChar, modeparam.c_str(), ml.setter.c_str(), do_strftime(ml.created).c_str()); + } + } + } + else + this->OnSyntaxError(source, subcommand); + } + + void DoSet(CommandSource &source, ChannelInfo *ci, const std::vector ¶ms) + { + User *u = source.u; + + spacesepstream sep(params.size() > 3 ? params[3] : ""); + Anope::string modes = params[2], param; + + Log(LOG_COMMAND, u, this, ci) << "to set " << params[2]; + + 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) + break; + for (unsigned j = 0; j < ModeManager::ChannelModes.size(); ++j) + { + ChannelMode *cm = ModeManager::ChannelModes[j]; + if (cm->CanSet(u)) + { + 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 || !cm->CanSet(u)) + continue; + switch (cm->Type) + { + case MODE_REGULAR: + if (adding) + ci->c->SetMode(NULL, cm); + else + ci->c->RemoveMode(NULL, cm); + break; + case MODE_PARAM: + 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)) + break; + + if (!this->CanSet(u, ci, cm->Name)) + { + source.Reply(_("You do not have access to set mode %c."), cm->ModeChar); + break; + } + + AccessGroup u_access = ci->AccessFor(u); + + if (str_is_wildcard(param)) + { + for (CUserList::const_iterator it = ci->c->users.begin(), it_end = ci->c->users.end(); it != it_end; ++it) + { + UserContainer *uc = *it; + + AccessGroup targ_access = ci->AccessFor(uc->user); + + if (targ_access > u_access) + { + 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->nick); + else + ci->c->RemoveMode(NULL, cm, uc->user->nick); + } + } + } + else + { + User *target = finduser(param); + if (target != NULL) + { + AccessGroup targ_access = ci->AccessFor(target); + if (targ_access > u_access) + { + source.Reply(_("You do not have the access to change %s's modes."), target->nick.c_str()); + break; + } + } + if (adding) + ci->c->SetMode(NULL, cm, param); + else + ci->c->RemoveMode(NULL, cm, param); + } + break; + } + case MODE_LIST: + if (!sep.GetToken(param)) + break; + if (adding) + ci->c->SetMode(NULL, cm, param); + else + { + std::pair its = ci->c->GetModeList(cm->Name); + for (; its.first != its.second;) + { + const Anope::string &mask = its.first->second; + ++its.first; + + if (Anope::Match(mask, param)) + ci->c->RemoveMode(NULL, cm, mask); + } + } + } + } + } + } + + public: + CommandCSMode(Module *creator) : Command(creator, "chanserv/mode", 3, 4) + { + this->SetDesc(_("Control modes and mode locks on a channel")); + this->SetSyntax(_("\037channel\037 LOCK {ADD|DEL|LIST} [\037what\037]")); + this->SetSyntax(_("\037channel\037 SET \037modes\037")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + const Anope::string &subcommand = params[1]; + + User *u = source.u; + ChannelInfo *ci = cs_findchan(params[0]); + + if (!ci || !ci->c) + source.Reply(CHAN_X_NOT_IN_USE, ci->name.c_str()); + else if (!ci->HasPriv(u, CA_MODE) && !u->HasCommand("chanserv/chanserv/mode")) + source.Reply(ACCESS_DENIED); + else if (subcommand.equals_ci("LOCK")) + this->DoLock(source, ci, params); + else if (subcommand.equals_ci("SET")) + this->DoSet(source, ci, params); + else + this->OnSyntaxError(source, ""); + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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 \002MODE 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.\n" + "Example:\n" + " \002MODE #channel LOCK ADD +bmnt *!*@*aol*\002\n" + " \n" + "The \002MODE 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:")); + return true; + } +}; + +class CSMode : public Module +{ + CommandCSMode commandcsmode; + + public: + CSMode(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandcsmode(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandcsmode); + } +}; + +MODULE_INIT(CSMode) diff --git a/modules/commands/cs_modes.cpp b/modules/commands/cs_modes.cpp new file mode 100644 index 000000000..cee2f9460 --- /dev/null +++ b/modules/commands/cs_modes.cpp @@ -0,0 +1,431 @@ +/* ChanServ core functions + * + * (C) 2003-2011 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 CommandModeBase : public Command +{ + void do_mode(CommandSource &source, Command *com, ChannelMode *cm, const Anope::string &chan, const Anope::string &nick, bool set, ChannelAccess level, ChannelAccess levelself, ChannelInfoFlag notice) + { + User *u = source.u; + User *u2 = finduser(nick); + Channel *c = findchan(chan); + ChannelInfo *ci = c ? c->ci : NULL; + + bool is_same = u == u2; + + AccessGroup u_access = ci ? ci->AccessFor(u) : AccessGroup(), u2_access = ci && u2 ? ci->AccessFor(u2) : AccessGroup(); + + if (!c) + source.Reply(CHAN_X_NOT_IN_USE, chan.c_str()); + else if (!ci) + source.Reply(CHAN_X_NOT_REGISTERED, chan.c_str()); + else if (!u2) + source.Reply(NICK_X_NOT_IN_USE, nick.c_str()); + else if (is_same ? !ci->HasPriv(u, levelself) : !ci->HasPriv(u, level)) + source.Reply(ACCESS_DENIED); + else if (!set && !is_same && ci->HasFlag(CI_PEACE) && u2_access >= u_access) + source.Reply(ACCESS_DENIED); + else if (!set && u2->IsProtected() && !is_same) + 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 + { + if (set) + c->SetMode(NULL, cm, u2->nick); + else + c->RemoveMode(NULL, cm, u2->nick); + + Log(LOG_COMMAND, u, com, ci) << "for " << u2->nick; + if (notice && ci->HasFlag(notice)) + ircdproto->SendMessage(ci->WhoSends(), c->name, "%s command used for %s by %s", com->name.c_str(), u2->nick.c_str(), u->nick.c_str()); + } + } + + protected: + /** do_util: not a command, but does the job of others + * @param source The source of the command + * @param com The command calling this function + * @param cm A channel mode class + * @param chan The channel its being set on + * @param nick The nick the modes being set on + * @param set Is the mode being set or removed + * @param level The acecss level required to set this mode on someone else + * @param levelself The access level required to set this mode on yourself + * @param notice Flag required on a channel to send a notice + */ + void do_util(CommandSource &source, Command *com, ChannelMode *cm, const Anope::string &chan, const Anope::string &nick, bool set, ChannelAccess level, ChannelAccess levelself, ChannelInfoFlag notice) + { + User *u = source.u; + + if (chan.empty()) + for (UChannelList::iterator it = u->chans.begin(); it != u->chans.end(); ++it) + do_mode(source, com, cm, (*it)->chan->name, u->nick, set, level, levelself, notice); + else + do_mode(source, com, cm, chan, !nick.empty() ? nick : u->nick, set, level, levelself, notice); + + return; + } + + public: + CommandModeBase(Module *creator, const Anope::string &cname) : Command(creator, cname, 0, 2) + { + this->SetSyntax(_("[\037#channel\037] [\037nick\037]")); + } +}; + +class CommandCSOp : public CommandModeBase +{ + public: + CommandCSOp(Module *creator) : CommandModeBase(creator, "chanserv/op") + { + this->SetDesc(_("Gives Op status to a selected nick on a channel")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + ChannelMode *cm = ModeManager::FindChannelModeByName(CMODE_OP); + + return do_util(source, this, cm, !params.empty() ? params[0] : "", params.size() > 1 ? params[1] : "", true, CA_OPDEOP, CA_OPDEOPME, CI_OPNOTICE); + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Ops a selected nick on a channel. If nick is not given,\n" + "it will op you. If channel is not given, it will op you\n" + "on every channel.\n" + " \n" + "By default, limited to AOPs or those with level 5 access \n" + "and above on the channel.")); + return true; + } +}; + +class CommandCSDeOp : public CommandModeBase +{ + public: + CommandCSDeOp(Module *creator) : CommandModeBase(creator, "chanserv/deop") + { + this->SetDesc(_("Deops a selected nick on a channel")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + ChannelMode *cm = ModeManager::FindChannelModeByName(CMODE_OP); + + return do_util(source, this, cm, !params.empty() ? params[0] : "", params.size() > 1 ? params[1] : "", false, CA_OPDEOP, CA_OPDEOPME, CI_OPNOTICE); + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply("Deops a selected nick on a channel. If nick is not given,\n" + "it will deop you. If channel is not given, it will deop\n" + "you on every channel.\n" + " \n" + "By default, limited to AOPs or those with level 5 access \n" + "and above on the channel."); + return true; + } +}; + +class CommandCSVoice : public CommandModeBase +{ + public: + CommandCSVoice(Module *creator) : CommandModeBase(creator, "chanserv/voice") + { + this->SetDesc(_("Voices a selected nick on a channel")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + ChannelMode *cm = ModeManager::FindChannelModeByName(CMODE_VOICE); + + return do_util(source, this, cm, !params.empty() ? params[0] : "", params.size() > 1 ? params[1] : "", true, CA_VOICE, CA_VOICEME, CI_BEGIN); + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Voices a selected nick on a channel. If nick is not given,\n" + "it will voice you. If channel is not given, it will voice you\n" + "on every channel.\n" + " \n" + "By default, limited to AOPs or those with level 5 access \n" + "and above on the channel, or to VOPs or those with level 3 \n" + "and above for self voicing.")); + return true; + } +}; + +class CommandCSDeVoice : public CommandModeBase +{ + public: + CommandCSDeVoice(Module *creator) : CommandModeBase(creator, "chanserv/devoice") + { + this->SetDesc(_("Devoices a selected nick on a channel")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + ChannelMode *cm = ModeManager::FindChannelModeByName(CMODE_VOICE); + + return do_util(source, this, cm, !params.empty() ? params[0] : "", params.size() > 1 ? params[1] : "", false, CA_VOICE, CA_VOICEME, CI_BEGIN); + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Devoices a selected nick on a channel. If nick is not given,\n" + "it will devoice you. If channel is not given, it will devoice\n" + "you on every channel.\n" + " \n" + "By default, limited to AOPs or those with level 5 access \n" + "and above on the channel, or to VOPs or those with level 3 \n" + "and above for self devoicing.")); + return true; + } +}; + +class CommandCSHalfOp : public CommandModeBase +{ + public: + CommandCSHalfOp(Module *creator) : CommandModeBase(creator, "chanserv/halfop") + { + this->SetDesc(_("Halfops a selected nick on a channel")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + ChannelMode *cm = ModeManager::FindChannelModeByName(CMODE_HALFOP); + + if (!cm) + return; + + return do_util(source, this, cm, !params.empty() ? params[0] : "", params.size() > 1 ? params[1] : "", true, CA_HALFOP, CA_HALFOPME, CI_BEGIN); + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Halfops a selected nick on a channel. If nick is not given,\n" + "it will halfop you. If channel is not given, it will halfop\n" + "you on every channel.\n" + " \n" + "By default, limited to AOPs and those with level 5 access \n" + "and above on the channel, or to HOPs or those with level 4 \n")); + return true; + } +}; + +class CommandCSDeHalfOp : public CommandModeBase +{ + public: + CommandCSDeHalfOp(Module *creator) : CommandModeBase(creator, "chanserv/dehalfop") + { + this->SetDesc(_("Dehalfops a selected nick on a channel")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + ChannelMode *cm = ModeManager::FindChannelModeByName(CMODE_HALFOP); + + if (!cm) + return; + + return do_util(source, this, cm, !params.empty() ? params[0] : "", params.size() > 1 ? params[1] : "", false, CA_HALFOP, CA_HALFOPME, CI_BEGIN); + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Dehalfops a selected nick on a channel. If nick is not given,\n" + "it will dehalfop you. If channel is not given, it will dehalfop\n" + "you on every channel.\n" + " \n" + "By default, limited to AOPs and those with level 5 access \n" + "and above on the channel, or to HOPs or those with level 4 \n" + "and above for self dehalfopping.")); + return true; + } +}; + +class CommandCSProtect : public CommandModeBase +{ + public: + CommandCSProtect(Module *creator) : CommandModeBase(creator, "chanserv/protect") + { + this->SetDesc(_("Protects a selected nick on a channel")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + ChannelMode *cm = ModeManager::FindChannelModeByName(CMODE_PROTECT); + + if (!cm) + return; + + return do_util(source, this, cm, !params.empty() ? params[0] : "", params.size() > 1 ? params[1] : "", true, CA_PROTECT, CA_PROTECTME, CI_BEGIN); + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Protects a selected nick on a channel. If nick is not given,\n" + "it will protect you. If channel is not given, it will protect\n" + "you on every channel.\n" + " \n" + "By default, limited to the founder, or to SOPs or those with \n" + "level 10 and above on the channel for self protecting.")); + return true; + } +}; + +class CommandCSDeProtect : public CommandModeBase +{ + public: + CommandCSDeProtect(Module *creator) : CommandModeBase(creator, "chanserv/deprotect") + { + this->SetDesc(_("Deprotects a selected nick on a channel")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + ChannelMode *cm = ModeManager::FindChannelModeByName(CMODE_PROTECT); + + if (!cm) + return; + + return do_util(source, this, cm, !params.empty() ? params[0] : "", params.size() > 1 ? params[1] : "", false, CA_PROTECT, CA_PROTECTME, CI_BEGIN); + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Deprotects a selected nick on a channel. If nick is not given,\n" + "it will deprotect you. If channel is not given, it will deprotect\n" + "you on every channel.\n" + " \n" + "By default, limited to the founder, or to SOPs or those with \n")); + return true; + } +}; + +class CommandCSOwner : public CommandModeBase +{ + public: + CommandCSOwner(Module *creator) : CommandModeBase(module, "chanserv/owner") + { + this->SetDesc(_("Gives you owner status on channel")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + ChannelMode *cm = ModeManager::FindChannelModeByName(CMODE_OWNER); + + if (!cm) + return; + + return do_util(source, this, cm, !params.empty() ? params[0] : "", params.size() > 1 ? params[1] : "", true, CA_OWNER, CA_OWNERME, CI_BEGIN); + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Gives the selected nick owner status on \002channel\002. If nick is not\n" + "given, it will give you owner. If channel is not given, it will\n" + "give you owner on every channel.\n" + " \n" + "Limited to those with founder access on the channel.")); + return true; + } +}; + +class CommandCSDeOwner : public CommandModeBase +{ + public: + CommandCSDeOwner(Module *creator) : CommandModeBase(creator, "chanserv/deowner") + { + this->SetDesc(_("Removes your owner status on a channel")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + ChannelMode *cm = ModeManager::FindChannelModeByName(CMODE_OWNER); + + if (!cm) + return; + + return do_util(source, this, cm, !params.empty() ? params[0] : "", params.size() > 1 ? params[1] : "", false, CA_OWNER, CA_OWNERME, CI_BEGIN); + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Removes owner status from the selected nick on \002channel\002. If nick\n" + "is not given, it will deowner you. If channel is not given, it will\n" + "deowner you on every channel.\n" + " \n" + "Limited to those with founder access on the channel.")); + return true; + } +}; + +class CSModes : public Module +{ + CommandCSOwner commandcsowner; + CommandCSDeOwner commandcsdeowner; + CommandCSProtect commandcsprotect; + CommandCSDeProtect commandcsdeprotect; + CommandCSOp commandcsop; + CommandCSDeOp commandcsdeop; + CommandCSHalfOp commandcshalfop; + CommandCSDeHalfOp commandcsdehalfop; + CommandCSVoice commandcsvoice; + CommandCSDeVoice commandcsdevoice; + + public: + CSModes(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandcsowner(this), commandcsdeowner(this), commandcsprotect(this), commandcsdeprotect(this), + commandcsop(this), commandcsdeop(this), commandcshalfop(this), commandcsdehalfop(this), + commandcsvoice(this), commandcsdevoice(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandcsop); + ModuleManager::RegisterService(&commandcsdeop); + ModuleManager::RegisterService(&commandcsvoice); + ModuleManager::RegisterService(&commandcsdevoice); + + ModuleManager::RegisterService(&commandcsowner); + ModuleManager::RegisterService(&commandcsdeowner); + ModuleManager::RegisterService(&commandcsprotect); + ModuleManager::RegisterService(&commandcsdeprotect); + ModuleManager::RegisterService(&commandcshalfop); + ModuleManager::RegisterService(&commandcsdehalfop); + } +}; + +MODULE_INIT(CSModes) diff --git a/modules/commands/cs_register.cpp b/modules/commands/cs_register.cpp new file mode 100644 index 000000000..117212519 --- /dev/null +++ b/modules/commands/cs_register.cpp @@ -0,0 +1,193 @@ +/* ChanServ core functions + * + * (C) 2003-2011 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 ¶ms) + { + const Anope::string &chan = params[0]; + const Anope::string &chdesc = params.size() > 1 ? params[1] : ""; + + User *u = source.u; + Channel *c = findchan(params[0]); + ChannelInfo *ci = cs_findchan(params[0]); + + if (readonly) + source.Reply(_("Sorry, channel registration is temporarily disabled.")); + else if (u->Account()->HasFlag(NI_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 (!ircdproto->IsChannelValid(chan)) + source.Reply(CHAN_X_INVALID, chan.c_str()); + else if (ci) + source.Reply(_("Channel \002%s\002 is already registered!"), chan.c_str()); + else if (c && !c->HasUserStatus(u, CMODE_OP)) + source.Reply(_("You must be a channel operator to register the channel.")); + else if (Config->CSMaxReg && u->Account()->channelcount >= Config->CSMaxReg && !u->HasPriv("chanserv/no-register-limit")) + source.Reply(u->Account()->channelcount > Config->CSMaxReg ? CHAN_EXCEEDED_CHANNEL_LIMIT : _(CHAN_REACHED_CHANNEL_LIMIT), Config->CSMaxReg); + else + { + ci = new ChannelInfo(chan); + ci->SetFounder(u->Account()); + if (!chdesc.empty()) + 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.owner->nick; + + ci->bi = NULL; + Log(LOG_COMMAND, u, this, ci); + source.Reply(_("Channel \002%s\002 registered under your nickname: %s"), chan.c_str(), u->nick.c_str()); + + /* Implement new mode lock */ + if (c) + { + check_modes(c); + + ChannelMode *cm; + if (u->FindChannel(c) != NULL) + { + /* On most ircds you do not receive the admin/owner mode till its registered */ + if ((cm = ModeManager::FindChannelModeByName(CMODE_OWNER))) + c->SetMode(NULL, cm, u->nick); + else if ((cm = ModeManager::FindChannelModeByName(CMODE_PROTECT))) + c->RemoveMode(NULL, cm, u->nick); + } + + /* Mark the channel as persistant */ + if (c->HasMode(CMODE_PERM)) + ci->SetFlag(CI_PERSIST); + /* Persist may be in def cflags, set it here */ + else if (ci->HasFlag(CI_PERSIST) && (cm = ModeManager::FindChannelModeByName(CMODE_PERM))) + c->SetMode(NULL, CMODE_PERM); + } + + FOREACH_MOD(I_OnChanRegistered, OnChanRegistered(ci)); + } + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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.\n" + "See the \002ACCESS\002 command (\002%s%s HELP ACCESS\002) for\n" + "information on giving a subset of these privileges to\n" + "other channel users.\n" + " \n" + "NOTICE: In order to register a channel, you must have\n" + "first registered your nickname. If you haven't,\n" + "\002%s%s HELP\002 for information on how to do so."), + source.owner->nick.c_str(), source.owner->nick.c_str(), Config->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str(), Config->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str()); + return true; + } +}; + +class ExpireCallback : public CallBack +{ + public: + ExpireCallback(Module *owner) : CallBack(owner, Config->ExpireTimeout, Anope::CurTime, true) { } + + void Tick(time_t) + { + if (!Config->CSExpire || noexpire || 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 (ci->HasFlag(CI_SUSPENDED)) + { + if (Config->CSSuspendExpire && Anope::CurTime - ci->last_used >= Config->CSSuspendExpire) + expire = true; + } + else if (!ci->c && Config->CSExpire && Anope::CurTime - ci->last_used >= Config->CSExpire) + expire = true; + + if (ci->HasFlag(CI_NO_EXPIRE)) + expire = false; + + if (expire) + { + EventReturn MOD_RESULT; + FOREACH_RESULT(I_OnPreChanExpire, OnPreChanExpire(ci)); + if (MOD_RESULT == EVENT_STOP) + continue; + + Anope::string extra; + if (ci->HasFlag(CI_SUSPENDED)) + extra = "suspended "; + + Log(LOG_NORMAL, "chanserv/expire") << "Expiring " << extra << "channel " << ci->name << " (founder: " << (ci->GetFounder() ? ci->GetFounder()->display : "(none)") << ")"; + FOREACH_MOD(I_OnChanExpire, OnChanExpire(ci)); + delete ci; + } + } + } +}; + +class CSRegister : public Module +{ + CommandCSRegister commandcsregister; + ExpireCallback ecb; + + public: + CSRegister(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandcsregister(this), ecb(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandcsregister); + ModuleManager::Attach(I_OnDelChan, this); + } + + void OnDelChan(ChannelInfo *ci) + { + if (ci->c && ci->c->HasMode(CMODE_REGISTERED)) + ci->c->RemoveMode(NULL, CMODE_REGISTERED, "", false); + } +}; + +MODULE_INIT(CSRegister) diff --git a/modules/commands/cs_saset.cpp b/modules/commands/cs_saset.cpp new file mode 100644 index 000000000..699c5bc2c --- /dev/null +++ b/modules/commands/cs_saset.cpp @@ -0,0 +1,74 @@ +/* ChanServ core functions + * + * (C) 2003-2011 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 CommandCSSASet : public Command +{ + public: + CommandCSSASet(Module *creator) : Command(creator, "chanserv/saset", 2, 3) + { + this->SetDesc(_("Forcefully set channel options and information")); + this->SetSyntax(_("\037option\037 \037channel\037 \037parameters\037")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + this->OnSyntaxError(source, ""); + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Allows Services Operators to forcefully change settings\n" + "on channels.\n" + " \n" + "Available options:")); + Anope::string this_name = source.command; + for (BotInfo::command_map::iterator it = source.owner->commands.begin(), it_end = source.owner->commands.end(); it != it_end; ++it) + { + const Anope::string &c_name = it->first; + CommandInfo &info = it->second; + if (c_name.find_ci(this_name + " ") == 0) + { + service_reference command(info.name); + if (command) + { + source.command = it->first; + command->OnServHelp(source); + } + } + } + source.Reply(_("Type \002%s%s HELP SASET \037option\037\002 for more information on a\n" + "particular option."), Config->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str()); + return true; + } +}; + +class CSSASet : public Module +{ + CommandCSSASet commandcssaset; + + public: + CSSASet(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandcssaset(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandcssaset); + } +}; + +MODULE_INIT(CSSASet) diff --git a/modules/commands/cs_saset_noexpire.cpp b/modules/commands/cs_saset_noexpire.cpp new file mode 100644 index 000000000..1e085496f --- /dev/null +++ b/modules/commands/cs_saset_noexpire.cpp @@ -0,0 +1,81 @@ +/* ChanServ core functions + * + * (C) 2003-2011 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 CommandCSSASetNoexpire : public Command +{ + public: + CommandCSSASetNoexpire(Module *creator) : Command(creator, "chanserv/saset/noexpire", 2, 2) + { + this->SetDesc(_("Prevent the channel from expiring")); + this->SetDesc(_("\037channel\037 {ON | OFF}")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + ChannelInfo *ci = cs_findchan(params[0]); + if (ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); + return; + } + + if (source.permission.empty() && !ci->HasPriv(u, CA_SET)) + { + source.Reply(ACCESS_DENIED); + return; + } + + if (params[1].equals_ci("ON")) + { + ci->SetFlag(CI_NO_EXPIRE); + source.Reply(_("Channel %s \002will not\002 expire."), ci->name.c_str()); + } + else if (params[1].equals_ci("OFF")) + { + ci->UnsetFlag(CI_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 &) + { + 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 CSSetNoexpire : public Module +{ + CommandCSSASetNoexpire commandcssasetnoexpire; + + public: + CSSetNoexpire(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandcssasetnoexpire(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandcssasetnoexpire); + } +}; + +MODULE_INIT(CSSetNoexpire) diff --git a/modules/commands/cs_set.cpp b/modules/commands/cs_set.cpp new file mode 100644 index 000000000..65719ce91 --- /dev/null +++ b/modules/commands/cs_set.cpp @@ -0,0 +1,74 @@ +/* ChanServ core functions + * + * (C) 2003-2011 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 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 ¶ms) + { + this->OnSyntaxError(source, ""); + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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; + for (BotInfo::command_map::iterator it = source.owner->commands.begin(), it_end = source.owner->commands.end(); it != it_end; ++it) + { + const Anope::string &c_name = it->first; + CommandInfo &info = it->second; + if (c_name.find_ci(this_name + " ") == 0) + { + service_reference command(info.name); + if (command) + { + source.command = it->first; + command->OnServHelp(source); + } + } + } + source.Reply(_("Type \002%s%s HELP SET \037option\037\002 for more information on a\n" + "particular option."), Config->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str()); + return true; + } +}; + +class CSSet : public Module +{ + CommandCSSet commandcsset; + + public: + CSSet(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandcsset(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandcsset); + } +}; + +MODULE_INIT(CSSet) diff --git a/modules/commands/cs_set_bantype.cpp b/modules/commands/cs_set_bantype.cpp new file mode 100644 index 000000000..94d2aa151 --- /dev/null +++ b/modules/commands/cs_set_bantype.cpp @@ -0,0 +1,98 @@ +/* ChanServ core functions + * + * (C) 2003-2011 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 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 ¶ms) + { + User *u = source.u; + ChannelInfo *ci = cs_findchan(params[0]); + if (ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); + return; + } + + if (source.permission.empty() && !ci->HasPriv(u, CA_SET)) + { + source.Reply(ACCESS_DENIED); + return; + } + + try + { + int16 new_type = convertTo(params[1]); + if (new_type < 0 || new_type > 3) + throw ConvertException("Invalid range"); + 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()); + } + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &) + { + 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 CommandCSSASetBanType : public CommandCSSetBanType +{ + public: + CommandCSSASetBanType(Module *creator) : CommandCSSetBanType(creator, "chanserv/saset/bantype") + { + } +}; + +class CSSetBanType : public Module +{ + CommandCSSetBanType commandcssetbantype; + CommandCSSASetBanType commandcssasetbantype; + + public: + CSSetBanType(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandcssetbantype(this), commandcssasetbantype(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandcssetbantype); + ModuleManager::RegisterService(&commandcssasetbantype); + } +}; + +MODULE_INIT(CSSetBanType) diff --git a/modules/commands/cs_set_description.cpp b/modules/commands/cs_set_description.cpp new file mode 100644 index 000000000..5c2cd447d --- /dev/null +++ b/modules/commands/cs_set_description.cpp @@ -0,0 +1,89 @@ +/* ChanServ core functions + * + * (C) 2003-2011 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 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 ¶ms) + { + User *u = source.u; + ChannelInfo *ci = cs_findchan(params[0]); + if (ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); + return; + } + + if (source.permission.empty() && !ci->HasPriv(u, CA_SET)) + { + source.Reply(ACCESS_DENIED); + return; + } + + if (params.size() > 1) + { + ci->desc = params[1]; + source.Reply(_("Description of %s changed to \002%s\002."), ci->name.c_str(), ci->desc.c_str()); + } + else + { + ci->desc.clear(); + source.Reply(_("Description of %s unset."), ci->name.c_str()); + } + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &) + { + 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 CommandCSSASetDescription : public CommandCSSetDescription +{ + public: + CommandCSSASetDescription(Module *creator) : CommandCSSetDescription(creator, "chanserv/saset/description") + { + } +}; + +class CSSetDescription : public Module +{ + CommandCSSetDescription commandcssetdescription; + CommandCSSASetDescription commandcssasetdescription; + + public: + CSSetDescription(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandcssetdescription(this), commandcssasetdescription(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandcssetdescription); + ModuleManager::RegisterService(&commandcssasetdescription); + } +}; + +MODULE_INIT(CSSetDescription) diff --git a/modules/commands/cs_set_founder.cpp b/modules/commands/cs_set_founder.cpp new file mode 100644 index 000000000..2169af972 --- /dev/null +++ b/modules/commands/cs_set_founder.cpp @@ -0,0 +1,105 @@ +/* ChanServ core functions + * + * (C) 2003-2011 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 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 ¶ms) + { + User *u = source.u; + ChannelInfo *ci = cs_findchan(params[0]); + if (ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); + return; + } + + if (source.permission.empty() && !ci->HasPriv(u, CA_SET)) + { + source.Reply(ACCESS_DENIED); + return; + } + + if (source.permission.empty() && (ci->HasFlag(CI_SECUREFOUNDER) ? !IsFounder(u, ci) : !ci->HasPriv(u, CA_FOUNDER))) + { + source.Reply(ACCESS_DENIED); + return; + } + + NickAlias *na = findnick(params[1]); + + if (!na) + { + source.Reply(NICK_X_NOT_REGISTERED, params[1].c_str()); + return; + } + + NickCore *nc = na->nc; + if (Config->CSMaxReg && nc->channelcount >= Config->CSMaxReg && !u->HasPriv("chanserv/no-register-limit")) + { + source.Reply(_("\002%s\002 has too many channels registered."), na->nick.c_str()); + return; + } + + Log(!source.permission.empty() ? LOG_ADMIN : LOG_COMMAND, u, this, ci) << "to change the founder to " << nc->display; + + ci->SetFounder(nc); + + source.Reply(_("Founder of %s changed to \002%s\002."), ci->name.c_str(), na->nick.c_str()); + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &) + { + 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 CommandCSSASetFounder : public CommandCSSetFounder +{ + public: + CommandCSSASetFounder(Module *creator) : CommandCSSetFounder(creator, "chanserv/saset/founder") + { + } +}; + +class CSSetFounder : public Module +{ + CommandCSSetFounder commandcssetfounder; + CommandCSSASetFounder commandcssasetfounder; + + public: + CSSetFounder(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandcssetfounder(this), commandcssasetfounder(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandcssetfounder); + ModuleManager::RegisterService(&commandcssasetfounder); + } +}; + +MODULE_INIT(CSSetFounder) diff --git a/modules/commands/cs_set_keeptopic.cpp b/modules/commands/cs_set_keeptopic.cpp new file mode 100644 index 000000000..05578a2e5 --- /dev/null +++ b/modules/commands/cs_set_keeptopic.cpp @@ -0,0 +1,94 @@ +/* ChanServ core functions + * + * (C) 2003-2011 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 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 ¶ms) + { + User *u = source.u; + ChannelInfo *ci = cs_findchan(params[0]); + if (ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); + return; + } + + if (source.permission.empty() && !ci->HasPriv(u, CA_SET)) + { + source.Reply(ACCESS_DENIED); + return; + } + + if (params[1].equals_ci("ON")) + { + ci->SetFlag(CI_KEEPTOPIC); + source.Reply(_("Topic retention option for %s is now \002on\002."), ci->name.c_str()); + } + else if (params[1].equals_ci("OFF")) + { + ci->UnsetFlag(CI_KEEPTOPIC); + source.Reply(_("Topic retention option for %s is now \002off\002."), ci->name.c_str()); + } + else + this->OnSyntaxError(source, "KEEPTOPIC"); + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Enables or disables the \002topic retention\002 option for a \n" + "channel. When \002topic retention\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."), this->name.c_str(), source.owner->nick.c_str()); + return true; + } +}; + +class CommandCSSASetKeepTopic : public CommandCSSetKeepTopic +{ + public: + CommandCSSASetKeepTopic(Module *creator) : CommandCSSetKeepTopic(creator, "chanserv/saset/keeptopic") + { + } +}; + +class CSSetKeepTopic : public Module +{ + CommandCSSetKeepTopic commandcssetkeeptopic; + CommandCSSASetKeepTopic commandcssasetkeeptopic; + + public: + CSSetKeepTopic(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandcssetkeeptopic(this), commandcssasetkeeptopic(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandcssetkeeptopic); + ModuleManager::RegisterService(&commandcssasetkeeptopic); + } +}; + +MODULE_INIT(CSSetKeepTopic) diff --git a/modules/commands/cs_set_misc.cpp b/modules/commands/cs_set_misc.cpp new file mode 100644 index 000000000..8f145ab88 --- /dev/null +++ b/modules/commands/cs_set_misc.cpp @@ -0,0 +1,110 @@ +/* + * (C) 2003-2011 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 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 ¶ms) + { + ChannelInfo *ci = cs_findchan(params[0]); + if (ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); + return; + } + + ci->Shrink("cs_set_misc:" + source.command.replace_all_cs(" ", "_")); + if (params.size() > 1) + { + ci->Extend("cs_set_misc:" + source.command.replace_all_cs(" ", "_"), new ExtensibleItemRegular(params[1])); + source.Reply(CHAN_SETTING_CHANGED, source.command.c_str(), ci->name.c_str(), params[1].c_str()); + } + else + source.Reply(CHAN_SETTING_UNSET, source.command.c_str(), ci->name.c_str()); + } +}; + +class CommandCSSASetMisc : public CommandCSSetMisc +{ + public: + CommandCSSASetMisc(Module *creator) : CommandCSSetMisc(creator, "chanserv/saset/misc") + { + } +}; + +class CSSetMisc : public Module +{ + CommandCSSetMisc commandcssetmisc; + CommandCSSASetMisc commandcssasetmisc; + + public: + CSSetMisc(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, SUPPORTED), + commandcssetmisc(this), commandcssasetmisc(this) + { + this->SetAuthor("Anope"); + + Implementation i[] = { I_OnChanInfo, I_OnDatabaseWriteMetadata, I_OnDatabaseReadMetadata }; + ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); + + ModuleManager::RegisterService(&this->commandcssetmisc); + ModuleManager::RegisterService(&this->commandcssasetmisc); + } + + void OnChanInfo(CommandSource &source, ChannelInfo *ci, bool ShowHidden) + { + std::deque list; + ci->GetExtList(list); + + for (unsigned i = 0; i < list.size(); ++i) + { + if (list[i].find("cs_set_misc:") != 0) + continue; + + Anope::string value; + if (ci->GetExtRegular(list[i], value)) + source.Reply(" %s: %s", list[i].substr(12).replace_all_cs("_", " ").c_str(), value.c_str()); + } + } + + void OnDatabaseWriteMetadata(void (*WriteMetadata)(const Anope::string &, const Anope::string &), ChannelInfo *ci) + { + std::deque list; + ci->GetExtList(list); + + for (unsigned i = 0; i < list.size(); ++i) + { + if (list[i].find("cs_set_misc:") != 0) + continue; + + Anope::string value; + if (ci->GetExtRegular(list[i], value)) + WriteMetadata(list[i], ":" + value); + } + } + + EventReturn OnDatabaseReadMetadata(ChannelInfo *ci, const Anope::string &key, const std::vector ¶ms) + { + if (key.find("cs_set_misc:") == 0) + ci->Extend(key, new ExtensibleItemRegular(params[0])); + + return EVENT_CONTINUE; + } +}; + +MODULE_INIT(CSSetMisc) diff --git a/modules/commands/cs_set_opnotice.cpp b/modules/commands/cs_set_opnotice.cpp new file mode 100644 index 000000000..018b89437 --- /dev/null +++ b/modules/commands/cs_set_opnotice.cpp @@ -0,0 +1,93 @@ +/* ChanServ core functions + * + * (C) 2003-2011 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 CommandCSSetOpNotice : public Command +{ + public: + CommandCSSetOpNotice(Module *creator, const Anope::string &cname = "chanserv/set/notice") : Command(creator, cname, 2, 2) + { + this->SetDesc(_("Send a notice when OP/DEOP commands are used")); + this->SetSyntax(_("\037channel\037 {ON | OFF}")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + ChannelInfo *ci = cs_findchan(params[0]); + if (ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); + return; + } + + if (source.permission.empty() && !ci->HasPriv(u, CA_SET)) + { + source.Reply(ACCESS_DENIED); + return; + } + + if (params[1].equals_ci("ON")) + { + ci->SetFlag(CI_OPNOTICE); + source.Reply(_("Op-notice option for %s is now \002on\002."), ci->name.c_str()); + } + else if (params[1].equals_ci("OFF")) + { + ci->UnsetFlag(CI_OPNOTICE); + source.Reply(_("Op-notice option for %s is now \002off\002."), ci->name.c_str()); + } + else + this->OnSyntaxError(source, "OPNOTICE"); + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Enables or disables the \002op-notice\002 option for a channel.\n" + "When \002op-notice\002 is set, %s will send a notice to the\n" + "channel whenever the \002OP\002 or \002DEOP\002 commands are used for a user\n" + "in the channel."), this->name.c_str(), source.owner->nick.c_str()); + return true; + } +}; + +class CommandCSSASetOpNotice : public CommandCSSetOpNotice +{ + public: + CommandCSSASetOpNotice(Module *creator) : CommandCSSetOpNotice(creator, "chanserv/saset/opnotice") + { + } +}; + +class CSSetOpNotice : public Module +{ + CommandCSSetOpNotice commandcssetopnotice; + CommandCSSASetOpNotice commandcssasetopnotice; + + public: + CSSetOpNotice(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandcssetopnotice(this), commandcssasetopnotice(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandcssetopnotice); + ModuleManager::RegisterService(&commandcssasetopnotice); + } +}; + +MODULE_INIT(CSSetOpNotice) diff --git a/modules/commands/cs_set_peace.cpp b/modules/commands/cs_set_peace.cpp new file mode 100644 index 000000000..3a045fc6d --- /dev/null +++ b/modules/commands/cs_set_peace.cpp @@ -0,0 +1,93 @@ +/* ChanServ core functions + * + * (C) 2003-2011 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 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 ¶ms) + { + User *u = source.u; + ChannelInfo *ci = cs_findchan(params[0]); + if (ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); + return; + } + + if (source.permission.empty() && !ci->HasPriv(u, CA_SET)) + { + source.Reply(ACCESS_DENIED); + return; + } + + if (params[1].equals_ci("ON")) + { + ci->SetFlag(CI_PEACE); + source.Reply(_("Peace option for %s is now \002on\002."), ci->name.c_str()); + } + else if (params[1].equals_ci("OFF")) + { + ci->UnsetFlag(CI_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 &) + { + 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.owner->nick.c_str()); + return true; + } +}; + +class CommandCSSASetPeace : public CommandCSSetPeace +{ + public: + CommandCSSASetPeace(Module *creator) : CommandCSSetPeace(creator, "chanserv/saset/peace") + { + } +}; + +class CSSetPeace : public Module +{ + CommandCSSetPeace commandcssetpeace; + CommandCSSASetPeace commandcssasetpeace; + + public: + CSSetPeace(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandcssetpeace(this), commandcssasetpeace(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandcssetpeace); + ModuleManager::RegisterService(&commandcssasetpeace); + } +}; + +MODULE_INIT(CSSetPeace) diff --git a/modules/commands/cs_set_persist.cpp b/modules/commands/cs_set_persist.cpp new file mode 100644 index 000000000..3cc9564c4 --- /dev/null +++ b/modules/commands/cs_set_persist.cpp @@ -0,0 +1,180 @@ +/* ChanServ core functions + * + * (C) 2003-2011 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 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 ¶ms) + { + User *u = source.u; + ChannelInfo *ci = cs_findchan(params[0]); + if (ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); + return; + } + + if (source.permission.empty() && !ci->HasPriv(u, CA_SET)) + { + source.Reply(ACCESS_DENIED); + return; + } + + ChannelMode *cm = ModeManager::FindChannelModeByName(CMODE_PERM); + + if (params[1].equals_ci("ON")) + { + if (!ci->HasFlag(CI_PERSIST)) + { + ci->SetFlag(CI_PERSIST); + if (ci->c) + ci->c->SetFlag(CH_PERSIST); + + /* Channel doesn't exist, create it */ + if (!ci->c) + { + Channel *c = new Channel(ci->name); + if (ci->bi) + ci->bi->Join(c); + } + + /* No botserv bot, no channel mode */ + /* Give them ChanServ + * Yes, this works fine with no Config->s_BotServ + */ + if (!ci->bi && !cm) + { + BotInfo *bi = findbot(Config->ChanServ); + if (!bi) + { + source.Reply(_("ChanServ is required to enable persist on this network.")); + return; + } + bi->Assign(NULL, ci); + if (!ci->c->FindUser(bi)) + bi->Join(ci->c); + } + + /* Set the perm mode */ + if (cm) + { + if (ci->c && !ci->c->HasMode(CMODE_PERM)) + ci->c->SetMode(NULL, cm); + /* Add it to the channels mlock */ + ci->SetMLock(cm, true); + } + } + + source.Reply(_("Channel \002%s\002 is now persistant."), ci->name.c_str()); + } + else if (params[1].equals_ci("OFF")) + { + if (ci->HasFlag(CI_PERSIST)) + { + ci->UnsetFlag(CI_PERSIST); + if (ci->c) + ci->c->UnsetFlag(CH_PERSIST); + + /* Unset perm mode */ + if (cm) + { + if (ci->c && ci->c->HasMode(CMODE_PERM)) + ci->c->RemoveMode(NULL, cm); + /* Remove from mlock */ + ci->RemoveMLock(cm); + } + + /* No channel mode, no BotServ, but using ChanServ as the botserv bot + * which was assigned when persist was set on + */ + if (!cm && Config->BotServ.empty() && ci->bi) + { + BotInfo *bi = findbot(Config->ChanServ); + if (!bi) + { + source.Reply(_("ChanServ is required to enable persist on this network.")); + return; + } + /* Unassign bot */ + bi->UnAssign(NULL, ci); + } + } + + source.Reply(_("Channel \002%s\002 is no longer persistant."), ci->name.c_str()); + } + else + this->OnSyntaxError(source, "PERSIST"); + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Enables or disables the persistant channel setting.\n" + "When persistant is set, the service bot will remain\n" + "in the channel when it has emptied of users.\n" + " \n" + "If your IRCd does not a permanent (persistant) 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 BotServ enabled and does\n" + "not have a permanent channel mode, ChanServ 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 (persistant) channel mode\n" + "and is is set or unset (for any reason, including MLOCK),\n" + "persist is automatically set and unset for the channel aswell.\n" + "Additionally, services will set or unset this mode when you\n" + "set persist on or off.")); + return true; + } +}; + +class CommandCSSASetPersist : public CommandCSSetPersist +{ + public: + CommandCSSASetPersist(Module *creator) : CommandCSSetPersist(creator, "chanserv/saset/persist") + { + } +}; + +class CSSetPersist : public Module +{ + CommandCSSetPersist commandcssetpeace; + CommandCSSASetPersist commandcssasetpeace; + + public: + CSSetPersist(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandcssetpeace(this), commandcssasetpeace(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandcssetpeace); + ModuleManager::RegisterService(&commandcssasetpeace); + } +}; + +MODULE_INIT(CSSetPersist) diff --git a/modules/commands/cs_set_private.cpp b/modules/commands/cs_set_private.cpp new file mode 100644 index 000000000..108b11569 --- /dev/null +++ b/modules/commands/cs_set_private.cpp @@ -0,0 +1,93 @@ +/* ChanServ core functions + * + * (C) 2003-2011 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 CommandCSSetPrivate : public Command +{ + public: + CommandCSSetPrivate(Module *creator, const Anope::string &cname = "chanserv/set/private") : Command(creator, cname, 2, 2) + { + this->SetDesc(_("Hide channel from LIST command")); + this->SetSyntax(_("\037channel\037 {ON | OFF}")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + ChannelInfo *ci = cs_findchan(params[0]); + if (ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); + return; + } + + if (source.permission.empty() && !ci->HasPriv(u, CA_SET)) + { + source.Reply(ACCESS_DENIED); + return; + } + + if (params[1].equals_ci("ON")) + { + ci->SetFlag(CI_PRIVATE); + source.Reply(_("Private option for %s is now \002on\002."), ci->name.c_str()); + } + else if (params[1].equals_ci("OFF")) + { + ci->UnsetFlag(CI_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 &) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Enables or disables the \002private\002 option for a channel.\n" + "When \002private\002 is set, a \002%s%s LIST\002 will not\n" + "include the channel in any lists."), + Config->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str()); + return true; + } +}; + +class CommandCSSASetPrivate : public CommandCSSetPrivate +{ + public: + CommandCSSASetPrivate(Module *creator) : CommandCSSetPrivate(creator, "chanserv/saset/private") + { + } +}; + +class CSSetPrivate : public Module +{ + CommandCSSetPrivate commandcssetprivate; + CommandCSSASetPrivate commandcssasetprivate; + + public: + CSSetPrivate(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandcssetprivate(this), commandcssasetprivate(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandcssetprivate); + ModuleManager::RegisterService(&commandcssasetprivate); + } +}; + +MODULE_INIT(CSSetPrivate) diff --git a/modules/commands/cs_set_restricted.cpp b/modules/commands/cs_set_restricted.cpp new file mode 100644 index 000000000..5d675d2e7 --- /dev/null +++ b/modules/commands/cs_set_restricted.cpp @@ -0,0 +1,91 @@ +/* ChanServ core functions + * + * (C) 2003-2011 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 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 ¶ms) + { + User *u = source.u; + ChannelInfo *ci = cs_findchan(params[0]); + if (ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); + return; + } + + if (source.permission.empty() && !ci->HasPriv(u, CA_SET)) + { + source.Reply(ACCESS_DENIED); + return; + } + + if (params[1].equals_ci("ON")) + { + ci->SetFlag(CI_RESTRICTED); + source.Reply(_("Restricted access option for %s is now \002on\002."), ci->name.c_str()); + } + else if (params[1].equals_ci("OFF")) + { + ci->UnsetFlag(CI_RESTRICTED); + source.Reply(_("Restricted access option for %s is now \002off\002."), ci->name.c_str()); + } + else + this->OnSyntaxError(source, "RESTRICTED"); + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &) + { + 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 CommandCSSASetRestricted : public CommandCSSetRestricted +{ + public: + CommandCSSASetRestricted(Module *creator) : CommandCSSetRestricted(creator, "chanserv/saset/restricted") + { + } +}; + +class CSSetRestricted : public Module +{ + CommandCSSetRestricted commandcssetrestricted; + CommandCSSASetRestricted commandcssasetrestricted; + + public: + CSSetRestricted(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandcssetrestricted(this), commandcssasetrestricted(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandcssetrestricted); + ModuleManager::RegisterService(&commandcssasetrestricted); + } +}; + +MODULE_INIT(CSSetRestricted) diff --git a/modules/commands/cs_set_secure.cpp b/modules/commands/cs_set_secure.cpp new file mode 100644 index 000000000..26e8c5768 --- /dev/null +++ b/modules/commands/cs_set_secure.cpp @@ -0,0 +1,94 @@ +/* ChanServ core functions + * + * (C) 2003-2011 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 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 ¶ms) + { + User *u = source.u; + ChannelInfo *ci = cs_findchan(params[0]); + if (ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); + return; + } + + if (source.permission.empty() && !ci->HasPriv(u, CA_SET)) + { + source.Reply(ACCESS_DENIED); + return; + } + + if (params[1].equals_ci("ON")) + { + ci->SetFlag(CI_SECURE); + source.Reply(_("Secure option for %s is now \002on\002."), ci->name.c_str()); + } + else if (params[1].equals_ci("OFF")) + { + ci->UnsetFlag(CI_SECURE); + source.Reply(_("Secure option for %s is now \002off\002."), ci->name.c_str()); + } + else + this->OnSyntaxError(source, "SECURE"); + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Enables or disables security features for a\n" + "channel. When \002%s\002 is set, only users who have\n" + "registered their nicknames and IDENTIFY'd\n" + "with their password will be given access to the channel\n" + "as controlled by the access list."), this->name.c_str()); + return true; + } +}; + +class CommandCSSASetSecure : public CommandCSSetSecure +{ + public: + CommandCSSASetSecure(Module *creator) : CommandCSSetSecure(creator, "chanserv/saset/secure") + { + } +}; + +class CSSetSecure : public Module +{ + CommandCSSetSecure commandcssetsecure; + CommandCSSASetSecure commandcssasetsecure; + + public: + CSSetSecure(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandcssetsecure(this), commandcssasetsecure(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandcssetsecure); + ModuleManager::RegisterService(&commandcssasetsecure); + } +}; + +MODULE_INIT(CSSetSecure) diff --git a/modules/commands/cs_set_securefounder.cpp b/modules/commands/cs_set_securefounder.cpp new file mode 100644 index 000000000..7afc2e32d --- /dev/null +++ b/modules/commands/cs_set_securefounder.cpp @@ -0,0 +1,95 @@ +/* ChanServ core functions + * + * (C) 2003-2011 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 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 ¶ms) + { + User *u = source.u; + ChannelInfo *ci = cs_findchan(params[0]); + if (ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); + return; + } + + + if (source.permission.empty() && ci->HasFlag(CI_SECUREFOUNDER) ? !IsFounder(u, ci) : !ci->HasPriv(u, CA_FOUNDER)) + { + source.Reply(ACCESS_DENIED); + return; + } + + if (params[1].equals_ci("ON")) + { + ci->SetFlag(CI_SECUREFOUNDER); + source.Reply(_("Secure founder option for %s is now \002on\002."), ci->name.c_str()); + } + else if (params[1].equals_ci("OFF")) + { + ci->UnsetFlag(CI_SECUREFOUNDER); + source.Reply(_("Secure founder option for %s is now \002off\002."), ci->name.c_str()); + } + else + this->OnSyntaxError(source, "SECUREFOUNDER"); + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &) + { + 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 password, its founder and its\n" + "successor, and not those who have founder level access through\n" + "the access/qop command.")); + return true; + } +}; + +class CommandCSSASetSecureFounder : public CommandCSSetSecureFounder +{ + public: + CommandCSSASetSecureFounder(Module *creator) : CommandCSSetSecureFounder(creator, "chanserv/saset/securefounder") + { + } +}; + +class CSSetSecureFounder : public Module +{ + CommandCSSetSecureFounder commandcssetsecurefounder; + CommandCSSASetSecureFounder commandcssasetsecurefounder; + + public: + CSSetSecureFounder(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandcssetsecurefounder(this), commandcssasetsecurefounder(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandcssetsecurefounder); + ModuleManager::RegisterService(&commandcssasetsecurefounder); + } +}; + +MODULE_INIT(CSSetSecureFounder) diff --git a/modules/commands/cs_set_secureops.cpp b/modules/commands/cs_set_secureops.cpp new file mode 100644 index 000000000..4c3c54b05 --- /dev/null +++ b/modules/commands/cs_set_secureops.cpp @@ -0,0 +1,92 @@ +/* ChanServ core functions + * + * (C) 2003-2011 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 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 ¶ms) + { + User *u = source.u; + ChannelInfo *ci = cs_findchan(params[0]); + if (ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); + return; + } + + if (source.permission.empty() && !ci->HasPriv(u, CA_SET)) + { + source.Reply(ACCESS_DENIED); + return; + } + + if (params[1].equals_ci("ON")) + { + ci->SetFlag(CI_SECUREOPS); + source.Reply(_("Secure ops option for %s is now \002on\002."), ci->name.c_str()); + } + else if (params[1].equals_ci("OFF")) + { + ci->UnsetFlag(CI_SECUREOPS); + source.Reply(_("Secure ops option for %s is now \002off\002."), ci->name.c_str()); + } + else + this->OnSyntaxError(source, "SECUREOPS"); + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &) + { + 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 userlist\n" + "will not be allowed chanop status.")); + return true; + } +}; + +class CommandCSSASetSecureOps : public CommandCSSetSecureOps +{ + public: + CommandCSSASetSecureOps(Module *creator) : CommandCSSetSecureOps(creator, "chanserv/saset/secureops") + { + } +}; + +class CSSetSecureOps : public Module +{ + CommandCSSetSecureOps commandcssetsecureops; + CommandCSSASetSecureOps commandcssasetsecureops; + + public: + CSSetSecureOps(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandcssetsecureops(this), commandcssasetsecureops(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandcssetsecureops); + ModuleManager::RegisterService(&commandcssasetsecureops); + } +}; + +MODULE_INIT(CSSetSecureOps) diff --git a/modules/commands/cs_set_signkick.cpp b/modules/commands/cs_set_signkick.cpp new file mode 100644 index 000000000..0d421576d --- /dev/null +++ b/modules/commands/cs_set_signkick.cpp @@ -0,0 +1,104 @@ +/* ChanServ core functions + * + * (C) 2003-2011 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 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 KICK command")); + this->SetSyntax(_("\037channel\037 SIGNKICK {ON | LEVEL | OFF}")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + ChannelInfo *ci = cs_findchan(params[0]); + if (ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); + return; + } + + if (source.permission.empty() && !ci->HasPriv(u, CA_SET)) + { + source.Reply(ACCESS_DENIED); + return; + } + + if (params[1].equals_ci("ON")) + { + ci->SetFlag(CI_SIGNKICK); + ci->UnsetFlag(CI_SIGNKICK_LEVEL); + source.Reply(_("Signed kick option for %s is now \002on\002."), ci->name.c_str()); + } + else if (params[1].equals_ci("LEVEL")) + { + ci->SetFlag(CI_SIGNKICK_LEVEL); + ci->UnsetFlag(CI_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()); + } + else if (params[1].equals_ci("OFF")) + { + ci->UnsetFlag(CI_SIGNKICK); + ci->UnsetFlag(CI_SIGNKICK_LEVEL); + source.Reply(_("Signed kick option for %s is now \002off\002."), ci->name.c_str()); + } + else + this->OnSyntaxError(source, "SIGNKICK"); + } + + bool OnHelp(CommandSource &source, const Anope::string &) + { + 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" + "KICK 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 CommandCSSASetSignKick : public CommandCSSetSignKick +{ + public: + CommandCSSASetSignKick(Module *creator) : CommandCSSetSignKick(creator, "chanserv/saset/signkick") + { + } +}; + +class CSSetSignKick : public Module +{ + CommandCSSetSignKick commandcssetsignkick; + CommandCSSASetSignKick commandcssasetsignkick; + + public: + CSSetSignKick(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandcssetsignkick(this), commandcssasetsignkick(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandcssetsignkick); + ModuleManager::RegisterService(&commandcssasetsignkick); + } +}; + +MODULE_INIT(CSSetSignKick) diff --git a/modules/commands/cs_set_successor.cpp b/modules/commands/cs_set_successor.cpp new file mode 100644 index 000000000..f7ed8dba9 --- /dev/null +++ b/modules/commands/cs_set_successor.cpp @@ -0,0 +1,119 @@ +/* ChanServ core functions + * + * (C) 2003-2011 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 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 ¶ms) + { + User *u = source.u; + ChannelInfo *ci = cs_findchan(params[0]); + if (ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); + return; + } + + if (source.permission.empty() && !ci->HasPriv(u, CA_SET)) + { + source.Reply(ACCESS_DENIED); + return; + } + + if (source.permission.empty() && ci->HasFlag(CI_SECUREFOUNDER) ? !IsFounder(u, ci) : !ci->HasPriv(u, CA_FOUNDER)) + { + source.Reply(ACCESS_DENIED); + return; + } + + NickCore *nc; + + if (params.size() > 1) + { + NickAlias *na = findnick(params[1]); + + if (!na) + { + source.Reply(NICK_X_NOT_REGISTERED, params[1].c_str()); + return; + } + if (na->nc == ci->GetFounder()) + { + source.Reply(_("%s cannot be the successor on channel %s they are the founder."), na->nick.c_str(), ci->name.c_str()); + return; + } + nc = na->nc; + } + else + nc = NULL; + + Log(!source.permission.empty() ? LOG_ADMIN : LOG_COMMAND, u, this, ci) << "to change the successor from " << (ci->successor ? ci->successor->display : "none") << " to " << (nc ? nc->display : "none"); + + ci->successor = nc; + + if (nc) + source.Reply(_("Successor for %s 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 &) + { + 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. 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. The new\n" + "nickname must be a registered one."), Config->CSMaxReg); + return true; + } +}; + +class CommandCSSASetSuccessor : public CommandCSSetSuccessor +{ + public: + CommandCSSASetSuccessor(Module *creator) : CommandCSSetSuccessor(creator, "chanserv/saset/successor") + { + } +}; + +class CSSetSuccessor : public Module +{ + CommandCSSetSuccessor commandcssetsuccessor; + CommandCSSASetSuccessor commandcssasetsuccessor; + + public: + CSSetSuccessor(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandcssetsuccessor(this), commandcssasetsuccessor(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandcssetsuccessor); + ModuleManager::RegisterService(&commandcssasetsuccessor); + } +}; + +MODULE_INIT(CSSetSuccessor) diff --git a/modules/commands/cs_set_topiclock.cpp b/modules/commands/cs_set_topiclock.cpp new file mode 100644 index 000000000..e670385ba --- /dev/null +++ b/modules/commands/cs_set_topiclock.cpp @@ -0,0 +1,92 @@ +/* ChanServ core functions + * + * (C) 2003-2011 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 CommandCSSetTopicLock : public Command +{ + public: + CommandCSSetTopicLock(Module *creator, const Anope::string &cname = "chanserv/set/topiclock") : Command(creator, cname, 2, 2) + { + this->SetDesc(_("Topic can only be changed with TOPIC")); + this->SetSyntax(_("\037channel\037 {ON | OFF}")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + ChannelInfo *ci = cs_findchan(params[0]); + if (ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); + return; + } + + if (source.permission.empty() && !ci->HasPriv(u, CA_SET)) + { + source.Reply(ACCESS_DENIED); + return; + } + + if (params[1].equals_ci("ON")) + { + ci->SetFlag(CI_TOPICLOCK); + source.Reply(_("Topic lock option for %s is now \002on\002."), ci->name.c_str()); + } + else if (params[1].equals_ci("OFF")) + { + ci->UnsetFlag(CI_TOPICLOCK); + source.Reply(_("Topic lock option for %s is now \002off\002."), ci->name.c_str()); + } + else + this->OnSyntaxError(source, "TOPICLOCK"); + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Enables or disables the \002topic lock\002 option for a channel.\n" + "When \002topic lock\002 is set, the channel topic will be unchangable\n" + " except via the \002TOPIC\002 command.")); + return true; + } +}; + +class CommandCSSASetTopicLock : public CommandCSSetTopicLock +{ + public: + CommandCSSASetTopicLock(Module *creator) : CommandCSSetTopicLock(creator, "chanserv/saset/topiclock") + { + } +}; + +class CSSetTopicLock : public Module +{ + CommandCSSetTopicLock commandcssettopiclock; + CommandCSSASetTopicLock commandcssasettopiclock; + + public: + CSSetTopicLock(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandcssettopiclock(this), commandcssasettopiclock(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandcssettopiclock); + ModuleManager::RegisterService(&commandcssasettopiclock); + } +}; + +MODULE_INIT(CSSetTopicLock) diff --git a/modules/commands/cs_suspend.cpp b/modules/commands/cs_suspend.cpp new file mode 100644 index 000000000..d59ffb44b --- /dev/null +++ b/modules/commands/cs_suspend.cpp @@ -0,0 +1,158 @@ +/* ChanServ core functions + * + * (C) 2003-2011 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 CommandCSSuspend : public Command +{ + public: + CommandCSSuspend(Module *creator) : Command(creator, "chanserv/suspend", 1, 2) + { + this->SetDesc(_("Prevent a channel from being used preserving channel data and settings")); + this->SetSyntax(_("\037channel\037 [\037reason\037]")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + const Anope::string &reason = params.size() > 1 ? params[1] : ""; + + User *u = source.u; + + if (Config->ForceForbidReason && reason.empty()) + { + this->OnSyntaxError(source, ""); + return; + } + + if (readonly) + source.Reply(READ_ONLY_MODE); + + ChannelInfo *ci = cs_findchan(params[0]); + if (ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); + return; + } + + ci->SetFlag(CI_SUSPENDED); + ci->Extend("suspend_by", new ExtensibleItemRegular(u->nick)); + if (!reason.empty()) + ci->Extend("suspend_reason", new ExtensibleItemRegular(u->nick)); + + if (ci->c) + { + for (CUserList::iterator it = ci->c->users.begin(), it_end = ci->c->users.end(); it != it_end; ) + { + UserContainer *uc = *it++; + + if (uc->user->HasMode(UMODE_OPER)) + continue; + + ci->c->Kick(NULL, uc->user, "%s", !reason.empty() ? reason.c_str() : translate(uc->user, _("This channel has been suspended."))); + } + } + + Log(LOG_ADMIN, u, this, ci) << (!reason.empty() ? reason : "No reason"); + source.Reply(_("Channel \002%s\002 is now suspended."), ci->name.c_str()); + + FOREACH_MOD(I_OnChanSuspend, OnChanSuspend(ci)); + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Disallows anyone from using the given channel.\n" + "May be cancelled by using the UNSUSPEND\n" + "command to preserve all previous channel data/settings.\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 ¶ms) + { + User *u = source.u; + + if (readonly) + source.Reply(READ_ONLY_MODE); + + ChannelInfo *ci = cs_findchan(params[0]); + if (ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); + return; + } + + /* Only UNSUSPEND already suspended channels */ + if (!ci->HasFlag(CI_SUSPENDED)) + { + source.Reply(_("Couldn't release channel \002%s\002!"), ci->name.c_str()); + return; + } + + Anope::string by, reason; + ci->GetExtRegular("suspend_by", by); + ci->GetExtRegular("suspend_reason", reason); + Log(LOG_ADMIN, u, this, ci) << " which was suspended by " << by << " for: " << (!reason.empty() ? reason : "No reason"); + + ci->UnsetFlag(CI_SUSPENDED); + ci->Shrink("suspend_by"); + ci->Shrink("suspend_reason"); + + source.Reply(_("Channel \002%s\002 is now released."), ci->name.c_str()); + + FOREACH_MOD(I_OnChanUnsuspend, OnChanUnsuspend(ci)); + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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; + + public: + CSSuspend(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandcssuspend(this), commandcsunsuspend(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandcssuspend); + ModuleManager::RegisterService(&commandcsunsuspend); + } +}; + +MODULE_INIT(CSSuspend) diff --git a/modules/commands/cs_sync.cpp b/modules/commands/cs_sync.cpp new file mode 100644 index 000000000..2937cc4fc --- /dev/null +++ b/modules/commands/cs_sync.cpp @@ -0,0 +1,63 @@ +/* + * + * (C) 2003-2011 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 ¶ms) + { + ChannelInfo *ci = cs_findchan(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 + { + for (CUserList::iterator it = ci->c->users.begin(), it_end = ci->c->users.end(); it != it_end; ++it) + chan_set_correct_modes((*it)->user, ci->c, 1); + + source.Reply(_("All user modes on \2%s\2 have been synced."), ci->name.c_str()); + } + } + + bool OnHelp(CommandSource &source, const Anope::string ¶ms) + { + 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, SUPPORTED), + commandcssync(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandcssync); + } +}; + +MODULE_INIT(CSSync) diff --git a/modules/commands/cs_tban.cpp b/modules/commands/cs_tban.cpp new file mode 100644 index 000000000..404279bef --- /dev/null +++ b/modules/commands/cs_tban.cpp @@ -0,0 +1,116 @@ +/* cs_tban.c - Bans the user for a given length of time + * + * (C) 2003-2011 Anope Team + * Contact us at team@anope.org + * + * Based on the original module by Rob + * Included in the Anope module pack since Anope 1.7.8 + * Anope Coder: Rob + * + * 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 Module *me; + +class TempBan : public CallBack +{ + private: + dynamic_reference chan; + Anope::string mask; + + public: + TempBan(time_t seconds, Channel *c, const Anope::string &banmask) : CallBack(me, seconds), chan(c), mask(banmask) { } + + void Tick(time_t ctime) + { + if (chan && chan->ci) + chan->RemoveMode(NULL, CMODE_BAN, mask); + } +}; + +static bool CanBanUser(CommandSource &source, Channel *c, User *u2) +{ + User *u = source.u; + ChannelInfo *ci = c->ci; + bool ok = false; + if (!ci->HasPriv(u, CA_BAN)) + source.Reply(ACCESS_DENIED); + else if (matches_list(c, u2, CMODE_EXCEPT)) + source.Reply(CHAN_EXCEPTED, u2->nick.c_str(), ci->name.c_str()); + else if (u2->IsProtected()) + source.Reply(ACCESS_DENIED); + else + ok = true; + + return ok; +} + +class CommandCSTBan : public Command +{ + public: + CommandCSTBan(Module *m) : Command(m, "TBAN", 3, 3) + { + this->SetDesc(_("Bans the user for a given length of time")); + this->SetSyntax(_("\037channel\037 \037nick\037 \037time\037")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + Channel *c = findchan(params[0]); + + const Anope::string &nick = params[1]; + const Anope::string &time = params[2]; + + User *u2; + if (!c) + source.Reply(CHAN_X_NOT_IN_USE, params[0].c_str()); + else if (!(u2 = finduser(nick))) + source.Reply(NICK_X_NOT_IN_USE, nick.c_str()); + else + if (CanBanUser(source, c, u2)) + { + Anope::string mask; + get_idealban(c->ci, u2, mask); + c->SetMode(NULL, CMODE_BAN, mask); + new TempBan(dotime(time), c, mask); + source.Reply(_("%s banned from %s, will auto-expire in %s"), mask.c_str(), c->name.c_str(), time.c_str()); + } + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->OnSyntaxError(source, ""); + source.Reply(" "); + source.Reply(_("Bans the given user from a channel for a specified length of\n" + "time. If the ban is removed before by hand, it will NOT be replaced.")); + + return true; + } +}; + +class CSTBan : public Module +{ + CommandCSTBan commandcstban; + + public: + CSTBan(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, SUPPORTED), + commandcstban(this) + { + this->SetAuthor("Anope"); + + me = this; + + ModuleManager::RegisterService(&commandcstban); + } +}; + +MODULE_INIT(CSTBan) diff --git a/modules/commands/cs_topic.cpp b/modules/commands/cs_topic.cpp new file mode 100644 index 000000000..1b5de11c3 --- /dev/null +++ b/modules/commands/cs_topic.cpp @@ -0,0 +1,84 @@ +/* ChanServ core functions + * + * (C) 2003-2011 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 CommandCSTopic : public Command +{ + public: + CommandCSTopic(Module *creator) : Command(creator, "chanserv/topic", 1, 2) + { + this->SetDesc(_("Manipulate the topic of the specified channel")); + this->SetSyntax(_("\037channel\037 [\037topic\037]")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + const Anope::string &topic = params.size() > 1 ? params[1] : ""; + + User *u = source.u; + + ChannelInfo *ci = cs_findchan(params[0]); + if (ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); + return; + } + + if (!ci->c) + source.Reply(CHAN_X_NOT_IN_USE, ci->name.c_str()); + else if (!ci->HasPriv(u, CA_TOPIC) && !u->HasCommand("chanserv/chanserv/topic")) + source.Reply(ACCESS_DENIED); + else + { + bool has_topiclock = ci->HasFlag(CI_TOPICLOCK); + ci->UnsetFlag(CI_TOPICLOCK); + ci->c->ChangeTopic(u->nick, topic, Anope::CurTime); + if (has_topiclock) + ci->SetFlag(CI_TOPICLOCK); + + bool override = !ci->HasPriv(u, CA_TOPIC); + Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "to change the topic to " << (!topic.empty() ? topic : "No topic"); + } + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Causes %s to set the channel topic to the one\n" + "specified. If \002topic\002 is not given, then an empty topic\n" + "is set. This command is most useful in conjunction\n" + "with topic lock.\n" + "By default, limited to those with founder access on the\n" + "channel."), source.owner->nick.c_str()); + return true; + } +}; + +class CSTopic : public Module +{ + CommandCSTopic commandcstopic; + + public: + CSTopic(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandcstopic(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandcstopic); + } +}; + +MODULE_INIT(CSTopic) diff --git a/modules/commands/cs_unban.cpp b/modules/commands/cs_unban.cpp new file mode 100644 index 000000000..cc3c68e91 --- /dev/null +++ b/modules/commands/cs_unban.cpp @@ -0,0 +1,94 @@ +/* ChanServ core functions + * + * (C) 2003-2011 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 CommandCSUnban : public Command +{ + public: + CommandCSUnban(Module *creator) : Command(creator, "chanserv/unban", 1, 2) + { + this->SetDesc(_("Remove all bans preventing a user from entering a channel")); + this->SetSyntax(_("\037channel\037 [\037nick\037]")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + + ChannelInfo *ci = cs_findchan(params[0]); + if (ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); + return; + } + + if (ci->c == NULL) + { + source.Reply(CHAN_X_NOT_IN_USE, ci->name.c_str()); + return; + } + + if (!ci->HasPriv(u, CA_UNBAN)) + { + source.Reply(ACCESS_DENIED); + return; + } + + User *u2 = u; + if (params.size() > 1) + u2 = finduser(params[1]); + + if (!u2) + { + source.Reply(NICK_X_NOT_IN_USE, params[1].c_str()); + return; + } + + common_unban(ci, u2, u == u2); + if (u2 == u) + source.Reply(_("You have been unbanned from \002%s\002."), ci->c->name.c_str()); + else + source.Reply(_("\002%s\002 has been unbanned from \002%s\002."), u2->nick.c_str(), ci->c->name.c_str()); + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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. \n" + " \n" + "By default, limited to AOPs or those with level 5 and above\n" + "on the channel."), source.owner->nick.c_str()); + return true; + } +}; + +class CSUnban : public Module +{ + CommandCSUnban commandcsunban; + + public: + CSUnban(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandcsunban(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandcsunban); + } +}; + +MODULE_INIT(CSUnban) diff --git a/modules/commands/cs_xop.cpp b/modules/commands/cs_xop.cpp new file mode 100644 index 000000000..1b9348026 --- /dev/null +++ b/modules/commands/cs_xop.cpp @@ -0,0 +1,880 @@ +/* ChanServ core functions + * + * (C) 2003-2011 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" + +enum XOPType +{ + XOP_QOP, + XOP_SOP, + XOP_AOP, + XOP_HOP, + XOP_VOP, + XOP_UNKNOWN +}; + +static struct XOPAccess +{ + XOPType type; + Anope::string name; + ChannelAccess access[CA_SIZE]; +} xopAccess[] = { + { XOP_QOP, "QOP", + { + CA_SIGNKICK, + CA_SET, + CA_AUTOOWNER, + CA_OWNERME, + CA_PROTECT, + CA_INFO, + CA_SIZE + } + }, + { XOP_SOP, "SOP", + { + CA_AUTOPROTECT, + CA_AKICK, + CA_BADWORDS, + CA_ASSIGN, + CA_MEMO, + CA_ACCESS_CHANGE, + CA_PROTECTME, + CA_OPDEOP, + CA_SIZE + } + }, + { XOP_AOP, "AOP", + { + CA_TOPIC, + CA_MODE, + CA_GETKEY, + CA_INVITE, + CA_UNBAN, + CA_AUTOOP, + CA_OPDEOPME, + CA_HALFOP, + CA_SAY, + CA_NOKICK, + CA_SIZE + } + }, + { XOP_HOP, "HOP", + { + CA_AUTOHALFOP, + CA_HALFOPME, + CA_KICK, + CA_BAN, + CA_FANTASIA, + CA_SIZE + } + }, + { XOP_VOP, "VOP", + { + CA_AUTOVOICE, + CA_VOICEME, + CA_ACCESS_LIST, + CA_SIZE + } + }, + { XOP_UNKNOWN, "", { } + } +}; + +class XOPChanAccess : public ChanAccess +{ + public: + XOPType type; + + XOPChanAccess(AccessProvider *p) : ChanAccess(p) + { + } + + bool Matches(User *u, NickCore *nc) + { + if (u && (Anope::Match(u->nick, this->mask) || Anope::Match(u->GetMask(), this->mask))) + return true; + else if (nc && Anope::Match(nc->display, this->mask)) + return true; + return false; + } + + bool HasPriv(ChannelAccess priv) + { + for (int i = 0; xopAccess[i].type != XOP_UNKNOWN; ++i) + { + XOPAccess &x = xopAccess[i]; + + if (this->type > x.type) + continue; + + for (int j = 0; x.access[j] != CA_SIZE; ++j) + if (x.access[j] == priv) + return true; + } + + return false; + } + + Anope::string Serialize() + { + for (int i = 0; xopAccess[i].type != XOP_UNKNOWN; ++i) + { + XOPAccess &x = xopAccess[i]; + + if (this->type == x.type) + return x.name; + } + + return ""; + } + + void Unserialize(const Anope::string &data) + { + for (int i = 0; xopAccess[i].type != XOP_UNKNOWN; ++i) + { + XOPAccess &x = xopAccess[i]; + + if (data == x.name) + { + this->type = x.type; + return; + } + } + + this->type = XOP_UNKNOWN; + } + + static XOPType DetermineLevel(ChanAccess *access) + { + if (access->provider->name == "access/xop") + { + XOPChanAccess *xaccess = debug_cast(access); + return xaccess->type; + } + else + { + int count[XOP_UNKNOWN]; + for (int i = 0; i < XOP_UNKNOWN; ++i) + count[i] = 0; + + for (int i = 0; xopAccess[i].type != XOP_UNKNOWN; ++i) + { + XOPAccess &x = xopAccess[i]; + + for (int j = 0; x.access[j] != CA_SIZE; ++j) + if (access->HasPriv(x.access[j])) + ++count[x.type]; + } + + XOPType max = XOP_UNKNOWN; + int maxn = 0; + for (int i = 0; i < XOP_UNKNOWN; ++i) + if (count[i] > maxn) + { + max = static_cast(i); + maxn = count[i]; + } + + return max; + } + } +}; + +class XOPAccessProvider : public AccessProvider +{ + public: + XOPAccessProvider(Module *o) : AccessProvider(o, "access/xop") + { + } + + ChanAccess *Create() + { + return new XOPChanAccess(this); + } +}; + +class XOPListCallback : public NumberList +{ + CommandSource &source; + ChannelInfo *ci; + XOPType type; + Command *c; + bool SentHeader; + public: + XOPListCallback(CommandSource &_source, ChannelInfo *_ci, const Anope::string &numlist, XOPType _type, Command *_c) : NumberList(numlist, false), source(_source), ci(_ci), type(_type), c(_c), SentHeader(false) + { + } + + void HandleNumber(unsigned Number) + { + if (!Number || Number > ci->GetAccessCount()) + return; + + ChanAccess *access = ci->GetAccess(Number - 1); + + if (this->type != XOPChanAccess::DetermineLevel(access)) + return; + + if (!SentHeader) + { + SentHeader = true; + source.Reply(_("%s list for %s:\n Num Nick"), source.command.c_str(), ci->name.c_str()); + } + + DoList(source, access, Number - 1, this->type); + } + + static void DoList(CommandSource &source, ChanAccess *access, unsigned index, XOPType type) + { + source.Reply(_(" %3d %s"), index, access->mask.c_str()); + } +}; + +class XOPDelCallback : public NumberList +{ + CommandSource &source; + ChannelInfo *ci; + Command *c; + unsigned Deleted; + Anope::string Nicks; + bool override; + XOPType type; + public: + XOPDelCallback(CommandSource &_source, ChannelInfo *_ci, Command *_c, bool _override, XOPType _type, const Anope::string &numlist) : NumberList(numlist, true), source(_source), ci(_ci), c(_c), Deleted(0), override(_override), type(_type) + { + } + + ~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.u, c, ci) << "deleted access of users " << 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) + { + if (!Number || Number > ci->GetAccessCount()) + return; + + ChanAccess *access = ci->GetAccess(Number - 1); + + if (this->type != XOPChanAccess::DetermineLevel(access)) + return; + + ++Deleted; + if (!Nicks.empty()) + Nicks += ", " + access->mask; + else + Nicks = access->mask; + + FOREACH_MOD(I_OnAccessDel, OnAccessDel(ci, source.u, access)); + + ci->EraseAccess(Number - 1); + } +}; + +class XOPBase : public Command +{ + private: + void DoAdd(CommandSource &source, ChannelInfo *ci, const std::vector ¶ms, XOPType level) + { + User *u = source.u; + + Anope::string mask = params.size() > 2 ? params[2] : ""; + + if (mask.empty()) + { + this->OnSyntaxError(source, "ADD"); + return; + } + + if (readonly) + { + source.Reply(_("Sorry, channel %s list modification is temporarily disabled."), source.command.c_str()); + return; + } + + AccessGroup access = ci->AccessFor(u); + ChanAccess *highest = access.Highest(); + int u_level = (highest ? XOPChanAccess::DetermineLevel(highest) : 0); + + if (((access.HasPriv(CA_FOUNDER) || level >= u_level) || !access.HasPriv(CA_ACCESS_CHANGE)) && !u->HasPriv("chanserv/access/modify")) + { + source.Reply(ACCESS_DENIED); + return; + } + + for (unsigned i = 0; i < ci->GetAccessCount(); ++i) + { + ChanAccess *a = ci->GetAccess(i); + + if (a->mask.equals_ci(mask)) + { + if (XOPChanAccess::DetermineLevel(a) >= u_level && !u->HasPriv("chanserv/access/modify")) + { + source.Reply(ACCESS_DENIED); + return; + } + + ci->EraseAccess(i); + break; + } + } + + if (ci->GetAccessCount() >= Config->CSAccessMax) + { + source.Reply(_("Sorry, you can only have %d %s entries on a channel."), Config->CSAccessMax, source.command.c_str()); + return; + } + + if (mask.find_first_of("!*@") == Anope::string::npos && findnick(mask) == NULL) + mask += "!*@*"; + + service_reference provider("access/xop"); + if (!provider) + return; + XOPChanAccess *acc = debug_cast(provider->Create()); + acc->ci = ci; + acc->mask = mask; + acc->creator = u->nick; + acc->type = level; + acc->last_seen = 0; + acc->created = Anope::CurTime; + ci->AddAccess(acc); + + bool override = level >= u_level || !access.HasPriv(CA_ACCESS_CHANGE); + Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "ADD " << mask << " as level " << level; + + FOREACH_MOD(I_OnAccessAdd, OnAccessAdd(ci, u, 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 ¶ms, XOPType level) + { + User *u = source.u; + + const Anope::string &mask = params.size() > 2 ? params[2] : ""; + + if (mask.empty()) + { + this->OnSyntaxError(source, "DEL"); + return; + } + + if (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; + } + + AccessGroup access = ci->AccessFor(u); + ChanAccess *highest = access.Highest(); + bool override = false; + if (!mask.equals_ci(u->Account()->display) && !access.HasPriv(CA_ACCESS_CHANGE) && (!highest || level >= XOPChanAccess::DetermineLevel(highest))) + { + if (u->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) + { + XOPDelCallback list(source, ci, this, override, level, mask); + list.Process(); + } + else + { + for (unsigned i = 0; i < ci->GetAccessCount(); ++i) + { + ChanAccess *a = ci->GetAccess(i); + + if (a->mask.equals_ci(mask) && XOPChanAccess::DetermineLevel(a) == level) + { + Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "DEL " << a->mask; + + source.Reply(_("\002%s\002 deleted from %s %s list."), a->mask.c_str(), ci->name.c_str(), source.command.c_str()); + + FOREACH_MOD(I_OnAccessDel, OnAccessDel(ci, u, a)); + ci->EraseAccess(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 ¶ms, XOPType level) + { + User *u = source.u; + + const Anope::string &nick = params.size() > 2 ? params[2] : ""; + + AccessGroup access = ci->AccessFor(u); + bool override = false; + + if (!access.HasPriv(CA_ACCESS_LIST)) + { + if (u->HasCommand("chanserv/access/list")) + override = true; + else + { + source.Reply(ACCESS_DENIED); + return; + } + } + + Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci); + + if (!ci->GetAccessCount()) + { + source.Reply(_("%s %s list is empty."), ci->name.c_str(), source.command.c_str()); + return; + } + + if (!nick.empty() && nick.find_first_not_of("1234567890,-") == Anope::string::npos) + { + XOPListCallback list(source, ci, nick, level, this); + list.Process(); + } + else + { + bool SentHeader = false; + + for (unsigned i = 0, end = ci->GetAccessCount(); i < end; ++i) + { + ChanAccess *a = ci->GetAccess(i); + + if (XOPChanAccess::DetermineLevel(a) != level) + continue; + else if (!nick.empty() && !Anope::Match(a->mask, nick)) + continue; + + if (!SentHeader) + { + SentHeader = true; + source.Reply(_("%s list for %s:\n Num Nick"), source.command.c_str(), ci->name.c_str()); + } + + XOPListCallback::DoList(source, a, i + 1, level); + } + + if (!SentHeader) + source.Reply(_("No matching entries on %s %s list."), ci->name.c_str(), source.command.c_str()); + } + + return; + } + + void DoClear(CommandSource &source, ChannelInfo *ci, XOPType level) + { + User *u = source.u; + + if (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 (!ci->HasPriv(u, CA_FOUNDER) && !u->HasPriv("chanserv/access/modify")) + { + source.Reply(ACCESS_DENIED); + return; + } + + bool override = !ci->HasPriv(u, CA_FOUNDER); + Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "CLEAR level " << level; + + for (unsigned i = ci->GetAccessCount(); i > 0; --i) + { + ChanAccess *access = ci->GetAccess(i - 1); + if (XOPChanAccess::DetermineLevel(access) == level) + ci->EraseAccess(i - 1); + } + + FOREACH_MOD(I_OnAccessClear, OnAccessClear(ci, u)); + + source.Reply(_("Channel %s %s list has been cleared."), ci->name.c_str(), source.command.c_str()); + + return; + } + + protected: + void DoXop(CommandSource &source, const std::vector ¶ms, XOPType level) + { + ChannelInfo *ci = cs_findchan(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, level); + else if (cmd.equals_ci("DEL")) + return this->DoDel(source, ci, params, level); + else if (cmd.equals_ci("LIST")) + return this->DoList(source, ci, params, level); + else if (cmd.equals_ci("CLEAR")) + return this->DoClear(source, ci, level); + else + this->OnSyntaxError(source, ""); + + return; + } + public: + XOPBase(Module *modname, const Anope::string &command) : Command(modname, command, 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"); + } + + virtual ~XOPBase() + { + } + + virtual void Execute(CommandSource &source, const std::vector ¶ms) = 0; + + virtual bool OnHelp(CommandSource &source, const Anope::string &subcommand) = 0; +}; + +class CommandCSQOP : public XOPBase +{ + public: + CommandCSQOP(Module *creator) : XOPBase(creator, "chanserv/qop") + { + this->SetDesc(_("Modify the list of QOP users")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + return this->DoXop(source, params, XOP_QOP); + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Maintains the \002QOP\002 (AutoOwner) \002list\002 for a channel. The QOP \n" + "list gives users the right to be auto-owner on your channel,\n" + "which gives them almost (or potentially, total) access.\n" + " \n" + "The \002QOP ADD\002 command adds the given nickname to the\n" + "QOP list.\n" + " \n" + "The \002QOP DEL\002 command removes the given nick from the\n" + "QOP list. If a list of entry numbers is given, those\n" + "entries are deleted. (See the example for LIST below.)\n" + " \n" + "The \002QOP LIST\002 command displays the QOP 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" + " \002QOP #channel LIST 2-5,7-9\002\n" + " Lists QOP entries numbered 2 through 5 and\n" + " 7 through 9.\n" + " \n" + "The \002QOP CLEAR\002 command clears all entries of the\n" + "QOP list.\n")); + source.Reply(_(" \n" + "The \002QOP\002 commands are limited to\n" + "founders (unless SECUREOPS is off). However, any user on the\n" + "QOP list may use the \002QOP LIST\002 command.\n" + " \n")); + source.Reply(_("This command may have been disabled for your channel, and\n" + "in that case you need to use the access list. See \n" + "\002%s%s HELP ACCESS\002 for information about the access list,\n" + "and \002%s%s HELP SET XOP\002 to know how to toggle between \n" + "the access list and xOP list systems."), + Config->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str(), + Config->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str()); + return true; + } +}; + +class CommandCSAOP : public XOPBase +{ + public: + CommandCSAOP(Module *creator) : XOPBase(creator, "chanserv/aop") + { + this->SetDesc(_("Modify the list of AOP users")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + return this->DoXop(source, params, XOP_AOP); + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Maintains the \002AOP\002 (AutoOP) \002list\002 for a channel. The AOP \n" + "list gives users the right to be auto-opped on your channel,\n" + "to unban or invite themselves if needed, to have their\n" + "greet message showed on join, and so on.\n" + " \n" + "The \002AOP ADD\002 command adds the given nickname to the\n" + "AOP list.\n" + " \n" + "The \002AOP DEL\002 command removes the given nick from the\n" + "AOP list. If a list of entry numbers is given, those\n" + "entries are deleted. (See the example for LIST below.)\n" + " \n" + "The \002AOP LIST\002 command displays the AOP 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" + " \002AOP #channel LIST 2-5,7-9\002\n" + " Lists AOP entries numbered 2 through 5 and\n" + " 7 through 9.\n" + " \n" + "The \002AOP CLEAR\002 command clears all entries of the\n" + "AOP list.\n")); + source.Reply(_(" \n" + "The \002AOP ADD\002 and \002AOP DEL\002 commands are limited to\n" + "SOPs or above, while the \002AOP CLEAR\002 command can only\n" + "be used by the channel founder. However, any user on the\n" + "AOP list may use the \002AOP LIST\002 command.\n" + " \n")); + source.Reply(_("This command may have been disabled for your channel, and\n" + "in that case you need to use the access list. See \n" + "\002%s%s HELP ACCESS\002 for information about the access list,\n" + "and \002%s%s HELP SET XOP\002 to know how to toggle between \n" + "the access list and xOP list systems."), + Config->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str(), + Config->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str()); + return true; + } +}; + +class CommandCSHOP : public XOPBase +{ + public: + CommandCSHOP(Module *creator) : XOPBase(creator, "chanserv/hop") + { + this->SetDesc(_("Maintains the HOP (HalfOP) list for a channel")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + return this->DoXop(source, params, XOP_HOP); + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Maintains the \002HOP\002 (HalfOP) \002list\002 for a channel. The HOP \n" + "list gives users the right to be auto-halfopped on your \n" + "channel.\n" + " \n" + "The \002HOP ADD\002 command adds the given nickname to the\n" + "HOP list.\n" + " \n" + "The \002HOP DEL\002 command removes the given nick from the\n" + "HOP list. If a list of entry numbers is given, those\n" + "entries are deleted. (See the example for LIST below.)\n" + " \n" + "The \002HOP LIST\002 command displays the HOP 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" + " \002HOP #channel LIST 2-5,7-9\002\n" + " Lists HOP entries numbered 2 through 5 and\n" + " 7 through 9.\n" + " \n" + "The \002HOP CLEAR\002 command clears all entries of the\n" + "HOP list.\n")); + source.Reply(_(" \n" + "The \002HOP ADD\002, \002HOP DEL\002 and \002HOP LIST\002 commands are \n" + "limited to AOPs or above, while the \002HOP CLEAR\002 command \n" + "can only be used by the channel founder.\n" + " \n")); + source.Reply(_("This command may have been disabled for your channel, and\n" + "in that case you need to use the access list. See \n" + "\002%s%s HELP ACCESS\002 for information about the access list,\n" + "and \002%s%s HELP SET XOP\002 to know how to toggle between \n" + "the access list and xOP list systems."), + Config->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str(), + Config->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str()); + return true; + } +}; + +class CommandCSSOP : public XOPBase +{ + public: + CommandCSSOP(Module *creator) : XOPBase(creator, "chanserv/sop") + { + this->SetDesc(_("Modify the list of SOP users")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + return this->DoXop(source, params, XOP_SOP); + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Maintains the \002SOP\002 (SuperOP) \002list\002 for a channel. The SOP \n" + "list gives users all rights given by the AOP list, and adds\n" + "those needed to use the AutoKick and the BadWords lists, \n" + "to send and read channel memos, and so on.\n" + " \n" + "The \002SOP ADD\002 command adds the given nickname to the\n" + "SOP list.\n" + " \n" + "The \002SOP DEL\002 command removes the given nick from the\n" + "SOP list. If a list of entry numbers is given, those\n" + "entries are deleted. (See the example for LIST below.)\n" + " \n" + "The \002SOP LIST\002 command displays the SOP 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" + " \002SOP #channel LIST 2-5,7-9\002\n" + " Lists AOP entries numbered 2 through 5 and\n" + " 7 through 9.\n" + " \n" + "The \002SOP CLEAR\002 command clears all entries of the\n" + "SOP list.\n")); + source.Reply(_( + " \n" + "The \002SOP ADD\002, \002SOP DEL\002 and \002SOP CLEAR\002 commands are \n" + "limited to the channel founder. However, any user on the\n" + "AOP list may use the \002SOP LIST\002 command.\n" + " \n")); + source.Reply(_("This command may have been disabled for your channel, and\n" + "in that case you need to use the access list. See \n" + "\002%s%s HELP ACCESS\002 for information about the access list,\n" + "and \002%s%s HELP SET XOP\002 to know how to toggle between \n" + "the access list and xOP list systems."), + Config->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str(), + Config->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str()); + return true; + } +}; + +class CommandCSVOP : public XOPBase +{ + public: + CommandCSVOP(Module *creator) : XOPBase(creator, "chanserv/vop") + { + this->SetDesc(_("Maintains the VOP (VOicePeople) list for a channel")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + return this->DoXop(source, params, XOP_VOP); + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Maintains the \002VOP\002 (VOicePeople) \002list\002 for a channel. \n" + "The VOP list allows users to be auto-voiced and to voice \n" + "themselves if they aren't.\n" + " \n" + "The \002VOP ADD\002 command adds the given nickname to the\n" + "VOP list.\n" + " \n" + "The \002VOP DEL\002 command removes the given nick from the\n" + "VOP list. If a list of entry numbers is given, those\n" + "entries are deleted. (See the example for LIST below.)\n" + " \n" + "The \002VOP LIST\002 command displays the VOP 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" + " \002VOP #channel LIST 2-5,7-9\002\n" + " Lists VOP entries numbered 2 through 5 and\n" + " 7 through 9.\n" + " \n" + "The \002VOP CLEAR\002 command clears all entries of the\n" + "VOP list.\n")); + source.Reply(_( + " \n" + "The \002VOP ADD\002, \002VOP DEL\002 and \002VOP LIST\002 commands are \n" + "limited to AOPs or above, while the \002VOP CLEAR\002 command \n" + "can only be used by the channel founder.\n" + " \n")); + source.Reply(_("This command may have been disabled for your channel, and\n" + "in that case you need to use the access list. See \n" + "\002%s%s HELP ACCESS\002 for information about the access list,\n" + "and \002%s%s HELP SET XOP\002 to know how to toggle between \n" + "the access list and xOP list systems."), + Config->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str(), + Config->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str()); + return true; + } +}; + +class CSXOP : public Module +{ + XOPAccessProvider accessprovider; + CommandCSQOP commandcsqop; + CommandCSSOP commandcssop; + CommandCSAOP commandcsaop; + CommandCSHOP commandcshop; + CommandCSVOP commandcsvop; + + public: + CSXOP(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + accessprovider(this), commandcsqop(this), commandcssop(this), commandcsaop(this), commandcshop(this), commandcsvop(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&accessprovider); + ModuleManager::RegisterService(&commandcssop); + ModuleManager::RegisterService(&commandcsaop); + ModuleManager::RegisterService(&commandcsqop); + ModuleManager::RegisterService(&commandcsvop); + ModuleManager::RegisterService(&commandcshop); + } +}; + +MODULE_INIT(CSXOP) diff --git a/modules/commands/gl_global.cpp b/modules/commands/gl_global.cpp new file mode 100644 index 000000000..80c42e017 --- /dev/null +++ b/modules/commands/gl_global.cpp @@ -0,0 +1,64 @@ +/* Global core functions + * + * (C) 2003-2011 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 "global.h" + +class CommandGLGlobal : public Command +{ + 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 ¶ms) + { + User *u = source.u; + const Anope::string &msg = params[0]; + + if (!global) + source.Reply("No global reference, is gl_main loaded?"); + else + { + Log(LOG_ADMIN, u, this); + global->SendGlobal(findbot(Config->Global), u->nick, msg); + } + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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.owner->nick.c_str()); + return true; + } +}; + +class GLGlobal : public Module +{ + CommandGLGlobal commandglglobal; + + public: + GLGlobal(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandglglobal(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandglglobal); + } +}; + +MODULE_INIT(GLGlobal) diff --git a/modules/commands/help.cpp b/modules/commands/help.cpp new file mode 100644 index 000000000..2a4f25b6c --- /dev/null +++ b/modules/commands/help.cpp @@ -0,0 +1,130 @@ +/* Core functions + * + * (C) 2003-2011 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 CommandHelp : public Command +{ + public: + CommandHelp(Module *creator) : Command(creator, "generic/help", 0) + { + this->SetDesc(_("Displays this list and give information about commands")); + this->SetFlag(CFLAG_STRIP_CHANNEL); + this->SetFlag(CFLAG_ALLOW_UNREGISTERED); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + FOREACH_MOD(I_OnPreHelp, OnPreHelp(source, params)); + + User *u = source.u; + BotInfo *bi = source.owner; + + if (params.empty()) + { + for (BotInfo::command_map::iterator it = bi->commands.begin(), it_end = bi->commands.end(); it != it_end; ++it) + { + const Anope::string &c_name = it->first; + CommandInfo &info = it->second; + + // Smaller command exists + Anope::string cmd = myStrGetToken(c_name, ' ', 0); + if (cmd != it->first && bi->commands.count(cmd)) + continue; + + service_reference c(info.name); + if (!c) + continue; + if (!Config->HidePrivilegedCommands || info.permission.empty() || u->HasCommand(info.permission)) + { + source.command = c_name; + c->OnServHelp(source); + } + } + } + else + { + bool helped = false; + for (unsigned max = params.size(); max > 0; --max) + { + Anope::string full_command; + for (unsigned i = 0; i < max; ++i) + full_command += " " + params[i]; + full_command.erase(full_command.begin()); + + BotInfo::command_map::iterator it = bi->commands.find(full_command); + if (it == bi->commands.end()) + continue; + + CommandInfo &info = it->second; + + service_reference c(info.name); + if (!c) + continue; + + if (Config->HidePrivilegedCommands && !info.permission.empty() && !u->HasCommand(info.permission)) + continue; + + const Anope::string &subcommand = params.size() > max ? params[max] : ""; + source.command = full_command; + if (!c->OnHelp(source, subcommand)) + continue; + + helped = true; + + /* Inform the user what permission is required to use the 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()); + } + if (!c->HasFlag(CFLAG_ALLOW_UNREGISTERED) && !u->IsIdentified()) + { + if (info.permission.empty()) + source.Reply(" "); + source.Reply( _("You need to be identified to use this command.")); + } + /* User doesn't have the proper permission to use this command */ + else if (!info.permission.empty() && !u->HasCommand(info.permission)) + { + source.Reply(_("You cannot use this command.")); + } + + break; + } + + if (helped == false) + source.Reply(_("No help available for \002%s\002."), params[0].c_str()); + } + + FOREACH_MOD(I_OnPostHelp, OnPostHelp(source, params)); + + return; + } +}; + +class Help : public Module +{ + CommandHelp commandhelp; + + public: + Help(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandhelp(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandhelp); + } +}; + +MODULE_INIT(Help) diff --git a/modules/commands/hs_del.cpp b/modules/commands/hs_del.cpp new file mode 100644 index 000000000..541986aff --- /dev/null +++ b/modules/commands/hs_del.cpp @@ -0,0 +1,107 @@ +/* HostServ core functions + * + * (C) 2003-2011 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 ¶ms) + { + User *u = source.u; + const Anope::string &nick = params[0]; + NickAlias *na = findnick(nick); + if (na) + { + Log(LOG_ADMIN, u, this) << "for user " << na->nick; + FOREACH_MOD(I_OnDeleteVhost, OnDeleteVhost(na)); + na->hostinfo.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) + { + 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(_("Delete the vhost for all nicks in a group")); + this->SetSyntax(_("\037nick\037")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + const Anope::string &nick = params[0]; + User *u = source.u; + NickAlias *na = findnick(nick); + if (na) + { + FOREACH_MOD(I_OnDeleteVhost, OnDeleteVhost(na)); + NickCore *nc = na->nc; + for (std::list::iterator it = nc->aliases.begin(), it_end = nc->aliases.end(); it != it_end; ++it) + { + na = *it; + na->hostinfo.RemoveVhost(); + } + Log(LOG_ADMIN, u, 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) + { + 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, CORE), + commandhsdel(this), commandhsdelall(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandhsdel); + ModuleManager::RegisterService(&commandhsdelall); + } +}; + +MODULE_INIT(HSDel) diff --git a/modules/commands/hs_group.cpp b/modules/commands/hs_group.cpp new file mode 100644 index 000000000..8d6fbd630 --- /dev/null +++ b/modules/commands/hs_group.cpp @@ -0,0 +1,80 @@ +/* HostServ core functions + * + * (C) 2003-2011 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 +{ + void Sync(NickAlias *na) + { + if (!na || !na->hostinfo.HasVhost()) + return; + + for (std::list::iterator it = na->nc->aliases.begin(), it_end = na->nc->aliases.end(); it != it_end; ++it) + { + NickAlias *nick = *it; + nick->hostinfo.SetVhost(na->hostinfo.GetIdent(), na->hostinfo.GetHost(), na->hostinfo.GetCreator()); + } + } + + public: + CommandHSGroup(Module *creator) : Command(creator, "hostserv/group", 0, 0) + { + this->SetDesc(_("Syncs the vhost for all nicks in a group")); + this->SetSyntax(""); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + NickAlias *na = findnick(u->nick); + if (na && u->Account() == na->nc && na->hostinfo.HasVhost()) + { + this->Sync(na); + if (!na->hostinfo.GetIdent().empty()) + source.Reply(_("All vhost's in the group \002%s\002 have been set to \002%s\002@\002%s\002"), u->Account()->display.c_str(), na->hostinfo.GetIdent().c_str(), na->hostinfo.GetHost().c_str()); + else + source.Reply(_("All vhost's in the group \002%s\002 have been set to \002%s\002"), u->Account()->display.c_str(), na->hostinfo.GetHost().c_str()); + } + else + source.Reply(HOST_NOT_ASSIGNED); + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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; + + public: + HSGroup(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandhsgroup(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandhsgroup); + } +}; + +MODULE_INIT(HSGroup) diff --git a/modules/commands/hs_list.cpp b/modules/commands/hs_list.cpp new file mode 100644 index 000000000..589eef3e1 --- /dev/null +++ b/modules/commands/hs_list.cpp @@ -0,0 +1,137 @@ +/* HostServ core functions + * + * (C) 2003-2011 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 CommandHSList : public Command +{ + public: + CommandHSList(Module *creator) : Command(creator, "hostserv/list", 0, 1) + { + this->SetDesc(_("Displays one or more vhost entries.")); + this->SetSyntax(_("\002[|<#X-Y>]")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + const Anope::string &key = !params.empty() ? params[0] : ""; + int from = 0, to = 0, counter = 1; + unsigned display_counter = 0; + + /** + * Do a check for a range here, then in the next loop + * we'll only display what has been requested.. + **/ + if (!key.empty() && key[0] == '#') + { + size_t tmp = key.find('-'); + if (tmp == Anope::string::npos || tmp == key.length() || tmp == 1) + { + source.Reply(LIST_INCORRECT_RANGE); + return; + } + for (unsigned i = 1, end = key.length(); i < end; ++i) + { + if (!isdigit(key[i]) && i != tmp) + { + source.Reply(LIST_INCORRECT_RANGE); + return; + } + try + { + from = convertTo(key.substr(1, tmp - 1)); + to = convertTo(key.substr(tmp + 1)); + } + catch (const ConvertException &) { } + } + } + + for (nickalias_map::const_iterator it = NickAliasList.begin(), it_end = NickAliasList.end(); it != it_end; ++it) + { + NickAlias *na = it->second; + + if (!na->hostinfo.HasVhost()) + continue; + + if (!key.empty() && key[0] != '#') + { + if ((Anope::Match(na->nick, key) || Anope::Match(na->hostinfo.GetHost(), key)) && display_counter < Config->NSListMax) + { + ++display_counter; + if (!na->hostinfo.GetIdent().empty()) + source.Reply(_("#%d Nick:\002%s\002, vhost:\002%s\002@\002%s\002 (%s - %s)"), counter, na->nick.c_str(), na->hostinfo.GetIdent().c_str(), na->hostinfo.GetHost().c_str(), na->hostinfo.GetCreator().c_str(), do_strftime(na->hostinfo.GetTime()).c_str()); + else + source.Reply(_("#%d Nick:\002%s\002, vhost:\002%s\002 (%s - %s)"), counter, na->nick.c_str(), na->hostinfo.GetHost().c_str(), na->hostinfo.GetCreator().c_str(), do_strftime(na->hostinfo.GetTime()).c_str()); + } + } + else + { + /** + * List the host if its in the display range, and not more + * than NSListMax records have been displayed... + **/ + if (((counter >= from && counter <= to) || (!from && !to)) && display_counter < Config->NSListMax) + { + ++display_counter; + if (!na->hostinfo.GetIdent().empty()) + source.Reply(_("#%d Nick:\002%s\002, vhost:\002%s\002@\002%s\002 (%s - %s)"), counter, na->nick.c_str(), na->hostinfo.GetIdent().c_str(), na->hostinfo.GetHost().c_str(), na->hostinfo.GetCreator().c_str(), do_strftime(na->hostinfo.GetTime()).c_str()); + else + source.Reply(_("#%d Nick:\002%s\002, vhost:\002%s\002 (%s - %s)"), counter, na->nick.c_str(), na->hostinfo.GetHost().c_str(), na->hostinfo.GetCreator().c_str(), do_strftime(na->hostinfo.GetTime()).c_str()); + } + } + ++counter; + } + if (!key.empty()) + source.Reply(_("Displayed records matching key \002%s\002 (Count: \002%d\002)"), key.c_str(), display_counter); + else + { + if (from) + source.Reply(_("Displayed records from \002%d\002 to \002%d\002"), from, to); + else + source.Reply(_("Displayed all records (Count: \002%d\002)"), display_counter); + } + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("This command lists registered vhosts to the operator\n" + "if a Key is specified, only entries whos nick or vhost match\n" + "the pattern given in are displayed e.g. Rob* for all\n" + "entries beginning with \"Rob\"\n" + "If a #X-Y style is used, only entries between the range of X\n" + "and Y will be displayed, e.g. #1-3 will display the first 3\n" + "nick/vhost entries.\n" + "The list uses the value of NSListMax as a hard limit for the\n" + "number of items to display to a operator at any 1 time.")); + return true; + } +}; + +class HSList : public Module +{ + CommandHSList commandhslist; + + public: + HSList(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandhslist(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandhslist); + } +}; + +MODULE_INIT(HSList) diff --git a/modules/commands/hs_off.cpp b/modules/commands/hs_off.cpp new file mode 100644 index 000000000..a3f11c2bd --- /dev/null +++ b/modules/commands/hs_off.cpp @@ -0,0 +1,67 @@ +/* HostServ core functions + * + * (C) 2003-2011 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->SetSyntax(""); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + NickAlias *na = findnick(u->nick); + + if (!na || !na->hostinfo.HasVhost()) + source.Reply(HOST_NOT_ASSIGNED); + else + { + ircdproto->SendVhostDel(u); + Log(LOG_COMMAND, u, 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) + { + 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 IP address.")); + return true; + } +}; + +class HSOff : public Module +{ + CommandHSOff commandhsoff; + + public: + HSOff(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandhsoff(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandhsoff); + } +}; + +MODULE_INIT(HSOff) diff --git a/modules/commands/hs_on.cpp b/modules/commands/hs_on.cpp new file mode 100644 index 000000000..0ba124ba1 --- /dev/null +++ b/modules/commands/hs_on.cpp @@ -0,0 +1,77 @@ +/* HostServ core functions + * + * (C) 2003-2011 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->SetSyntax(""); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + NickAlias *na = findnick(u->nick); + if (na && u->Account() == na->nc && na->hostinfo.HasVhost()) + { + if (!na->hostinfo.GetIdent().empty()) + source.Reply(_("Your vhost of \002%s\002@\002%s\002 is now activated."), na->hostinfo.GetIdent().c_str(), na->hostinfo.GetHost().c_str()); + else + source.Reply(_("Your vhost of \002%s\002 is now activated."), na->hostinfo.GetHost().c_str()); + Log(LOG_COMMAND, u, this) << "to enable their vhost of " << (!na->hostinfo.GetIdent().empty() ? na->hostinfo.GetIdent() + "@" : "") << na->hostinfo.GetHost(); + ircdproto->SendVhost(u, na->hostinfo.GetIdent(), na->hostinfo.GetHost()); + if (ircd->vhost) + u->vhost = na->hostinfo.GetHost(); + if (ircd->vident) + { + if (!na->hostinfo.GetIdent().empty()) + u->SetVIdent(na->hostinfo.GetIdent()); + } + u->UpdateHost(); + } + else + source.Reply(HOST_NOT_ASSIGNED); + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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 IP address.")); + return true; + } +}; + +class HSOn : public Module +{ + CommandHSOn commandhson; + + public: + HSOn(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandhson(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandhson); + } +}; + +MODULE_INIT(HSOn) diff --git a/modules/commands/hs_request.cpp b/modules/commands/hs_request.cpp new file mode 100644 index 000000000..7686794f5 --- /dev/null +++ b/modules/commands/hs_request.cpp @@ -0,0 +1,424 @@ +/* hs_request.c - Add request and activate functionality to HostServ, + * + * + * (C) 2003-2011 Anope Team + * Contact us at team@anope.org + * + * Based on the original module by Rob + * Included in the Anope module pack since Anope 1.7.11 + * Anope Coder: GeniusDex + * + * 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" +#include "memoserv.h" + +static bool HSRequestMemoUser = false; +static bool HSRequestMemoOper = false; + +void my_add_host_request(const Anope::string &nick, const Anope::string &vIdent, const Anope::string &vhost, const Anope::string &creator, time_t tmp_time); +void req_send_memos(CommandSource &source, const Anope::string &vIdent, const Anope::string &vHost); + +struct HostRequest +{ + Anope::string ident; + Anope::string host; + time_t time; +}; + +typedef std::map > RequestMap; +RequestMap Requests; + +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 ¶ms) + { + User *u = source.u; + + 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->UserLen) + { + source.Reply(HOST_SET_IDENTTOOLONG, Config->UserLen); + return; + } + else if (!ircd->vident) + { + 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->HostLen) + { + source.Reply(HOST_SET_TOOLONG, Config->HostLen); + return; + } + + if (!isValidHost(host, 3)) + { + source.Reply(HOST_SET_ERROR); + return; + } + + if (HSRequestMemoOper && Config->MSSendDelay > 0 && u && u->lastmemosend + Config->MSSendDelay > Anope::CurTime) + { + source.Reply(_("Please wait %d seconds before requesting a new vHost"), Config->MSSendDelay); + u->lastmemosend = Anope::CurTime; + return; + } + my_add_host_request(u->nick, user, host, u->nick, Anope::CurTime); + + source.Reply(_("Your vHost has been requested")); + req_send_memos(source, user, host); + Log(LOG_COMMAND, u, this, NULL) << "to request new vhost " << (!user.empty() ? user + "@" : "") << host; + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Request the given vHost to be actived 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 ¶ms) + { + User *u = source.u; + + const Anope::string &nick = params[0]; + + NickAlias *na = findnick(nick); + if (na) + { + RequestMap::iterator it = Requests.find(na->nick); + if (it != Requests.end()) + { + na->hostinfo.SetVhost(it->second->ident, it->second->host, u->nick, it->second->time); + FOREACH_MOD(I_OnSetVhost, OnSetVhost(na)); + + if (HSRequestMemoUser && memoserv) + memoserv->Send(Config->HostServ, 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, u, this, NULL) << "for " << na->nick << " for vhost " << (!it->second->ident.empty() ? it->second->ident + "@" : "") << it->second->host; + delete it->second; + Requests.erase(it); + } + else + source.Reply(_("No request for nick %s found."), nick.c_str()); + } + else + source.Reply(NICK_X_NOT_REGISTERED, nick.c_str()); + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Activate the requested vHost for the given nick.")); + if (HSRequestMemoUser) + 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")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + + const Anope::string &nick = params[0]; + const Anope::string &reason = params.size() > 1 ? params[1] : ""; + + RequestMap::iterator it = Requests.find(nick); + if (it != Requests.end()) + { + delete it->second; + Requests.erase(it); + + if (HSRequestMemoUser && 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(Config->HostServ, nick, message, true); + } + + source.Reply(_("vHost for %s has been rejected"), nick.c_str()); + Log(LOG_COMMAND, u, this, NULL) << "to reject vhost for " << nick << " (" << (!reason.empty() ? reason : "") << ")"; + } + else + source.Reply(_("No request for nick %s found."), nick.c_str()); + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Reject the requested vHost for the given nick.")); + if (HSRequestMemoUser) + source.Reply(_("A memo informing the user will also be sent.")); + + return true; + } +}; + +class HSListBase : public Command +{ + protected: + void DoList(CommandSource &source) + { + int counter = 1; + int from = 0, to = 0; + unsigned display_counter = 0; + + for (RequestMap::iterator it = Requests.begin(), it_end = Requests.end(); it != it_end; ++it) + { + HostRequest *hr = it->second; + if (((counter >= from && counter <= to) || (!from && !to)) && display_counter < Config->NSListMax) + { + ++display_counter; + if (!hr->ident.empty()) + source.Reply(_("#%d Nick:\002%s\002, vhost:\002%s\002@\002%s\002 (%s - %s)"), counter, it->first.c_str(), hr->ident.c_str(), hr->host.c_str(), it->first.c_str(), do_strftime(hr->time).c_str()); + else + source.Reply(_("#%d Nick:\002%s\002, vhost:\002%s\002 (%s - %s)"), counter, it->first.c_str(), hr->host.c_str(), it->first.c_str(), do_strftime(hr->time).c_str()); + } + ++counter; + } + source.Reply(_("Displayed all records (Count: \002%d\002)"), display_counter); + + return; + } + public: + HSListBase(Module *creator, const Anope::string &cmd, int min, int max) : Command(creator, cmd, min, max) + { + } +}; + +class CommandHSWaiting : public HSListBase +{ + public: + CommandHSWaiting(Module *creator) : HSListBase(creator, "hostserv/waiting", 0, 0) + { + this->SetDesc(_("Retrieves the vhost requests")); + this->SetSyntax(""); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + return this->DoList(source); + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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; + + public: + HSRequest(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, SUPPORTED), + commandhsrequest(this), commandhsactive(this), commandhsreject(this), commandhswaiting(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandhsrequest); + ModuleManager::RegisterService(&commandhsactive); + ModuleManager::RegisterService(&commandhsreject); + ModuleManager::RegisterService(&commandhswaiting); + + Implementation i[] = { I_OnDelNick, I_OnDatabaseRead, I_OnDatabaseWrite, I_OnReload }; + ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); + + this->OnReload(); + } + + ~HSRequest() + { + /* Clean up all open host requests */ + while (!Requests.empty()) + { + delete Requests.begin()->second; + Requests.erase(Requests.begin()); + } + } + + void OnDelNick(NickAlias *na) + { + RequestMap::iterator it = Requests.find(na->nick); + + if (it != Requests.end()) + { + delete it->second; + Requests.erase(it); + } + } + + EventReturn OnDatabaseRead(const std::vector ¶ms) + { + if (params[0].equals_ci("HS_REQUEST") && params.size() >= 5) + { + Anope::string vident = params[2].equals_ci("(null)") ? "" : params[2]; + my_add_host_request(params[1], vident, params[3], params[1], params[4].is_pos_number_only() ? convertTo(params[4]) : 0); + + return EVENT_STOP; + } + + return EVENT_CONTINUE; + } + + void OnDatabaseWrite(void (*Write)(const Anope::string &)) + { + for (RequestMap::iterator it = Requests.begin(), it_end = Requests.end(); it != it_end; ++it) + { + HostRequest *hr = it->second; + std::stringstream buf; + buf << "HS_REQUEST " << it->first << " " << (hr->ident.empty() ? "(null)" : hr->ident) << " " << hr->host << " " << hr->time; + Write(buf.str()); + } + } + + void OnReload() + { + ConfigReader config; + HSRequestMemoUser = config.ReadFlag("hs_request", "memouser", "no", 0); + HSRequestMemoOper = config.ReadFlag("hs_request", "memooper", "no", 0); + + Log(LOG_DEBUG) << "[hs_request] Set config vars: MemoUser=" << HSRequestMemoUser << " MemoOper=" << HSRequestMemoOper; + } +}; + +void req_send_memos(CommandSource &source, const Anope::string &vIdent, const Anope::string &vHost) +{ + Anope::string host; + std::list >::iterator it, it_end; + + if (!vIdent.empty()) + host = vIdent + "@" + vHost; + else + host = vHost; + + if (HSRequestMemoOper == 1 && memoserv) + for (unsigned i = 0; i < Config->Opers.size(); ++i) + { + Oper *o = Config->Opers[i]; + + NickAlias *na = findnick(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.u->GetMask().c_str()); + + memoserv->Send(Config->HostServ, na->nick, message, true); + } +} + +void my_add_host_request(const Anope::string &nick, const Anope::string &vIdent, const Anope::string &vhost, const Anope::string &creator, time_t tmp_time) +{ + HostRequest *hr = new HostRequest; + hr->ident = vIdent; + hr->host = vhost; + hr->time = tmp_time; + RequestMap::iterator it = Requests.find(nick); + if (it != Requests.end()) + { + delete it->second; + Requests.erase(it); + } + Requests.insert(std::make_pair(nick, hr)); +} + +void my_load_config() +{ + ConfigReader config; + HSRequestMemoUser = config.ReadFlag("hs_request", "memouser", "no", 0); + HSRequestMemoOper = config.ReadFlag("hs_request", "memooper", "no", 0); + + Log(LOG_DEBUG) << "[hs_request] Set config vars: MemoUser=" << HSRequestMemoUser << " MemoOper=" << HSRequestMemoOper; +} + +MODULE_INIT(HSRequest) diff --git a/modules/commands/hs_set.cpp b/modules/commands/hs_set.cpp new file mode 100644 index 000000000..4711022db --- /dev/null +++ b/modules/commands/hs_set.cpp @@ -0,0 +1,236 @@ +/* HostServ core functions + * + * (C) 2003-2011 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 ¶ms) + { + User *u = source.u; + + const Anope::string &nick = params[0]; + + NickAlias *na = findnick(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 (user.length() > Config->UserLen) + { + source.Reply(HOST_SET_IDENTTOOLONG, Config->UserLen); + return; + } + else if (!ircd->vident) + { + 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->HostLen) + { + source.Reply(HOST_SET_TOOLONG, Config->HostLen); + return; + } + + if (!isValidHost(host, 3)) + { + source.Reply(HOST_SET_ERROR); + return; + } + + Log(LOG_ADMIN, u, this) << "to set the vhost of " << na->nick << " to " << (!user.empty() ? user + "@" : "") << host; + + na->hostinfo.SetVhost(user, host, u->nick); + FOREACH_MOD(I_OnSetVhost, 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) + { + 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 @ set idents for users as \n" + "well as vhosts.")); + return true; + } +}; + +class CommandHSSetAll : public Command +{ + void Sync(NickAlias *na) + { + if (!na || !na->hostinfo.HasVhost()) + return; + + for (std::list::iterator it = na->nc->aliases.begin(), it_end = na->nc->aliases.end(); it != it_end; ++it) + { + NickAlias *nick = *it; + nick->hostinfo.SetVhost(na->hostinfo.GetIdent(), na->hostinfo.GetHost(), na->hostinfo.GetCreator()); + } + } + + 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 ¶ms) + { + User *u = source.u; + + Anope::string nick = params[0]; + + NickAlias *na = findnick(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 (user.length() > Config->UserLen) + { + source.Reply(HOST_SET_IDENTTOOLONG, Config->UserLen); + return; + } + else if (!ircd->vident) + { + 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->HostLen) + { + source.Reply(HOST_SET_TOOLONG, Config->HostLen); + return; + } + + if (!isValidHost(host, 3)) + { + source.Reply(HOST_SET_ERROR); + return; + } + + Log(LOG_ADMIN, u, this) << "to set the vhost of " << na->nick << " to " << (!user.empty() ? user + "@" : "") << host; + + na->hostinfo.SetVhost(user, host, u->nick); + this->Sync(na); + FOREACH_MOD(I_OnSetVhost, 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) + { + 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 @ 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, CORE), commandhsset(this), commandhssetall(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandhsset); + ModuleManager::RegisterService(&commandhssetall); + } +}; + +MODULE_INIT(HSSet) diff --git a/modules/commands/ms_cancel.cpp b/modules/commands/ms_cancel.cpp new file mode 100644 index 000000000..e5beffa10 --- /dev/null +++ b/modules/commands/ms_cancel.cpp @@ -0,0 +1,77 @@ +/* MemoServ core functions + * + * (C) 2003-2011 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 "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(_("{\037nick\037 | \037channel\037}")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + + const Anope::string &nname = params[0]; + + bool ischan; + MemoInfo *mi = memoserv->GetMemoInfo(nname, ischan); + + if (mi == NULL) + source.Reply(ischan ? CHAN_X_NOT_REGISTERED : _(NICK_X_NOT_REGISTERED), nname.c_str()); + else + { + for (int i = mi->memos.size() - 1; i >= 0; --i) + if (mi->memos[i]->HasFlag(MF_UNREAD) && u->Account()->display.equals_ci(mi->memos[i]->sender)) + { + FOREACH_MOD(I_OnMemoDel, OnMemoDel(findnick(nname)->nc, mi, mi->memos[i])); + mi->Del(mi->memos[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) + { + 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, CORE), + commandmscancel(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandmscancel); + } +}; + +MODULE_INIT(MSCancel) diff --git a/modules/commands/ms_check.cpp b/modules/commands/ms_check.cpp new file mode 100644 index 000000000..3019e270f --- /dev/null +++ b/modules/commands/ms_check.cpp @@ -0,0 +1,89 @@ +/* MemoServ core functions + * + * (C) 2003-2011 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 ¶ms) + { + User *u = source.u; + + const Anope::string &recipient = params[0]; + + bool found = false; + + NickAlias *na = findnick(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 wanna get the LAST memo, so we + have to loop backwards */ + + for (int i = mi->memos.size() - 1; i >= 0; --i) + { + if (u->Account()->display.equals_ci(mi->memos[i]->sender)) + { + found = true; /* Yes, we've found the memo */ + + if (mi->memos[i]->HasFlag(MF_UNREAD)) + source.Reply(_("The last memo you sent to %s (sent on %s) has not yet been read."), na->nick.c_str(), do_strftime(mi->memos[i]->time).c_str()); + else + source.Reply(_("The last memo you sent to %s (sent on %s) has been read."), na->nick.c_str(), do_strftime(mi->memos[i]->time).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) + { + 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 does only work with nicks, not with chans.")); + return true; + } +}; + +class MSCheck : public Module +{ + CommandMSCheck commandmscheck; + + public: + MSCheck(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandmscheck(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandmscheck); + } +}; + +MODULE_INIT(MSCheck) diff --git a/modules/commands/ms_del.cpp b/modules/commands/ms_del.cpp new file mode 100644 index 000000000..991adff2b --- /dev/null +++ b/modules/commands/ms_del.cpp @@ -0,0 +1,163 @@ +/* MemoServ core functions + * + * (C) 2003-2011 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) + { + if (!Number || Number > mi->memos.size()) + return; + + if (ci) + FOREACH_MOD(I_OnMemoDel, OnMemoDel(ci, mi, mi->memos[Number - 1])); + else + FOREACH_MOD(I_OnMemoDel, OnMemoDel(source.u->Account(), mi, mi->memos[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 ¶ms) + { + User *u = source.u; + + 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] : ""; + + if (!(ci = cs_findchan(chan))) + { + source.Reply(CHAN_X_NOT_REGISTERED, chan.c_str()); + return; + } + else if (readonly) + { + source.Reply(READ_ONLY_MODE); + return; + } + else if (!ci->HasPriv(u, CA_MEMO)) + { + source.Reply(ACCESS_DENIED); + return; + } + mi = &ci->memos; + } + else + mi = &u->Account()->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. */ + if (ci) + FOREACH_MOD(I_OnMemoDel, OnMemoDel(ci, mi, mi->memos[mi->memos.size() - 1])); + else + FOREACH_MOD(I_OnMemoDel, OnMemoDel(u->Account(), mi, mi->memos[mi->memos.size() - 1])); + mi->Del(mi->memos[mi->memos.size() - 1]); + source.Reply(_("Memo %d has been deleted."), mi->memos.size() + 1); + } + else + { + if (ci) + FOREACH_MOD(I_OnMemoDel, OnMemoDel(ci, mi, NULL)); + else + FOREACH_MOD(I_OnMemoDel, OnMemoDel(u->Account(), mi, NULL)); + /* Delete all memos. */ + for (unsigned i = 0, end = mi->memos.size(); i < end; ++i) + delete mi->memos[i]; + mi->memos.clear(); + 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) + { + 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, CORE), + commandmsdel(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandmsdel); + } +}; + +MODULE_INIT(MSDel) diff --git a/modules/commands/ms_ignore.cpp b/modules/commands/ms_ignore.cpp new file mode 100644 index 000000000..c8b9c19e6 --- /dev/null +++ b/modules/commands/ms_ignore.cpp @@ -0,0 +1,110 @@ +/* MemoServ core functions + * + * (C) 2003-2011 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 "memoserv.h" + +class CommandMSIgnore : public Command +{ + public: + CommandMSIgnore(Module *creator) : Command(creator, "memoserv/ignore", 1, 3) + { + this->SetDesc(_("Manage your memo ignore list")); + this->SetSyntax(_("[\037channel\037] {\002ADD|DEL|LIST\002} [\037entry\037]")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + + 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 = u->nick; + } + + bool ischan; + MemoInfo *mi = memoserv->GetMemoInfo(channel, ischan); + if (!mi) + source.Reply(ischan ? CHAN_X_NOT_REGISTERED : _(NICK_X_NOT_REGISTERED), channel.c_str()); + else if (ischan && !cs_findchan(channel)->HasPriv(u, CA_MEMO)) + source.Reply(ACCESS_DENIED); + else if (command.equals_ci("ADD") && !param.empty()) + { + 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 your ignore list."), param.c_str()); + } + else + source.Reply(_("\002%s\002 is already on your ignore list."), param.c_str()); + } + else if (command.equals_ci("DEL") && !param.empty()) + { + std::vector::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 your ignore list."), param.c_str()); + } + else + source.Reply(_("\002%s\002 is not on your ignore list."), param.c_str()); + } + else if (command.equals_ci("LIST")) + { + if (mi->ignores.empty()) + source.Reply(_("Your memo ignore list is empty.")); + else + { + source.Reply(_("Ignore list:")); + for (unsigned i = 0; i < mi->ignores.size(); ++i) + source.Reply(" %s", mi->ignores[i].c_str()); + } + } + else + this->OnSyntaxError(source, ""); + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Allows you to ignore users by nick or host from memoing you. If someone on your\n" + "memo ignore list tries to memo you, 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, CORE), + commandmsignore(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandmsignore); + } +}; + +MODULE_INIT(MSIgnore) diff --git a/modules/commands/ms_info.cpp b/modules/commands/ms_info.cpp new file mode 100644 index 000000000..9fdb467c9 --- /dev/null +++ b/modules/commands/ms_info.cpp @@ -0,0 +1,219 @@ +/* MemoServ core functions + * + * (C) 2003-2011 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 ¶ms) + { + User *u = source.u; + + const MemoInfo *mi; + NickAlias *na = NULL; + ChannelInfo *ci = NULL; + const Anope::string &nname = !params.empty() ? params[0] : ""; + int hardmax = 0; + + if (!nname.empty() && nname[0] != '#' && u->HasPriv("memoserv/info")) + { + na = findnick(nname); + if (!na) + { + source.Reply(NICK_X_NOT_REGISTERED, nname.c_str()); + return; + } + mi = &na->nc->memos; + hardmax = na->nc->HasFlag(NI_MEMO_HARDMAX) ? 1 : 0; + } + else if (!nname.empty() && nname[0] == '#') + { + if (!(ci = cs_findchan(nname))) + { + source.Reply(CHAN_X_NOT_REGISTERED, nname.c_str()); + return; + } + else if (!ci->HasPriv(u, CA_MEMO)) + { + source.Reply(ACCESS_DENIED); + return; + } + mi = &ci->memos; + hardmax = ci->HasFlag(CI_MEMO_HARDMAX) ? 1 : 0; + } + else if (!nname.empty()) /* It's not a chan and we aren't services admin */ + { + source.Reply(ACCESS_DENIED); + return; + } + else + { + mi = &u->Account()->memos; + hardmax = u->Account()->HasFlag(NI_MEMO_HARDMAX) ? 1 : 0; + } + + if (!nname.empty() && (ci || na->nc != u->Account())) + { + if (mi->memos.empty()) + source.Reply(_("%s currently has no memos."), nname.c_str()); + else if (mi->memos.size() == 1) + { + if (mi->memos[0]->HasFlag(MF_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->memos[i]->HasFlag(MF_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->HasFlag(NI_MEMO_RECEIVE) && na->nc->HasFlag(NI_MEMO_SIGNON)) + source.Reply(_("%s is notified of new memos at logon and when they arrive."), nname.c_str()); + else if (na->nc->HasFlag(NI_MEMO_RECEIVE)) + source.Reply(_("%s is notified when new memos arrive."), nname.c_str()); + else if (na->nc->HasFlag(NI_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 == u->Account()) */ + { + if (mi->memos.empty()) + source.Reply(_("You currently have no memos.")); + else if (mi->memos.size() == 1) + { + if (mi->memos[0]->HasFlag(MF_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->memos[i]->HasFlag(MF_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 (!u->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 (!u->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.")); + + /* Ripped too. But differently because of a seg fault (loughs) */ + if (u->Account()->HasFlag(NI_MEMO_RECEIVE) && u->Account()->HasFlag(NI_MEMO_SIGNON)) + source.Reply(_("You will be notified of new memos at logon and when they arrive.")); + else if (u->Account()->HasFlag(NI_MEMO_RECEIVE)) + source.Reply(_("You will be notified when new memos arrive.")); + else if (u->Account()->HasFlag(NI_MEMO_SIGNON)) + source.Reply(_("You will be notified of new memos at logon.")); + else + source.Reply(_("You will not be notified of new memos.")); + } + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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 use limited to \002Services\n" + "Operators\002.")); + + return true; + } +}; + +class MSInfo : public Module +{ + CommandMSInfo commandmsinfo; + + public: + MSInfo(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandmsinfo(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandmsinfo); + } +}; + +MODULE_INIT(MSInfo) diff --git a/modules/commands/ms_list.cpp b/modules/commands/ms_list.cpp new file mode 100644 index 000000000..26821a413 --- /dev/null +++ b/modules/commands/ms_list.cpp @@ -0,0 +1,168 @@ +/* MemoServ core functions + * + * (C) 2003-2011 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 MemoListCallback : public NumberList +{ + CommandSource &source; + ChannelInfo *ci; + const MemoInfo *mi; + bool SentHeader; + public: + MemoListCallback(CommandSource &_source, ChannelInfo *_ci, const MemoInfo *_mi, const Anope::string &list) : NumberList(list, false), source(_source), ci(_ci), mi(_mi), SentHeader(false) + { + } + + void HandleNumber(unsigned Number) + { + if (!Number || Number > mi->memos.size()) + return; + + if (!SentHeader) + { + SentHeader = true; + source.Reply(_("Memos for %s:"), ci ? ci->name.c_str() : source.u->nick.c_str()); + source.Reply(_(" Num Sender Date/Time")); + } + + DoList(source, mi, Number - 1); + } + + static void DoList(CommandSource &source, const MemoInfo *mi, unsigned index) + { + Memo *m = mi->memos[index]; + source.Reply(_("%c%3d %-16s %s"), (m->HasFlag(MF_UNREAD)) ? '*' : ' ', index + 1, m->sender.c_str(), do_strftime(m->time).c_str()); + } +}; + +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 ¶ms) + { + User *u = source.u; + + Anope::string param = !params.empty() ? params[0] : "", chan; + ChannelInfo *ci = NULL; + const MemoInfo *mi; + int i, end; + + if (!param.empty() && param[0] == '#') + { + chan = param; + param = params.size() > 1 ? params[1] : ""; + + if (!(ci = cs_findchan(chan))) + { + source.Reply(CHAN_X_NOT_REGISTERED, chan.c_str()); + return; + } + else if (!ci->HasPriv(u, CA_MEMO)) + { + source.Reply(ACCESS_DENIED); + return; + } + mi = &ci->memos; + } + else + mi = &u->Account()->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 + { + if (!param.empty() && isdigit(param[0])) + { + MemoListCallback list(source, ci, mi, param); + list.Process(); + } + else + { + if (!param.empty()) + { + for (i = 0, end = mi->memos.size(); i < end; ++i) + if (mi->memos[i]->HasFlag(MF_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; + } + } + + bool SentHeader = false; + + for (i = 0, end = mi->memos.size(); i < end; ++i) + { + if (!param.empty() && !mi->memos[i]->HasFlag(MF_UNREAD)) + continue; + + if (!SentHeader) + { + SentHeader = true; + source.Reply(_("New memo for %s."), ci ? ci->name.c_str() : u->nick.c_str()); + source.Reply(_(" Num Sender Date/Time")); + } + + MemoListCallback::DoList(source, mi, i); + } + } + } + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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, CORE), + commandmslist(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandmslist); + } +}; + +MODULE_INIT(MSList) diff --git a/modules/commands/ms_read.cpp b/modules/commands/ms_read.cpp new file mode 100644 index 000000000..f275f8d7d --- /dev/null +++ b/modules/commands/ms_read.cpp @@ -0,0 +1,195 @@ +/* MemoServ core functions + * + * (C) 2003-2011 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 "memoserv.h" + +void rsend_notify(CommandSource &source, MemoInfo *mi, Memo *m, const Anope::string &targ) +{ + /* Only send receipt if memos are allowed */ + if (memoserv && !readonly) + { + /* Get nick alias for sender */ + NickAlias *na = findnick(m->sender); + + if (!na) + return; + + /* Get nick core for sender */ + NickCore *nc = na->nc; + + if (!nc) + return; + + /* Text of the memo varies if the recepient was a + nick or channel */ + Anope::string text = Anope::printf(translate(na->nc, _("\002[auto-memo]\002 The memo you sent to %s has been viewed.")), targ.c_str()); + + /* Send notification */ + memoserv->Send(source.u->nick, m->sender, text, true); + + /* Notify recepient 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->UnsetFlag(MF_RECEIPT); +} + +class MemoListCallback : public NumberList +{ + CommandSource &source; + MemoInfo *mi; + public: + MemoListCallback(CommandSource &_source, MemoInfo *_mi, const Anope::string &numlist) : NumberList(numlist, false), source(_source), mi(_mi) + { + } + + void HandleNumber(unsigned Number) + { + if (!Number || Number > mi->memos.size()) + return; + + MemoListCallback::DoRead(source, mi, NULL, Number - 1); + } + + static void DoRead(CommandSource &source, MemoInfo *mi, ChannelInfo *ci, unsigned index) + { + Memo *m = mi->memos[index]; + if (ci) + source.Reply(_("Memo %d from %s (%s). To delete, type: \002%s%s DEL %s %d\002"), index + 1, m->sender.c_str(), do_strftime(m->time).c_str(), Config->UseStrictPrivMsgString.c_str(), Config->MemoServ.c_str(), ci->name.c_str(), index + 1); + else + source.Reply(_("Memo %d from %s (%s). To delete, type: \002%s%s DEL %d\002"), index + 1, m->sender.c_str(), do_strftime(m->time).c_str(), Config->UseStrictPrivMsgString.c_str(), Config->MemoServ.c_str(), index + 1); + source.Reply("%s", m->text.c_str()); + m->UnsetFlag(MF_UNREAD); + + /* Check if a receipt notification was requested */ + if (m->HasFlag(MF_RECEIPT)) + rsend_notify(source, mi, m, ci ? ci->name : source.u->nick); + } +}; + +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 ¶ms) + { + User *u = source.u; + + MemoInfo *mi; + ChannelInfo *ci = NULL; + Anope::string numstr = params[0], chan; + + if (!numstr.empty() && numstr[0] == '#') + { + chan = numstr; + numstr = params.size() > 1 ? params[1] : ""; + + if (!(ci = cs_findchan(chan))) + { + source.Reply(CHAN_X_NOT_REGISTERED, chan.c_str()); + return; + } + else if (!ci->HasPriv(u, CA_MEMO)) + { + source.Reply(ACCESS_DENIED); + return; + } + mi = &ci->memos; + } + else + mi = &u->Account()->memos; + + if (numstr.empty() || (!numstr.equals_ci("LAST") && !numstr.equals_ci("NEW") && !numstr.is_number_only())) + 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->memos[i]->HasFlag(MF_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, numstr); + list.Process(); + } + } + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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, CORE), + commandmsread(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandmsread); + } +}; + +MODULE_INIT(MSRead) diff --git a/modules/commands/ms_rsend.cpp b/modules/commands/ms_rsend.cpp new file mode 100644 index 000000000..37c633e02 --- /dev/null +++ b/modules/commands/ms_rsend.cpp @@ -0,0 +1,106 @@ +/* MemoServ core functions + * + * (C) 2003-2011 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 "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 ¶ms) + { + User *u = source.u; + + const Anope::string &nick = params[0]; + const Anope::string &text = params[1]; + NickAlias *na = NULL; + + /* prevent user from rsend to themselves */ + if ((na = findnick(nick)) && na->nc == u->Account()) + { + source.Reply(_("You can not request a receipt when sending a memo to yourself.")); + return; + } + + if (Config->MSMemoReceipt == 1 && !u->IsServicesOper()) + source.Reply(ACCESS_DENIED); + else if (Config->MSMemoReceipt > 2 || Config->MSMemoReceipt == 0) + { + Log() << "MSMemoReceipt is set misconfigured to " << Config->MSMemoReceipt; + source.Reply(_("Sorry, RSEND has been disabled on this network.")); + } + else + { + MemoServService::MemoResult result = memoserv->Send(u->nick, 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 SEND command again."), Config->MSSendDelay); + else if (result == MemoServService::MEMO_TARGET_FULL) + source.Reply(_("%s currently has too many memos and cannot receive more."), nick.c_str()); + else + { + source.Reply(_("Memo sent to \002%s\002."), name.c_str()); + + bool ischan; + MemoInfo *mi = memoserv->GetMemoInfo(nick, ischan); + if (mi == NULL) + throw CoreException("NULL mi in ms_rsend"); + Memo *m = (mi->memos.size() ? mi->memos[mi->memos.size() - 1] : NULL); + if (m != NULL) + m->SetFlag(MF_RECEIPT); + } + } + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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, CORE), + commandmsrsend(this) + { + this->SetAuthor("Anope"); + + if (!Config->MSMemoReceipt) + throw ModuleException("Invalid value for memoreceipt"); + + ModuleManager::RegisterService(&commandmsrsend); + } +}; + +MODULE_INIT(MSRSend) diff --git a/modules/commands/ms_send.cpp b/modules/commands/ms_send.cpp new file mode 100644 index 000000000..9397e7ad7 --- /dev/null +++ b/modules/commands/ms_send.cpp @@ -0,0 +1,70 @@ +/* MemoServ core functions + * + * (C) 2003-2011 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 "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(_("{\037nick\037 | \037channel\037} \037memo-text\037")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + const Anope::string &nick = params[0]; + const Anope::string &text = params[1]; + + MemoServService::MemoResult result = memoserv->Send(source.u->nick, nick, text); + if (result == MemoServService::MEMO_SUCCESS) + source.Reply(_("Memo sent to \002%s\002."), nick.c_str()); + 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 SEND command again."), Config->MSSendDelay); + else if (result == MemoServService::MEMO_TARGET_FULL) + source.Reply(_("%s currently has too many memos and cannot receive more."), nick.c_str()); + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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, CORE), + commandmssend(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandmssend); + } +}; + +MODULE_INIT(MSSend) diff --git a/modules/commands/ms_sendall.cpp b/modules/commands/ms_sendall.cpp new file mode 100644 index 000000000..7af5ac7af --- /dev/null +++ b/modules/commands/ms_sendall.cpp @@ -0,0 +1,74 @@ +/* MemoServ core functions + * + * (C) 2003-2011 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 "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 ¶ms) + { + User *u = source.u; + const Anope::string &text = params[0]; + + if (readonly) + { + source.Reply(MEMO_SEND_DISABLED); + return; + } + + NickAlias *na = findnick(u->nick); + + for (nickcore_map::const_iterator it = NickCoreList.begin(), it_end = NickCoreList.end(); it != it_end; ++it) + { + NickCore *nc = it->second; + + if ((na && na->nc == nc) || !nc->display.equals_ci(u->nick)) + memoserv->Send(u->nick, nc->display, text); + } + + source.Reply(_("A massmemo has been sent to all registered users.")); + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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, CORE), + commandmssendall(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandmssendall); + } +}; + +MODULE_INIT(MSSendAll) diff --git a/modules/commands/ms_set.cpp b/modules/commands/ms_set.cpp new file mode 100644 index 000000000..1aa3f469d --- /dev/null +++ b/modules/commands/ms_set.cpp @@ -0,0 +1,313 @@ +/* MemoServ core functions + * + * (C) 2003-2011 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 ¶ms, MemoInfo *mi) + { + User *u = source.u; + const Anope::string ¶m = params[1]; + + if (param.equals_ci("ON")) + { + u->Account()->SetFlag(NI_MEMO_SIGNON); + u->Account()->SetFlag(NI_MEMO_RECEIVE); + source.Reply(_("%s will now notify you of memos when you log on and when they are sent to you."), Config->MemoServ.c_str()); + } + else if (param.equals_ci("LOGON")) + { + u->Account()->SetFlag(NI_MEMO_SIGNON); + u->Account()->UnsetFlag(NI_MEMO_RECEIVE); + source.Reply(_("%s will now notify you of memos when you log on or unset /AWAY."), Config->MemoServ.c_str()); + } + else if (param.equals_ci("NEW")) + { + u->Account()->UnsetFlag(NI_MEMO_SIGNON); + u->Account()->SetFlag(NI_MEMO_RECEIVE); + source.Reply(_("%s will now notify you of memos when they are sent to you."), Config->MemoServ.c_str()); + } + else if (param.equals_ci("MAIL")) + { + if (!u->Account()->email.empty()) + { + u->Account()->SetFlag(NI_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")) + { + u->Account()->UnsetFlag(NI_MEMO_MAIL); + source.Reply(_("You will no longer be informed via email.")); + } + else if (param.equals_ci("OFF")) + { + u->Account()->UnsetFlag(NI_MEMO_SIGNON); + u->Account()->UnsetFlag(NI_MEMO_RECEIVE); + u->Account()->UnsetFlag(NI_MEMO_MAIL); + source.Reply(_("%s will not send you any notification of memos."), Config->MemoServ.c_str()); + } + else + this->OnSyntaxError(source, param); + + return; + } + + void DoLimit(CommandSource &source, const std::vector ¶ms, MemoInfo *mi) + { + User *u = source.u; + + 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 limit; + NickCore *nc = u->Account(); + ChannelInfo *ci = NULL; + bool is_servadmin = u->HasPriv("memoserv/set-limit"); + + if (p1[0] == '#') + { + chan = p1; + p1 = p2; + p2 = p3; + p3 = params.size() > 4 ? params[4] : ""; + if (!(ci = cs_findchan(chan))) + { + source.Reply(CHAN_X_NOT_REGISTERED, chan.c_str()); + return; + } + else if (!is_servadmin && !ci->HasPriv(u, CA_MEMO)) + { + source.Reply(ACCESS_DENIED); + return; + } + mi = &ci->memos; + } + if (is_servadmin) + { + if (!p2.empty() && !p2.equals_ci("HARD") && chan.empty()) + { + NickAlias *na; + if (!(na = findnick(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->SetFlag(CI_MEMO_HARDMAX); + else + ci->UnsetFlag(CI_MEMO_HARDMAX); + } + else + { + if (!p2.empty()) + nc->SetFlag(NI_MEMO_HARDMAX); + else + nc->UnsetFlag(NI_MEMO_HARDMAX); + } + limit = -1; + try + { + limit = convertTo(p1); + } + catch (const ConvertException &) { } + } + else + { + if (p1.empty() || !p2.empty() || !isdigit(p1[0])) + { + this->OnSyntaxError(source, ""); + return; + } + if (!chan.empty() && ci->HasFlag(CI_MEMO_HARDMAX)) + { + source.Reply(_("The memo limit for %s may not be changed."), chan.c_str()); + return; + } + else if (chan.empty() && nc->HasFlag(NI_MEMO_HARDMAX)) + { + source.Reply(_("You are not permitted to change your memo limit.")); + return; + } + limit = -1; + try + { + limit = convertTo(p1); + } + catch (const ConvertException &) { } + /* The first character is a digit, but we could still go negative + * from overflow... watch out! */ + if (limit < 0 || (Config->MSMaxMemos > 0 && static_cast(limit) > Config->MSMaxMemos)) + { + if (!chan.empty()) + source.Reply(_("You cannot set the memo limit for %s higher than %d."), chan.c_str(), Config->MSMaxMemos); + else + source.Reply(_("You cannot set your memo limit higher than %d."), Config->MSMaxMemos); + return; + } + } + mi->memomax = limit; + if (limit > 0) + { + if (chan.empty() && nc == u->Account()) + 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 == u->Account()) + 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 == u->Account()) + 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 ¶ms) + { + User *u = source.u; + const Anope::string &cmd = params[0]; + MemoInfo *mi = &u->Account()->memos; + + if (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 + { + source.Reply(NICK_SET_UNKNOWN_OPTION, Config->UseStrictPrivMsgString.c_str(), cmd.c_str()); + source.Reply(MORE_INFO, Config->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str(), "SET"); + } + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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->UseStrictPrivMsgString.c_str(), source.owner->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" + "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 aswell 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")) + { + User *u = source.u; + if (u->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" + "This use of the \002SET LIMIT\002 command is limited to \002Services\002\n" + "\002admins\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."), Config->MSMaxMemos); + 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."), Config->MSMaxMemos); + } + else + return false; + + return true; + } +}; + +class MSSet : public Module +{ + CommandMSSet commandmsset; + + public: + MSSet(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandmsset(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandmsset); + } +}; + +MODULE_INIT(MSSet) diff --git a/modules/commands/ms_staff.cpp b/modules/commands/ms_staff.cpp new file mode 100644 index 000000000..6264dbea4 --- /dev/null +++ b/modules/commands/ms_staff.cpp @@ -0,0 +1,71 @@ +/* MemoServ core functions + * + * (C) 2003-2011 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 "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 ¶ms) + { + const Anope::string &text = params[0]; + + if (readonly) + { + source.Reply(MEMO_SEND_DISABLED); + return; + } + + for (nickcore_map::const_iterator it = NickCoreList.begin(), it_end = NickCoreList.end(); it != it_end; ++it) + { + NickCore *nc = it->second; + + if (nc->IsServicesOper()) + memoserv->Send(source.u->nick, nc->display, text, true); + } + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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, CORE), + commandmsstaff(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandmsstaff); + } +}; + +MODULE_INIT(MSStaff) diff --git a/modules/commands/ns_access.cpp b/modules/commands/ns_access.cpp new file mode 100644 index 000000000..df9ae6eed --- /dev/null +++ b/modules/commands/ns_access.cpp @@ -0,0 +1,195 @@ +/* NickServ core functions + * + * (C) 2003-2011 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 DoServAdminList(CommandSource &source, const std::vector ¶ms, NickCore *nc) + { + Anope::string mask = params.size() > 2 ? params[2] : ""; + unsigned i, end; + + if (nc->access.empty()) + { + source.Reply(_("Access list for \002%s\002 is empty."), nc->display.c_str()); + return; + } + + if (nc->HasFlag(NI_SUSPENDED)) + { + source.Reply(NICK_X_SUSPENDED, nc->display.c_str()); + return; + } + + source.Reply(_("Access list for \002%s\002:"), params[1].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; + } + + void DoAdd(CommandSource &source, NickCore *nc, const Anope::string &mask) + { + + if (mask.empty()) + { + this->OnSyntaxError(source, "ADD"); + return; + } + + if (nc->access.size() >= Config->NSAccessMax) + { + source.Reply(_("Sorry, you can only have %d access entries for a nickname."), Config->NSAccessMax); + return; + } + + if (nc->FindAccess(mask)) + { + source.Reply(_("Mask \002%s\002 already present on your access list."), mask.c_str()); + return; + } + + nc->AddAccess(mask); + source.Reply(_("\002%s\002 added to your access list."), mask.c_str()); + + return; + } + + void DoDel(CommandSource &source, NickCore *nc, const Anope::string &mask) + { + if (mask.empty()) + { + this->OnSyntaxError(source, "DEL"); + return; + } + + if (!nc->FindAccess(mask)) + { + source.Reply(_("\002%s\002 not found on your access list."), mask.c_str()); + return; + } + + source.Reply(_("\002%s\002 deleted from your access list."), mask.c_str()); + nc->EraseAccess(mask); + + return; + } + + void DoList(CommandSource &source, NickCore *nc, const Anope::string &mask) + { + User *u = source.u; + unsigned i, end; + + if (nc->access.empty()) + { + source.Reply(_("Your access list is empty."), u->nick.c_str()); + return; + } + + source.Reply(_("Access list:")); + 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 \037mask\037")); + this->SetSyntax(_("DEL \037mask\037")); + this->SetSyntax(_("LIST")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + const Anope::string &cmd = params[0]; + const Anope::string &mask = params.size() > 1 ? params[1] : ""; + + NickAlias *na; + if (cmd.equals_ci("LIST") && u->IsServicesOper() && !mask.empty() && (na = findnick(params[1]))) + return this->DoServAdminList(source, params, na->nc); + + if (!mask.empty() && mask.find('@') == Anope::string::npos) + { + source.Reply(BAD_USERHOST_MASK); + source.Reply(MORE_INFO, Config->UseStrictPrivMsgString.c_str(), Config->NickServ.c_str(), this->name.c_str()); + } + else if (u->Account()->HasFlag(NI_SUSPENDED)) + source.Reply(NICK_X_SUSPENDED, u->Account()->display.c_str()); + else if (cmd.equals_ci("ADD")) + return this->DoAdd(source, u->Account(), mask); + else if (cmd.equals_ci("DEL")) + return this->DoDel(source, u->Account(), mask); + else if (cmd.equals_ci("LIST")) + return this->DoList(source, u->Account(), mask); + else + this->OnSyntaxError(source, ""); + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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.\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."), Config->NickServ.c_str(), Config->NickServ.c_str()); + return true; + } +}; + +class NSAccess : public Module +{ + CommandNSAccess commandnsaccess; + + public: + NSAccess(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandnsaccess(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandnsaccess); + } +}; + +MODULE_INIT(NSAccess) diff --git a/modules/commands/ns_ajoin.cpp b/modules/commands/ns_ajoin.cpp new file mode 100644 index 000000000..4f35f28b8 --- /dev/null +++ b/modules/commands/ns_ajoin.cpp @@ -0,0 +1,239 @@ +/* NickServ core functions + * + * (C) 2003-2011 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 CommandNSAJoin : public Command +{ + void DoList(CommandSource &source, const std::vector ¶ms) + { + std::vector > channels; + source.u->Account()->GetExtRegular("ns_ajoin_channels", channels); + + if (channels.empty()) + source.Reply(_("Your auto join list is empty.")); + else + { + source.Reply(_("Your auto join list:\n" + " Num Channel Key")); + for (unsigned i = 0; i < channels.size(); ++i) + source.Reply(" %3d %-12s %s", i + 1, channels[i].first.c_str(), channels[i].second.c_str()); + } + } + + void DoAdd(CommandSource &source, const std::vector ¶ms) + { + std::vector > channels; + source.u->Account()->GetExtRegular("ns_ajoin_channels", channels); + + if (channels.size() >= Config->AJoinMax) + source.Reply(_("Your auto join list is full.")); + else if (ircdproto->IsChannelValid(params[1]) == false) + source.Reply(CHAN_X_INVALID, params[1].c_str()); + else + { + channels.push_back(std::make_pair(params[1], params.size() > 2 ? params[2] : "")); + source.Reply(_("Added %s to your auto join list."), params[1].c_str()); + source.u->Account()->Extend("ns_ajoin_channels", new ExtensibleItemRegular > >(channels)); + } + } + + void DoDel(CommandSource &source, const std::vector ¶ms) + { + std::vector > channels; + source.u->Account()->GetExtRegular("ns_ajoin_channels", channels); + + unsigned i; + for (i = 0; i < channels.size(); ++i) + if (channels[i].first.equals_ci(params[1])) + break; + + if (i == channels.size()) + source.Reply(_("%s was not found on your auto join list."), params[1].c_str()); + else + { + channels.erase(channels.begin() + i); + source.u->Account()->Extend("ns_ajoin_channels", new ExtensibleItemRegular > >(channels)); + source.Reply(_("%s was removed from your auto join list."), params[1].c_str()); + } + } + + public: + CommandNSAJoin(Module *creator) : Command(creator, "nickserv/ajoin", 1, 3) + { + this->SetDesc(_("Manage your auto join list")); + this->SetSyntax(_("{ADD | DEL | LIST} [\037channel\037] [\037key\037]")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + if (params[0].equals_ci("LIST")) + this->DoList(source, params); + else if (params.size() < 2) + this->OnSyntaxError(source, ""); + else if (params[0].equals_ci("ADD")) + this->DoAdd(source, params); + else if (params[0].equals_ci("DEL")) + this->DoDel(source, params); + else + this->OnSyntaxError(source, ""); + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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")); + return true; + } +}; + +class NSAJoin : public Module +{ + CommandNSAJoin commandnsajoin; + + public: + NSAJoin(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandnsajoin(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandnsajoin); + + Implementation i[] = { I_OnNickIdentify, I_OnDatabaseWriteMetadata, I_OnDatabaseReadMetadata }; + ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); + } + + void OnNickIdentify(User *u) + { + std::vector > channels; + u->Account()->GetExtRegular("ns_ajoin_channels", channels); + + for (unsigned i = 0; i < channels.size(); ++i) + { + Channel *c = findchan(channels[i].first); + ChannelInfo *ci = c != NULL ? c->ci : cs_findchan(channels[i].first); + if (c == NULL && ci != NULL) + c = ci->c; + + bool need_invite = false; + Anope::string key = channels[i].second; + + if (ci != NULL) + { + if (ci->HasFlag(CI_SUSPENDED)) + continue; + } + if (c != NULL) + { + if (c->FindUser(u) != NULL) + continue; + else if (c->HasMode(CMODE_OPERONLY) && !u->HasMode(UMODE_OPER)) + continue; + else if (c->HasMode(CMODE_ADMINONLY) && !u->HasMode(UMODE_ADMIN)) + continue; + else if (c->HasMode(CMODE_SSL) && !u->HasMode(UMODE_SSL)) + continue; + else if (matches_list(c, u, CMODE_BAN) == true && matches_list(c, u, CMODE_EXCEPT) == false) + need_invite = true; + else if (c->HasMode(CMODE_INVITE) && matches_list(c, u, CMODE_INVITEOVERRIDE) == false) + need_invite = true; + + if (c->HasMode(CMODE_KEY)) + { + Anope::string k; + if (c->GetParam(CMODE_KEY, k)) + { + if (ci->HasPriv(u, CA_GETKEY)) + key = k; + else if (key != k) + need_invite = true; + } + } + if (c->HasMode(CMODE_LIMIT)) + { + Anope::string l; + if (c->GetParam(CMODE_LIMIT, l)) + { + try + { + unsigned limit = convertTo(l); + if (c->users.size() >= limit) + need_invite = true; + } + catch (const ConvertException &) { } + } + } + } + + if (need_invite) + { + BotInfo *bi = findbot(Config->NickServ); + if (!bi || !ci->HasPriv(u, CA_INVITE)) + continue; + ircdproto->SendInvite(bi, channels[i].first, u->nick); + } + + ircdproto->SendSVSJoin(Config->NickServ, u->nick, channels[i].first, key); + } + } + + void OnDatabaseWriteMetadata(void (*WriteMetadata)(const Anope::string &, const Anope::string &), NickCore *nc) + { + std::vector > channels; + nc->GetExtRegular("ns_ajoin_channels", channels); + + Anope::string chans; + for (unsigned i = 0; i < channels.size(); ++i) + chans += " " + channels[i].first + "," + channels[i].second; + + if (!chans.empty()) + { + chans.erase(chans.begin()); + WriteMetadata("NS_AJOIN", chans); + } + } + + EventReturn OnDatabaseReadMetadata(NickCore *nc, const Anope::string &key, const std::vector ¶ms) + { + if (key == "NS_AJOIN") + { + std::vector > channels; + nc->GetExtRegular("ns_ajoin_channels", channels); + + for (unsigned i = 0; i < params.size(); ++i) + { + Anope::string chan, chankey; + commasepstream sep(params[i]); + sep.GetToken(chan); + sep.GetToken(chankey); + + channels.push_back(std::make_pair(chan, chankey)); + } + + nc->Extend("ns_ajoin_channels", new ExtensibleItemRegular > >(channels)); + + return EVENT_STOP; + } + + return EVENT_CONTINUE; + } +}; + +MODULE_INIT(NSAJoin) diff --git a/modules/commands/ns_alist.cpp b/modules/commands/ns_alist.cpp new file mode 100644 index 000000000..f502b4344 --- /dev/null +++ b/modules/commands/ns_alist.cpp @@ -0,0 +1,96 @@ +/* NickServ core functions + * + * (C) 2003-2011 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 +{ + 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 ¶ms) + { + User *u = source.u; + Anope::string nick = u->Account()->display; + + if (params.size() && u->IsServicesOper()) + nick = params[0]; + + NickAlias *na = findnick(nick); + + if (!na) + source.Reply(NICK_X_NOT_REGISTERED, nick.c_str()); + else + { + int chan_count = 0; + + source.Reply(_("Channels that \002%s\002 has access on:\n" + " Num Channel Level"), na->nick.c_str()); + + for (registered_channel_map::const_iterator it = RegisteredChannelList.begin(), it_end = RegisteredChannelList.end(); it != it_end; ++it) + { + ChannelInfo *ci = it->second; + + AccessGroup access = ci->AccessFor(na->nc); + if (access.empty()) + continue; + + ++chan_count; + + if (access.size() > 1) + { + source.Reply(_(" %3d You match %d access entries on %c%s, they are"), chan_count, access.size(), ci->HasFlag(CI_NO_EXPIRE) ? '!' : ' ', ci->name.c_str()); + for (unsigned i = 0; i < access.size(); ++i) + source.Reply(_(" %3d %-8s"), i + 1, access[i]->Serialize().c_str()); + } + else + source.Reply(_(" %3d %c%-20s %-8s"), chan_count, ci->HasFlag(CI_NO_EXPIRE) ? '!' : ' ', ci->name.c_str(), access[0]->Serialize().c_str()); + } + + source.Reply(_("End of list - %d channels shown."), chan_count); + } + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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, CORE), + commandnsalist(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandnsalist); + } +}; + +MODULE_INIT(NSAList) diff --git a/modules/commands/ns_cert.cpp b/modules/commands/ns_cert.cpp new file mode 100644 index 000000000..fbd75d597 --- /dev/null +++ b/modules/commands/ns_cert.cpp @@ -0,0 +1,231 @@ +/* NickServ core functions + * + * (C) 2003-2011 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 CommandNSCert : public Command +{ + private: + void DoServAdminList(CommandSource &source, NickCore *nc) + { + if (nc->cert.empty()) + { + source.Reply(_("Certificate list for \002%s\002 is empty."), nc->display.c_str()); + return; + } + + if (nc->HasFlag(NI_SUSPENDED)) + { + source.Reply(NICK_X_SUSPENDED, nc->display.c_str()); + return; + } + + source.Reply(_("Certificate list for \002%s\002:"), nc->display.c_str()); + for (unsigned i = 0, end = nc->cert.size(); i < end; ++i) + { + Anope::string fingerprint = nc->GetCert(i); + source.Reply(" %s", fingerprint.c_str()); + } + + return; + } + + void DoAdd(CommandSource &source, NickCore *nc, const Anope::string &mask) + { + + if (nc->cert.size() >= Config->NSAccessMax) + { + source.Reply(_("Sorry, you can only have %d certificate entries for a nickname."), Config->NSAccessMax); + return; + } + + if (!source.u->fingerprint.empty() && !nc->FindCert(source.u->fingerprint)) + { + nc->AddCert(source.u->fingerprint); + source.Reply(_("\002%s\002 added to your certificate list."), source.u->fingerprint.c_str()); + return; + } + + if (mask.empty()) + { + this->OnSyntaxError(source, "ADD"); + return; + } + + if (nc->FindCert(mask)) + { + source.Reply(_("Fingerprint \002%s\002 already present on your certificate list."), mask.c_str()); + return; + } + + nc->AddCert(mask); + source.Reply(_("\002%s\002 added to your certificate list."), mask.c_str()); + return; + } + + void DoDel(CommandSource &source, NickCore *nc, const Anope::string &mask) + { + + if (!source.u->fingerprint.empty() && nc->FindCert(source.u->fingerprint)) + { + nc->EraseCert(source.u->fingerprint); + source.Reply(_("\002%s\002 deleted from your certificate list."), source.u->fingerprint.c_str()); + return; + } + + if (mask.empty()) + { + this->OnSyntaxError(source, "DEL"); + return; + } + + if (!nc->FindCert(mask)) + { + source.Reply(_("\002%s\002 not found on your certificate list."), mask.c_str()); + return; + } + + source.Reply(_("\002%s\002 deleted from your certificate list."), mask.c_str()); + nc->EraseCert(mask); + + return; + } + + void DoList(CommandSource &source, NickCore *nc) + { + User *u = source.u; + + if (nc->cert.empty()) + { + source.Reply(_("Your certificate list is empty."), u->nick.c_str()); + return; + } + + source.Reply(_("Cert list:")); + for (unsigned i = 0, end = nc->cert.size(); i < end; ++i) + { + Anope::string fingerprint = nc->GetCert(i); + source.Reply(" %s", fingerprint.c_str()); + } + + return; + } + + public: + CommandNSCert(Module *creator) : Command(creator, "nickserv/cert", 1, 2) + { + this->SetDesc("Modify the nickname client certificate list"); + this->SetSyntax("ADD \037fingerprint\037"); + this->SetSyntax("DEL \037fingerprint\037"); + this->SetSyntax("LIST"); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + const Anope::string &cmd = params[0]; + const Anope::string &mask = params.size() > 1 ? params[1] : ""; + + NickAlias *na; + if (cmd.equals_ci("LIST") && u->IsServicesOper() && !mask.empty() && (na = findnick(mask))) + return this->DoServAdminList(source, na->nc); + + if (u->Account()->HasFlag(NI_SUSPENDED)) + source.Reply(NICK_X_SUSPENDED, u->Account()->display.c_str()); + else if (cmd.equals_ci("ADD")) + return this->DoAdd(source, u->Account(), mask); + else if (cmd.equals_ci("DEL")) + return this->DoDel(source, u->Account(), mask); + else if (cmd.equals_ci("LIST")) + return this->DoList(source, u->Account()); + else + this->OnSyntaxError(source, cmd); + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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, your nick will be\n" + "automatically identified to %s.\n" + " \n"), Config->NickServ.c_str(), Config->NickServ.c_str()); + source.Reply(_("Examples:\n" + " \n" + " \002CERT ADD \002\n" + " Adds this fingerprint to the certificate list and\n" + " automatically identifies you when you connect to IRC\n" + " using this certificate.\n" + " \n" + " \002CERT DEL \002\n" + " Reverses the previous command.\n" + " \n" + " \002CERT LIST\002\n" + " Displays the current certificate list."), Config->NickServ.c_str()); + return true; + } +}; + +class NSCert : public Module +{ + CommandNSCert commandnscert; + + void DoAutoIdentify(User *u) + { + BotInfo *bi = findbot(Config->NickServ); + NickAlias *na = findnick(u->nick); + if (!bi || !na) + return; + if (u->IsIdentified() && u->Account() == na->nc) + return; + if (na->nc->HasFlag(NI_SUSPENDED)) + return; + if (!na->nc->FindCert(u->fingerprint)) + return; + + u->Identify(na); + u->SendMessage(bi, _("SSL Fingerprint accepted. You are now identified.")); + return; + } + + public: + NSCert(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandnscert(this) + { + this->SetAuthor("Anope"); + + if (!ircd || !ircd->certfp) + throw ModuleException("Your IRCd does not support ssl client certificates"); + + Implementation i[] = { I_OnUserNickChange, I_OnFingerprint }; + ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); + + ModuleManager::RegisterService(&commandnscert); + } + + void OnFingerprint(User *u) + { + DoAutoIdentify(u); + } + + void OnUserNickChange(User *u, const Anope::string &oldnick) + { + if (!u->fingerprint.empty()) + DoAutoIdentify(u); + } +}; + +MODULE_INIT(NSCert) diff --git a/modules/commands/ns_drop.cpp b/modules/commands/ns_drop.cpp new file mode 100644 index 000000000..7960652ff --- /dev/null +++ b/modules/commands/ns_drop.cpp @@ -0,0 +1,130 @@ +/* NickServ core functions + * + * (C) 2003-2011 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", 0, 1) + { + this->SetFlag(CFLAG_ALLOW_UNREGISTERED); + this->SetDesc(_("Cancel the registration of a nickname")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + Anope::string nick = !params.empty() ? params[0] : ""; + + if (readonly) + { + source.Reply(_("Sorry, nickname de-registration is temporarily disabled.")); + return; + } + + NickAlias *na = findnick((u->Account() && !nick.empty() ? nick : u->nick)); + if (!na) + { + source.Reply(NICK_NOT_REGISTERED); + return; + } + + if (!u->Account()) + { + source.Reply(NICK_IDENTIFY_REQUIRED, Config->UseStrictPrivMsgString.c_str(), Config->NickServ.c_str()); + return; + } + + bool is_mine = u->Account() == na->nc; + Anope::string my_nick; + if (is_mine && nick.empty()) + my_nick = na->nick; + + if (!is_mine && !u->HasPriv("nickserv/drop")) + source.Reply(ACCESS_DENIED); + else if (Config->NSSecureAdmins && !is_mine && na->nc->IsServicesOper()) + source.Reply(ACCESS_DENIED); + else + { + if (readonly) + source.Reply(READ_ONLY_MODE); + + FOREACH_MOD(I_OnNickDrop, OnNickDrop(u, na)); + + Log(!is_mine ? LOG_OVERRIDE : LOG_COMMAND, u, this) << "to drop nickname " << na->nick << " (group: " << na->nc->display << ") (email: " << (!na->nc->email.empty() ? na->nc->email : "none") << ")"; + delete na; + + if (!is_mine) + { + source.Reply(_("Nickname \002%s\002 has been dropped."), nick.c_str()); + } + else + { + if (!nick.empty()) + source.Reply(_("Nickname \002%s\002 has been dropped."), nick.c_str()); + else + source.Reply(_("Your nickname has been dropped.")); + } + } + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + User *u = source.u; + if (u->Account() && u->HasPriv("nickserv/drop")) + source.Reply(_("Syntax: \002%s [\037nickname\037]\002\n" + " \n" + "Without a parameter, deletes your nickname.\n" + " \n" + "With a parameter, drops the named nick from the database.\n" + "You may drop any nick within your group without any \n" + "special privileges. Dropping any nick is limited to \n" + "\002Services Operators\002."), source.command.c_str()); + else + source.Reply(_("Syntax: \002%s [\037nickname\037 | \037password\037]\002\n" + " \n" + "Deltes your nickname. A nick\n" + "that has been dropped is free for anyone to re-register.\n" + " \n" + "You may drop a nick within your group by passing it\n" + "as the \002nick\002 parameter.\n" + " \n" + "If you have a nickname registration pending but can not confirm\n" + "it for any reason, you can cancel your registration by passing\n" + "your password as the \002password\002 parameter.\n" + " \n" + "In order to use this command, you must first identify\n" + "with your password."), source.command.c_str()); + + return true; + } +}; + +class NSDrop : public Module +{ + CommandNSDrop commandnsdrop; + + public: + NSDrop(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandnsdrop(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandnsdrop); + } +}; + +MODULE_INIT(NSDrop) diff --git a/modules/commands/ns_getemail.cpp b/modules/commands/ns_getemail.cpp new file mode 100644 index 000000000..fa27e5174 --- /dev/null +++ b/modules/commands/ns_getemail.cpp @@ -0,0 +1,82 @@ +/* NickServ core functions + * + * (C) 2003-2011 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(_("\037user@email-host\037")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + const Anope::string &email = params[0]; + int j = 0; + + Log(LOG_ADMIN, u, this) << "on " << email; + + for (nickcore_map::const_iterator it = NickCoreList.begin(), it_end = NickCoreList.end(); it != it_end; ++it) + { + NickCore *nc = it->second; + + if (!nc->email.empty() && nc->email.equals_ci(email)) + { + ++j; + source.Reply(_("Emails Match \002%s\002 to \002%s\002."), nc->display.c_str(), email.c_str()); + } + } + + if (j <= 0) + { + source.Reply(_("No Emails listed for \002%s\002."), email.c_str()); + return; + } + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Returns the matching nicks that used given email. \002Note\002 that\n" + "you can not use wildcards for either user or emailhost. Whenever\n" + "this command is used, a message including the person who issued\n" + "the command and the email it was used on will be logged.")); + return true; + } +}; + +class NSGetEMail : public Module +{ + CommandNSGetEMail commandnsgetemail; + public: + NSGetEMail(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandnsgetemail(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandnsgetemail); + } +}; + +MODULE_INIT(NSGetEMail) diff --git a/modules/commands/ns_getpass.cpp b/modules/commands/ns_getpass.cpp new file mode 100644 index 000000000..9ebea5263 --- /dev/null +++ b/modules/commands/ns_getpass.cpp @@ -0,0 +1,79 @@ +/* NickServ core functions + * + * (C) 2003-2011 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 ¶ms) + { + User *u = source.u; + const Anope::string &nick = params[0]; + Anope::string tmp_pass; + NickAlias *na; + + if (!(na = findnick(nick))) + source.Reply(NICK_X_NOT_REGISTERED, nick.c_str()); + else if (Config->NSSecureAdmins && na->nc->IsServicesOper()) + source.Reply(ACCESS_DENIED); + else + { + if (enc_decrypt(na->nc->pass, tmp_pass) == 1) + { + Log(LOG_ADMIN, u, 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) + { + 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, CORE), + commandnsgetpass(this) + { + this->SetAuthor("Anope"); + + Anope::string tmp_pass = "plain:tmp"; + if (enc_decrypt(tmp_pass, tmp_pass) == -1) + throw ModuleException("Incompatible with the encryption module being used"); + + ModuleManager::RegisterService(&commandnsgetpass); + } +}; + +MODULE_INIT(NSGetPass) diff --git a/modules/commands/ns_ghost.cpp b/modules/commands/ns_ghost.cpp new file mode 100644 index 000000000..37dcef9c7 --- /dev/null +++ b/modules/commands/ns_ghost.cpp @@ -0,0 +1,122 @@ +/* NickServ core functions + * + * (C) 2003-2011 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 CommandNSGhost : public Command +{ + public: + CommandNSGhost(Module *creator) : Command(creator, "nickserv/ghost", 1, 2) + { + this->SetFlag(CFLAG_ALLOW_UNREGISTERED); + this->SetDesc(_("Disconnects a \"ghost\" IRC session using your nick")); + this->SetSyntax("\037nickname\037 [\037password\037]"); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + const Anope::string &nick = params[0]; + const Anope::string &pass = params.size() > 1 ? params[1] : ""; + + User *u = source.u; + User *user = finduser(nick); + NickAlias *na = findnick(nick); + + if (!user) + source.Reply(NICK_X_NOT_IN_USE, nick.c_str()); + else if (!na) + source.Reply(NICK_X_NOT_REGISTERED, nick.c_str()); + else if (na->nc->HasFlag(NI_SUSPENDED)) + source.Reply(NICK_X_SUSPENDED, na->nick.c_str()); + else if (nick.equals_ci(u->nick)) + source.Reply(_("You can't ghost yourself!")); + else + { + bool ok = false; + if (u->Account() == na->nc) + ok = true; + else if (!na->nc->HasFlag(NI_SECURE) && is_on_access(u, na->nc)) + ok = true; + else if (!u->fingerprint.empty() && na->nc->FindCert(u->fingerprint)) + ok = true; + else if (!pass.empty()) + { + EventReturn MOD_RESULT; + FOREACH_RESULT(I_OnCheckAuthentication, OnCheckAuthentication(this, &source, params, na->nc->display, pass)); + if (MOD_RESULT == EVENT_STOP) + return; + else if (MOD_RESULT == EVENT_ALLOW) + ok = true; + } + + if (ok) + { + if (!user->IsIdentified()) + source.Reply(_("You may not ghost an unidentified user, use RECOVER instead.")); + else + { + Log(LOG_COMMAND, u, this) << "for " << nick; + Anope::string buf = "GHOST command used by " + u->nick; + user->Kill(Config->NickServ, buf); + source.Reply(_("Ghost with your nick has been killed."), nick.c_str()); + } + } + else + { + source.Reply(ACCESS_DENIED); + if (!pass.empty()) + { + Log(LOG_COMMAND, u, this) << "with an invalid password for " << nick; + bad_password(u); + } + } + } + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("itermminates a \"ghost\" IRC session using your nick. A\n" + "ghost\" session is one which is not actually connected,\n" + "but which the IRC server believes is still online for one\n" + "reason or another. Typically, this happens if your\n" + "computer crashes or your Internet or modem connection\n" + "goes down while you're on IRC.\n" + " \n" + "In order to use the \002GHOST\002 command for a nick, your\n" + "current address as shown in /WHOIS must be on that nick's\n" + "access list, you must be identified and in the group of\n" + "that nick, or you must supply the correct password for\n" + "the nickname.")); + return true; + } +}; + +class NSGhost : public Module +{ + CommandNSGhost commandnsghost; + + public: + NSGhost(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandnsghost(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandnsghost); + } +}; + +MODULE_INIT(NSGhost) diff --git a/modules/commands/ns_group.cpp b/modules/commands/ns_group.cpp new file mode 100644 index 000000000..2e23e1f51 --- /dev/null +++ b/modules/commands/ns_group.cpp @@ -0,0 +1,305 @@ +/* NickServ core functions + * + * (C) 2003-2011 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 CommandNSGroup : public Command +{ + public: + CommandNSGroup(Module *creator) : Command(creator, "nickserv/group", 1, 2) + { + this->SetFlag(CFLAG_ALLOW_UNREGISTERED); + this->SetDesc(_("Join a group")); + this->SetSyntax(_("\037target\037 \037password\037")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + + const Anope::string &nick = params[0]; + const Anope::string &pass = params.size() > 1 ? params[1] : ""; + + if (readonly) + { + source.Reply(_("Sorry, nickname grouping is temporarily disabled.")); + return; + } + + if (Config->RestrictOperNicks) + for (unsigned i = 0; i < Config->Opers.size(); ++i) + { + Oper *o = Config->Opers[i]; + + if (!u->HasMode(UMODE_OPER) && u->nick.find_ci(o->name) != Anope::string::npos) + { + source.Reply(NICK_CANNOT_BE_REGISTERED, u->nick.c_str()); + return; + } + } + + NickAlias *target, *na = findnick(u->nick); + if (!(target = findnick(nick))) + source.Reply(NICK_X_NOT_REGISTERED, nick.c_str()); + else if (Anope::CurTime < u->lastnickreg + Config->NSRegDelay) + source.Reply(_("Please wait %d seconds before using the GROUP command again."), (Config->NSRegDelay + u->lastnickreg) - Anope::CurTime); + else if (target && target->nc->HasFlag(NI_SUSPENDED)) + { + Log(LOG_COMMAND, u, this) << "tried to use GROUP for SUSPENDED nick " << target->nick; + source.Reply(NICK_X_SUSPENDED, target->nick.c_str()); + } + 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, Config->UseStrictPrivMsgString.c_str(), Config->NickServ.c_str()); + else if (na && Config->NSNoGroupChange) + source.Reply(_("Your nick is already registered.")); + else if (Config->NSMaxAliases && (target->nc->aliases.size() >= Config->NSMaxAliases) && !target->nc->IsServicesOper()) + source.Reply(_("There are too many nicks in %s's group.")); + else + { + bool ok = false; + if (!na && u->Account()) + ok = true; + else if (!u->fingerprint.empty() && target->nc->FindCert(u->fingerprint)) + ok = true; + else if (!pass.empty()) + { + EventReturn MOD_RESULT; + FOREACH_RESULT(I_OnCheckAuthentication, OnCheckAuthentication(this, &source, params, target->nc->display, pass)); + if (MOD_RESULT == EVENT_STOP) + return; + else if (MOD_RESULT == EVENT_ALLOW) + ok = true; + } + if (ok) + { + /* If the nick is already registered, drop it. + * If not, check that it is valid. + */ + if (na) + delete na; + else + { + size_t prefixlen = Config->NSGuestNickPrefix.length(); + size_t nicklen = u->nick.length(); + + if (nicklen <= prefixlen + 7 && nicklen >= prefixlen + 1 && !u->nick.find_ci(Config->NSGuestNickPrefix) && !u->nick.substr(prefixlen).find_first_not_of("1234567890")) + { + source.Reply(NICK_CANNOT_BE_REGISTERED, u->nick.c_str()); + return; + } + } + + na = new NickAlias(u->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(na->nc); + FOREACH_MOD(I_OnNickGroup, OnNickGroup(u, target)); + if (target->nc->HasFlag(NI_UNCONFIRMED) == false) + ircdproto->SendAccountLogin(u, u->Account()); + ircdproto->SetAutoIdentificationToken(u); + + Log(LOG_COMMAND, u, this) << "makes " << u->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; + } + else + { + Log(LOG_COMMAND, u, this) << "failed group for " << target->nick; + source.Reply(PASSWORD_INCORRECT); + bad_password(u); + } + } + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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]")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + Anope::string nick = !params.empty() ? params[0] : ""; + NickAlias *na = findnick(!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(_("The nick %s is not in your group."), na->nick.c_str()); + else + { + NickCore *oldcore = na->nc; + + std::list::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)) + change_core_display(oldcore); + + na->nc = new NickCore(na->nick); + na->nc->aliases.push_back(na); + + na->nc->pass = oldcore->pass; + if (!oldcore->email.empty()) + na->nc->email = oldcore->email; + if (!oldcore->greet.empty()) + na->nc->greet = oldcore->greet; + na->nc->language = oldcore->language; + + source.Reply(_("Nick %s has been ungrouped from %s."), na->nick.c_str(), oldcore->display.c_str()); + + User *user = finduser(na->nick); + if (user) + /* The user on the nick who was ungrouped may be identified to the old group, set -r */ + user->RemoveMode(findbot(Config->NickServ), UMODE_REGISTERED); + } + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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, url, and icq. Everything\n" + "else is reset. You may not ungroup yourself if there is only one\n" + "nick 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 ¶ms) + { + User *u = source.u; + Anope::string nick = !params.empty() ? params[0] : ""; + + const NickCore *nc = u->Account(); + + if (!nick.empty() && (!nick.equals_ci(u->nick) && !u->IsServicesOper())) + source.Reply(ACCESS_DENIED, Config->NickServ.c_str()); + else if (!nick.empty() && (!findnick(nick) || !(nc = findnick(nick)->nc))) + source.Reply(nick.empty() ? NICK_NOT_REGISTERED : _(NICK_X_NOT_REGISTERED), nick.c_str()); + else + { + source.Reply(!nick.empty() ? _("List of nicknames in the group of \002%s\002:") : _("List of nicknames in your group:"), nc->display.c_str()); + for (std::list::const_iterator it = nc->aliases.begin(), it_end = nc->aliases.end(); it != it_end; ++it) + { + NickAlias *na2 = *it; + + source.Reply(na2->HasFlag(NS_NO_EXPIRE) || !Config->NSExpire ? _(" %s (does not expire)") : _(" %s (expires in %s)"), na2->nick.c_str(), do_strftime(na2->last_seen + Config->NSExpire).c_str()); + } + source.Reply(_("%d nicknames in the group."), nc->aliases.size()); + } + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + User *u = source.u; + if (u->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" + "This use 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, CORE), + commandnsgroup(this), commandnsungroup(this), commandnsglist(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandnsgroup); + ModuleManager::RegisterService(&commandnsungroup); + ModuleManager::RegisterService(&commandnsglist); + } +}; + +MODULE_INIT(NSGroup) diff --git a/modules/commands/ns_identify.cpp b/modules/commands/ns_identify.cpp new file mode 100644 index 000000000..b0a375cd2 --- /dev/null +++ b/modules/commands/ns_identify.cpp @@ -0,0 +1,93 @@ +/* NickServ core functions + * + * (C) 2003-2011 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 CommandNSIdentify : public Command +{ + public: + CommandNSIdentify(Module *creator) : Command(creator, "nickserv/identify", 1, 2) + { + this->SetFlag(CFLAG_ALLOW_UNREGISTERED); + this->SetDesc(_("Identify yourself with your password")); + this->SetSyntax(_("[\037account\037] \037password\037")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + + const Anope::string &nick = params.size() == 2 ? params[0] : u->nick; + Anope::string pass = params[params.size() - 1]; + + NickAlias *na = findnick(nick); + if (na && na->nc->HasFlag(NI_SUSPENDED)) + source.Reply(NICK_X_SUSPENDED, na->nick.c_str()); + else if (u->Account() && na && u->Account() == na->nc) + source.Reply(_("You are already identified.")); + else + { + EventReturn MOD_RESULT; + FOREACH_RESULT(I_OnCheckAuthentication, OnCheckAuthentication(this, &source, params, na ? na->nc->display : nick, pass)); + if (MOD_RESULT == EVENT_STOP) + return; + + if (!na) + source.Reply(NICK_X_NOT_REGISTERED, nick.c_str()); + else if (MOD_RESULT != EVENT_ALLOW) + { + Log(LOG_COMMAND, u, this) << "and failed to identify"; + source.Reply(PASSWORD_INCORRECT); + bad_password(u); + } + else + { + if (u->IsIdentified()) + Log(LOG_COMMAND, u, this) << "to log out of account " << u->Account()->display; + + Log(LOG_COMMAND, u, this) << "and identified for account " << na->nc->display; + source.Reply(_("Password accepted - you are now recognized.")); + u->Identify(na); + } + } + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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.owner->nick.c_str()); + return true; + } +}; + +class NSIdentify : public Module +{ + CommandNSIdentify commandnsidentify; + + public: + NSIdentify(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandnsidentify(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandnsidentify); + } +}; + +MODULE_INIT(NSIdentify) diff --git a/modules/commands/ns_info.cpp b/modules/commands/ns_info.cpp new file mode 100644 index 000000000..5a6d9dc0c --- /dev/null +++ b/modules/commands/ns_info.cpp @@ -0,0 +1,173 @@ +/* NickServ core functions + * + * (C) 2003-2011 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 +{ + private: + // cannot be const, as it is modified + void CheckOptStr(Anope::string &buf, NickCoreFlag opt, const Anope::string &str, NickCore *nc, bool reverse_logic = false) + { + if (reverse_logic ? !nc->HasFlag(opt) : nc->HasFlag(opt)) + { + if (!buf.empty()) + buf += ", "; + + buf += str; + } + } + public: + CommandNSInfo(Module *creator) : Command(creator, "nickserv/info", 0, 2) + { + this->SetFlag(CFLAG_ALLOW_UNREGISTERED); + this->SetDesc(_("Displays information about a given nickname")); + this->SetSyntax(_("[\037nickname\037]")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + + const Anope::string &nick = params.size() ? params[0] : (u->Account() ? u->Account()->display : u->nick); + NickAlias *na = findnick(nick); + bool has_auspex = u->IsIdentified() && u->HasPriv("nickserv/auspex"); + + if (!na) + { + if (nickIsServices(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 = finduser(na->nick); + if (u2 && u2->Account() == na->nc) + nick_online = true; + + if (has_auspex || (u->Account() && na->nc == u->Account())) + show_hidden = true; + + source.Reply(_("%s is %s"), na->nick.c_str(), na->last_realname.c_str()); + + if (na->nc->IsServicesOper() && (show_hidden || !na->nc->HasFlag(NI_HIDE_STATUS))) + source.Reply(_("%s is a services operator of type %s."), na->nick.c_str(), na->nc->o->ot->GetName().c_str()); + + if (nick_online) + { + if (show_hidden || !na->nc->HasFlag(NI_HIDE_MASK)) + source.Reply(_(" Is online from: %s"), na->last_usermask.c_str()); + else + source.Reply(_("%s is currently online."), na->nick.c_str()); + } + else + { + if (show_hidden || !na->nc->HasFlag(NI_HIDE_MASK)) + source.Reply(_("Last seen address: %s"), na->last_usermask.c_str()); + } + + source.Reply(_(" Time registered: %s"), do_strftime(na->time_registered).c_str()); + + if (!nick_online) + { + source.Reply(_(" Last seen time: %s"), do_strftime(na->last_seen).c_str()); + } + + if (!na->last_quit.empty() && (show_hidden || !na->nc->HasFlag(NI_HIDE_QUIT))) + source.Reply(_("Last quit message: %s"), na->last_quit.c_str()); + + if (!na->nc->email.empty() && (show_hidden || !na->nc->HasFlag(NI_HIDE_EMAIL))) + source.Reply(_(" E-mail address: %s"), na->nc->email.c_str()); + + if (show_hidden) + { + if (na->hostinfo.HasVhost()) + { + if (ircd->vident && !na->hostinfo.GetIdent().empty()) + source.Reply(_(" vhost: %s@%s"), na->hostinfo.GetIdent().c_str(), na->hostinfo.GetHost().c_str()); + else + source.Reply(_(" vhost: %s"), na->hostinfo.GetHost().c_str()); + } + if (!na->nc->greet.empty()) + source.Reply(_(" Greet message: %s"), na->nc->greet.c_str()); + + Anope::string optbuf; + + CheckOptStr(optbuf, NI_KILLPROTECT, _("Protection"), na->nc); + CheckOptStr(optbuf, NI_SECURE, _("Security"), na->nc); + CheckOptStr(optbuf, NI_PRIVATE, _("Private"), na->nc); + CheckOptStr(optbuf, NI_MSG, _("Message mode"), na->nc); + CheckOptStr(optbuf, NI_AUTOOP, _("Auto-op"), na->nc); + + source.Reply(NICK_INFO_OPTIONS, optbuf.empty() ? _("None") : optbuf.c_str()); + + if (na->nc->HasFlag(NI_SUSPENDED)) + { + if (!na->last_quit.empty()) + source.Reply(_("This nickname is currently suspended, reason: %s"), na->last_quit.c_str()); + else + source.Reply(_("This nickname is currently suspended")); + } + + if (na->nc->HasFlag(NI_UNCONFIRMED) == false) + { + if (na->HasFlag(NS_NO_EXPIRE) || !Config->NSExpire) + source.Reply(_("This nickname will not expire.")); + else + source.Reply(_("Expires on: %s"), do_strftime(na->last_seen + Config->NSExpire).c_str()); + } + else + source.Reply(_("Expires on: %s"), do_strftime(na->time_registered + Config->NSUnconfirmedExpire).c_str()); + } + + FOREACH_MOD(I_OnNickInfo, OnNickInfo(source, na, show_hidden)); + + if (na->nc->HasFlag(NI_UNCONFIRMED)) + source.Reply(_("This nickname is unconfirmed.")); + } + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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 NSInfo : public Module +{ + CommandNSInfo commandnsinfo; + + public: + NSInfo(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandnsinfo(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandnsinfo); + } +}; + +MODULE_INIT(NSInfo) diff --git a/modules/commands/ns_list.cpp b/modules/commands/ns_list.cpp new file mode 100644 index 000000000..96049ba02 --- /dev/null +++ b/modules/commands/ns_list.cpp @@ -0,0 +1,169 @@ +/* NickServ core functions + * + * (C) 2003-2011 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 ¶ms) + { + User *u = source.u; + + Anope::string pattern = params[0]; + const NickCore *mync; + unsigned nnicks; + bool is_servadmin = u->IsServicesOper(); + char noexpire_char = ' '; + int count = 0, from = 0, to = 0; + bool suspended, nsnoexpire, unconfirmed; + + suspended = nsnoexpire = unconfirmed = false; + + if (pattern[0] == '#') + { + Anope::string n1 = myStrGetToken(pattern.substr(1), '-', 0), /* Read FROM out */ + n2 = myStrGetToken(pattern, '-', 1); + + try + { + from = convertTo(n1); + to = convertTo(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 = u->Account(); + + source.Reply(LIST_HEADER, pattern.c_str()); + for (nickalias_map::const_iterator it = NickAliasList.begin(), it_end = NickAliasList.end(); it != it_end; ++it) + { + NickAlias *na = it->second; + + /* Don't show private nicks to non-services admins. */ + if (na->nc->HasFlag(NI_PRIVATE) && !is_servadmin && na->nc != mync) + continue; + else if (nsnoexpire && !na->HasFlag(NS_NO_EXPIRE)) + continue; + else if (suspended && !na->nc->HasFlag(NI_SUSPENDED)) + continue; + else if (unconfirmed && !na->nc->HasFlag(NI_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)) + { + if (((count + 1 >= from && count + 1 <= to) || (!from && !to)) && ++nnicks <= Config->NSListMax) + { + if (is_servadmin && na->HasFlag(NS_NO_EXPIRE)) + noexpire_char = '!'; + else + noexpire_char = ' '; + if (na->nc->HasFlag(NI_HIDE_MASK) && !is_servadmin && na->nc != mync) + buf = Anope::printf("%-20s [Hostname Hidden]", na->nick.c_str()); + else if (na->nc->HasFlag(NI_SUSPENDED)) + buf = Anope::printf("%-20s [Suspended]", na->nick.c_str()); + else if (na->nc->HasFlag(NI_UNCONFIRMED)) + buf = Anope::printf("%-20s [Unconfirmed]", na->nick.c_str()); + else + buf = Anope::printf("%-20s %s", na->nick.c_str(), na->last_usermask.c_str()); + source.Reply(" %c%s", noexpire_char, buf.c_str()); + } + ++count; + } + } + + + source.Reply(_("End of list - %d/%d matches shown."), nnicks > Config->NSListMax ? Config->NSListMax : nnicks, nnicks); + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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. Nicks\n" + "with the \002NOEXPIRE\002 option set will have a \002!\002 appended to\n" + "the nickname for Services Operators.\n" + " \n" + "If the SUSPENDED, NOEXPIRE or UNCONFIRMED 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" + "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")); + + return true; + } +}; + +class NSList : public Module +{ + CommandNSList commandnslist; + + public: + NSList(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandnslist(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandnslist); + } +}; + +MODULE_INIT(NSList) diff --git a/modules/commands/ns_logout.cpp b/modules/commands/ns_logout.cpp new file mode 100644 index 000000000..476330a11 --- /dev/null +++ b/modules/commands/ns_logout.cpp @@ -0,0 +1,97 @@ +/* NickServ core functions + * + * (C) 2003-2011 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 "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 ¶ms) + { + User *u = source.u; + + const Anope::string &nick = !params.empty() ? params[0] : ""; + const Anope::string ¶m = params.size() > 1 ? params[1] : ""; + + User *u2; + if (!u->IsServicesOper() && !nick.empty()) + this->OnSyntaxError(source, ""); + else if (!(u2 = (!nick.empty() ? finduser(nick) : u))) + source.Reply(NICK_X_NOT_IN_USE, nick.c_str()); + else if (!nick.empty() && !u2->IsServicesOper()) + source.Reply(_("You can't logout %s because they are a Services Operator."), nick.c_str()); + else + { + if (!nick.empty() && !param.empty() && param.equals_ci("REVALIDATE") && nickserv) + nickserv->Validate(u2); + + u2->isSuperAdmin = 0; /* Dont let people logout and remain a SuperAdmin */ + Log(LOG_COMMAND, u, 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.")); + + ircdproto->SendAccountLogout(u2, u2->Account()); + u2->RemoveMode(source.owner, UMODE_REGISTERED); + ircdproto->SendUnregisteredNick(u2); + + u2->Logout(); + + /* Send out an event */ + FOREACH_MOD(I_OnNickLogout, OnNickLogout(u2)); + } + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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 REVALIDATE as well, Services will ask the given nick\n" + "to re-identify. This use 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, CORE), + commandnslogout(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandnslogout); + } +}; + +MODULE_INIT(NSLogout) diff --git a/modules/commands/ns_recover.cpp b/modules/commands/ns_recover.cpp new file mode 100644 index 000000000..5e45aab9a --- /dev/null +++ b/modules/commands/ns_recover.cpp @@ -0,0 +1,132 @@ +/* NickServ core functions + * + * (C) 2003-2011 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 CommandNSRecover : public Command +{ + public: + CommandNSRecover(Module *creator) : Command(creator, "nickserv/recover", 1, 2) + { + this->SetFlag(CFLAG_ALLOW_UNREGISTERED); + this->SetDesc(_("Kill another user who has taken your nick")); + this->SetSyntax(_("\037nickname\037 [\037password\037]")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + + const Anope::string &nick = params[0]; + const Anope::string &pass = params.size() > 1 ? params[1] : ""; + + NickAlias *na; + User *u2; + if (!(u2 = finduser(nick))) + source.Reply(NICK_X_NOT_IN_USE, nick.c_str()); + else if (!(na = findnick(u2->nick))) + source.Reply(NICK_X_NOT_REGISTERED, nick.c_str()); + else if (na->nc->HasFlag(NI_SUSPENDED)) + source.Reply(NICK_X_SUSPENDED, na->nick.c_str()); + else if (nick.equals_ci(u->nick)) + source.Reply(_("You can't recover yourself!")); + else if (!pass.empty()) + { + EventReturn MOD_RESULT; + FOREACH_RESULT(I_OnCheckAuthentication, OnCheckAuthentication(this, &source, params, na->nc->display, pass)); + if (MOD_RESULT == EVENT_STOP) + return; + + if (MOD_RESULT == EVENT_ALLOW) + { + u2->SendMessage(source.owner, FORCENICKCHANGE_NOW); + u2->Collide(na); + + /* Convert Config->NSReleaseTimeout seconds to string format */ + Anope::string relstr = duration(Config->NSReleaseTimeout); + + source.Reply(NICK_RECOVERED, Config->UseStrictPrivMsgString.c_str(), Config->NickServ.c_str(), nick.c_str(), relstr.c_str()); + } + else + { + source.Reply(ACCESS_DENIED); + Log(LOG_COMMAND, u, this) << "with invalid password for " << nick; + bad_password(u); + } + } + else + { + if (u->Account() == na->nc || (!na->nc->HasFlag(NI_SECURE) && is_on_access(u, na->nc)) || + (!u->fingerprint.empty() && na->nc->FindCert(u->fingerprint))) + { + u2->SendMessage(source.owner, FORCENICKCHANGE_NOW); + u2->Collide(na); + + /* Convert Config->NSReleaseTimeout seconds to string format */ + Anope::string relstr = duration(Config->NSReleaseTimeout); + + source.Reply(NICK_RECOVERED, Config->UseStrictPrivMsgString.c_str(), Config->NickServ.c_str(), nick.c_str(), relstr.c_str()); + } + else + source.Reply(ACCESS_DENIED); + } + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + /* Convert Config->NSReleaseTimeout seconds to string format */ + Anope::string relstr = duration(Config->NSReleaseTimeout); + + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Allows you to recover your nickname if someone else has\n" + "taken it; this does the same thing that %s does\n" + "automatically if someone tries to use a kill-protected\n" + "nick.\n" + " \n" + "When you give this command, %s will bring a fake\n" + "user online with the same nickname as the user you're\n" + "trying to recover your nick from. This causes the IRC\n" + "servers to disconnect the other user. This fake user will\n" + "remain online for %s to ensure that the other\n" + "user does not immediately reconnect; after that time, you\n" + "can reclaim your nick. Alternatively, use the \002RELEASE\002\n" + "command (\002%s%s HELP RELEASE\002) to get the nick\n" + "back sooner.\n" + " \n" + "In order to use the \002RECOVER\002 command for a nick, your\n" + "current address as shown in /WHOIS must be on that nick's\n" + "access list, you must be identified and in the group of\n" + "that nick, or you must supply the correct password for\n" + "the nickname."), Config->NickServ.c_str(), Config->NickServ.c_str(), relstr.c_str(), Config->UseStrictPrivMsgString.c_str(), Config->NickServ.c_str()); + + return true; + } +}; + +class NSRecover : public Module +{ + CommandNSRecover commandnsrecover; + + public: + NSRecover(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandnsrecover(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandnsrecover); + } +}; + +MODULE_INIT(NSRecover) diff --git a/modules/commands/ns_register.cpp b/modules/commands/ns_register.cpp new file mode 100644 index 000000000..552551994 --- /dev/null +++ b/modules/commands/ns_register.cpp @@ -0,0 +1,355 @@ +/* NickServ core functions + * + * (C) 2003-2011 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, NickAlias *na, BotInfo *bi); + +class CommandNSConfirm : public Command +{ + public: + CommandNSConfirm(Module *creator) : Command(creator, "nickserv/confirm", 1, 2) + { + this->SetFlag(CFLAG_ALLOW_UNREGISTERED); + this->SetDesc(_("Confirm an auth code")); + this->SetSyntax(_("\037passcode\037")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + const Anope::string &passcode = params[0]; + + if (u->Account() && u->HasPriv("nickserv/confirm")) + { + NickAlias *na = findnick(passcode); + if (na == NULL) + source.Reply(NICK_X_NOT_REGISTERED, passcode.c_str()); + else if (na->nc->HasFlag(NI_UNCONFIRMED) == false) + source.Reply(_("Nick \002%s\002 is already confirmed."), na->nick.c_str()); + else + { + na->nc->UnsetFlag(NI_UNCONFIRMED); + Log(LOG_ADMIN, u, this) << "to confirm nick " << na->nick << " (" << na->nc->display << ")"; + source.Reply(_("Nick \002%s\002 has been confirmed."), na->nick.c_str()); + } + } + else if (u->Account()) + { + Anope::string code; + if (u->Account()->GetExtRegular("ns_register_passcode", code) && code == passcode) + { + u->Account()->Shrink("ns_register_passcode"); + Log(LOG_COMMAND, u, this) << "to confirm their email"; + source.Reply(_("Your email address of \002%s\002 has been confirmed."), u->Account()->email.c_str()); + u->Account()->UnsetFlag(NI_UNCONFIRMED); + ircdproto->SendAccountLogin(u, u->Account()); + NickAlias *na = findnick(u->nick); + if (na && na->nc == u->Account()) + u->SetMode(findbot(Config->NickServ), UMODE_REGISTERED); + } + else + source.Reply(_("Invalid passcode.")); + } + else + source.Reply(_("Invalid passcode.")); + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + User *u = source.u; + 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 (u->Account() && u->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) + { + source.Reply(NICK_CONFIRM_INVALID); + } +}; + +class CommandNSRegister : public Command +{ + public: + CommandNSRegister(Module *creator) : Command(creator, "nickserv/register", 1, 2) + { + this->SetFlag(CFLAG_ALLOW_UNREGISTERED); + this->SetDesc(_("Register a nickname")); + if (Config->NSForceEmail) + this->SetSyntax(_("\037password\037 \037email\037")); + else + this->SetSyntax(_("\037password\037 \037[email]\037")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + NickAlias *na; + size_t prefixlen = Config->NSGuestNickPrefix.length(); + size_t nicklen = u->nick.length(); + Anope::string pass = params[0]; + Anope::string email = params.size() > 1 ? params[1] : ""; + + if (readonly) + { + source.Reply(_("Sorry, nickname registration is temporarily disabled.")); + return; + } + + if (!u->HasMode(UMODE_OPER) && Config->NickRegDelay && Anope::CurTime - u->my_signon < Config->NickRegDelay) + { + source.Reply(_("You must have been using this nick for at least %d seconds to register."), Config->NickRegDelay); + return; + } + + /* Prevent "Guest" nicks from being registered. -TheShadow */ + + /* Guest nick can now have a series of between 1 and 7 digits. + * --lara + */ + if (nicklen <= prefixlen + 7 && nicklen >= prefixlen + 1 && !u->nick.find_ci(Config->NSGuestNickPrefix) && u->nick.substr(prefixlen).find_first_not_of("1234567890") == Anope::string::npos) + { + source.Reply(NICK_CANNOT_BE_REGISTERED, u->nick.c_str()); + return; + } + + if (Config->RestrictOperNicks) + for (unsigned i = 0; i < Config->Opers.size(); ++i) + { + Oper *o = Config->Opers[i]; + + if (!u->HasMode(UMODE_OPER) && u->nick.find_ci(o->name) != Anope::string::npos) + { + source.Reply(NICK_CANNOT_BE_REGISTERED, u->nick.c_str()); + return; + } + } + + if (Config->NSForceEmail && email.empty()) + this->OnSyntaxError(source, ""); + else if (Anope::CurTime < u->lastnickreg + Config->NSRegDelay) + source.Reply(_("Please wait %d seconds before using the REGISTER command again."), (u->lastnickreg + Config->NSRegDelay) - Anope::CurTime); + else if ((na = findnick(u->nick))) + source.Reply(NICK_ALREADY_REGISTERED, u->nick.c_str()); + else if (pass.equals_ci(u->nick) || (Config->StrictPasswords && pass.length() < 5)) + source.Reply(MORE_OBSCURE_PASSWORD); + else if (pass.length() > Config->PassLen) + source.Reply(PASSWORD_TOO_LONG); + else if (!email.empty() && !MailValidate(email)) + source.Reply(MAIL_X_INVALID, email.c_str()); + else + { + na = new NickAlias(u->nick, new NickCore(u->nick)); + enc_encrypt(pass, na->nc->pass); + if (!email.empty()) + na->nc->email = email; + + Anope::string last_usermask = u->GetIdent() + "@" + u->GetDisplayedHost(); + na->last_usermask = last_usermask; + na->last_realname = u->realname; + if (Config->NSAddAccessOnReg) + na->nc->AddAccess(create_mask(u)); + + u->Login(na->nc); + + Log(LOG_COMMAND, u, this) << "to register " << na->nick << " (email: " << (!na->nc->email.empty() ? na->nc->email : "none") << ")"; + + FOREACH_MOD(I_OnNickRegister, OnNickRegister(na)); + + if (Config->NSAddAccessOnReg) + source.Reply(_("Nickname \002%s\002 registered under your account: %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 (enc_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 (Config->NSEmailReg) + { + na->nc->SetFlag(NI_UNCONFIRMED); + if (SendRegmail(u, na, source.owner)) + { + source.Reply(_("A passcode has been sent to %s, please type %s%s confirm to confirm your email address."), email.c_str(), Config->UseStrictPrivMsgString.c_str(), Config->NickServ.c_str()); + source.Reply(_("If you do not confirm your email address within %s your account will expire."), duration(Config->NSUnconfirmedExpire).c_str()); + } + } + else + ircdproto->SendAccountLogin(u, u->Account()); + ircdproto->SetAutoIdentificationToken(u); + + u->lastnickreg = Anope::CurTime; + } + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply("\n"); + 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.\n"), + Config->NickServ.c_str(), Config->NickServ.c_str()); + + if (!Config->NSForceEmail) + source.Reply(_(" \n" + "The parameter \037email\037 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.\n" + " \n")); + + 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->SetSyntax(""); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + if (!Config->NSEmailReg) + return; + + User *u = source.u; + NickAlias *na = findnick(u->nick); + + if (na == NULL) + source.Reply(NICK_NOT_REGISTERED); + else if (na->nc != u->Account() || u->Account()->HasFlag(NI_UNCONFIRMED) == false) + source.Reply(_("Your account is already confirmed.")); + else + { + if (Anope::CurTime < u->Account()->lastmail + Config->NSResendDelay) + source.Reply(_("Cannot send mail now; please retry a little later.")); + else if (!SendRegmail(u, na, source.owner)) + { + na->nc->lastmail = Anope::CurTime; + source.Reply(_("Your passcode has been re-sent to %s."), na->nc->email.c_str()); + Log(LOG_COMMAND, u, this) << "to resend registration verification code"; + } + else + Log() << "Unable to resend registration verification code for " << u->nick; + } + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + if (!Config->NSEmailReg) + return false; + + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("This command will re-send the auth code (also called passcode)\n" + "to the e-mail address of the user whom is performing it.")); + return true; + } + + void OnServHelp(CommandSource &source) + { + if (Config->NSEmailReg) + source.Reply(" %-14s %s", this->name.c_str(),_("Resend the registration passcode")); + } +}; + +class NSRegister : public Module +{ + CommandNSRegister commandnsregister; + CommandNSConfirm commandnsconfirm; + CommandNSResend commandnsrsend; + + public: + NSRegister(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandnsregister(this), commandnsconfirm(this), commandnsrsend(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandnsregister); + ModuleManager::RegisterService(&commandnsconfirm); + ModuleManager::RegisterService(&commandnsrsend); + } +}; + +static bool SendRegmail(User *u, NickAlias *na, BotInfo *bi) +{ + Anope::string code; + if (na->nc->GetExtRegular("ns_register_passcode", code) == false) + { + int chars[] = { + ' ', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', + 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', + 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', + 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', + 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' + }; + int idx, min = 1, max = 62; + for (idx = 0; idx < 9; ++idx) + code += chars[1 + static_cast((static_cast(max - min)) * getrandom16() / 65536.0) + min]; + na->nc->Extend("ns_register_passcode", new ExtensibleItemRegular(code)); + } + + Anope::string subject = Anope::printf(_("Nickname Registration (%s)"), na->nick.c_str()); + Anope::string message = Anope::printf(_("Hi,\n" + " \n" + "You have requested to register the nickname %s on %s.\n" + "Please type \" %s%s confirm %s \" to complete registration.\n" + " \n" + "If you don't know why this mail was sent to you, please ignore it silently.\n" + " \n" + "%s administrators."), na->nick.c_str(), Config->NetworkName.c_str(), Config->UseStrictPrivMsgString.c_str(), Config->NickServ.c_str(), code.c_str(), Config->NetworkName.c_str()); + + return Mail(u, na->nc, bi, subject, message); +} + +MODULE_INIT(NSRegister) diff --git a/modules/commands/ns_release.cpp b/modules/commands/ns_release.cpp new file mode 100644 index 000000000..20a3e295c --- /dev/null +++ b/modules/commands/ns_release.cpp @@ -0,0 +1,110 @@ +/* NickServ core functions + * + * (C) 2003-2011 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 CommandNSRelease : public Command +{ + public: + CommandNSRelease(Module *creator) : Command(creator, "nickserv/release", 1, 2) + { + this->SetFlag(CFLAG_ALLOW_UNREGISTERED); + this->SetDesc(_("Regain custody of your nick after RECOVER")); + this->SetSyntax(_("\037nickname\037 [\037password\037]")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + const Anope::string &nick = params[0]; + Anope::string pass = params.size() > 1 ? params[1] : ""; + NickAlias *na; + + if (!(na = findnick(nick))) + source.Reply(NICK_X_NOT_REGISTERED, nick.c_str()); + else if (na->nc->HasFlag(NI_SUSPENDED)) + source.Reply(NICK_X_SUSPENDED, na->nick.c_str()); + else if (!na->HasFlag(NS_HELD)) + source.Reply(_("Nick \002%s\002 isn't being held."), nick.c_str()); + else if (!pass.empty()) + { + EventReturn MOD_RESULT; + FOREACH_RESULT(I_OnCheckAuthentication, OnCheckAuthentication(this, &source, params, na->nc->display, pass)); + if (MOD_RESULT == EVENT_STOP) + return; + + if (MOD_RESULT == EVENT_ALLOW) + { + Log(LOG_COMMAND, u, this) << "released " << na->nick; + na->Release(); + source.Reply(_("Services' hold on your nick has been released.")); + } + else + { + source.Reply(ACCESS_DENIED); + Log(LOG_COMMAND, u, this) << "invalid password for " << nick; + bad_password(u); + } + } + else + { + if (u->Account() == na->nc || (!na->nc->HasFlag(NI_SECURE) && is_on_access(u, na->nc)) || + (!u->fingerprint.empty() && na->nc->FindCert(u->fingerprint))) + { + na->Release(); + source.Reply(_("Services' hold on your nick has been released.")); + } + else + source.Reply(ACCESS_DENIED); + } + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + /* Convert Config->NSReleaseTimeout seconds to string format */ + Anope::string relstr = duration(Config->NSReleaseTimeout); + + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Instructs %s to remove any hold on your nickname\n" + "caused by automatic kill protection or use of the \002RECOVER\002\n" + "command. This holds lasts for %s;\n" + "This command gets rid of them sooner.\n" + " \n" + "In order to use the \002RELEASE\002 command for a nick, your\n" + "current address as shown in /WHOIS must be on that nick's\n" + "access list, you must be identified and in the group of\n" + "that nick, or you must supply the correct password for\n" + "the nickname."), Config->NickServ.c_str(), relstr.c_str()); + + + return true; + } +}; + +class NSRelease : public Module +{ + CommandNSRelease commandnsrelease; + + public: + NSRelease(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandnsrelease(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandnsrelease); + } +}; + +MODULE_INIT(NSRelease) diff --git a/modules/commands/ns_resetpass.cpp b/modules/commands/ns_resetpass.cpp new file mode 100644 index 000000000..b0d40226f --- /dev/null +++ b/modules/commands/ns_resetpass.cpp @@ -0,0 +1,152 @@ +/* NickServ core functions + * + * (C) 2003-2011 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, NickAlias *na, BotInfo *bi); + +class CommandNSResetPass : public Command +{ + public: + CommandNSResetPass(Module *creator) : Command(creator, "nickserv/resetpass", 1, 1) + { + this->SetFlag(CFLAG_ALLOW_UNREGISTERED); + this->SetDesc(_("Helps you reset lost passwords")); + this->SetSyntax(_("\037nickname\037")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + NickAlias *na; + + if (Config->RestrictMail && (!u->Account() || !u->HasCommand("nickserv/nickserv/resetpass"))) + source.Reply(ACCESS_DENIED); + else if (!(na = findnick(params[0]))) + source.Reply(NICK_X_NOT_REGISTERED, params[0].c_str()); + else + { + if (SendResetEmail(u, na, source.owner)) + { + Log(LOG_COMMAND, u, 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) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Sends a code key to the nickname with instructions on how to\n" + "reset their password.")); + return true; + } +}; + +class NSResetPass : public Module +{ + CommandNSResetPass commandnsresetpass; + + public: + NSResetPass(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandnsresetpass(this) + { + this->SetAuthor("Anope"); + + if (!Config->UseMail) + throw ModuleException("Not using mail."); + + ModuleManager::RegisterService(&commandnsresetpass); + + ModuleManager::Attach(I_OnPreCommand, this); + } + + EventReturn OnPreCommand(CommandSource &source, Command *command, std::vector ¶ms) + { + if (command->name == "nickserv/confirm" && params.size() > 1) + { + User *u = source.u; + NickAlias *na = findnick(params[0]); + + time_t t; + Anope::string c; + if (na && na->nc->GetExtRegular("ns_resetpass_code", c) && na->nc->GetExtRegular("ns_resetpass_time", t)) + { + const Anope::string &passcode = params[1]; + if (t < Anope::CurTime - 3600) + { + na->nc->Shrink("ns_resetpass_code"); + na->nc->Shrink("ns_resetpass_time"); + source.Reply(_("Your password reset request has expired.")); + } + else if (passcode.equals_cs(c)) + { + na->nc->Shrink("ns_resetpass_code"); + na->nc->Shrink("ns_resetpass_time"); + + Log(LOG_COMMAND, u, &commandnsresetpass) << "confirmed RESETPASS to forcefully identify to " << na->nick; + + na->nc->UnsetFlag(NI_UNCONFIRMED); + u->Identify(na); + + source.Reply(_("You are now identified for your nick. Change your passwor now.")); + + } + else + return EVENT_CONTINUE; + + return EVENT_STOP; + } + } + + return EVENT_CONTINUE; + } +}; + +static bool SendResetEmail(User *u, NickAlias *na, BotInfo *bi) +{ + int min = 1, max = 62; + int chars[] = { + ' ', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', + 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', + 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', + 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', + 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' + }; + + Anope::string passcode; + int idx; + for (idx = 0; idx < 20; ++idx) + passcode += chars[1 + static_cast((static_cast(max - min)) * getrandom16() / 65536.0) + min]; + + Anope::string subject = Anope::printf(translate(na->nc, _("Reset password request for %s")), na->nick.c_str()); + Anope::string message = Anope::printf(translate(na->nc, _( + "Hi,\n" + " \n" + "You have requested to have the password for %s reset.\n" + "To reset your password, type %s%s CONFIRM %s %s\n" + " \n" + "If you don't know why this mail was sent to you, please ignore it silently.\n" + " \n" + "%s administrators.")), na->nick.c_str(), Config->UseStrictPrivMsgString.c_str(), Config->NickServ.c_str(), na->nick.c_str(), passcode.c_str(), Config->NetworkName.c_str()); + + na->nc->Extend("ns_resetpass_code", new ExtensibleItemRegular(passcode)); + na->nc->Extend("ns_resetpass_time", new ExtensibleItemRegular(Anope::CurTime)); + + return Mail(u, na->nc, bi, subject, message); +} + +MODULE_INIT(NSResetPass) diff --git a/modules/commands/ns_saset.cpp b/modules/commands/ns_saset.cpp new file mode 100644 index 000000000..a4aa2f422 --- /dev/null +++ b/modules/commands/ns_saset.cpp @@ -0,0 +1,173 @@ +/* NickServ core functions + * + * (C) 2003-2011 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 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 ¶ms) + { + this->OnSyntaxError(source, ""); + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(_("Sets various nickname options. \037option\037 can be one of:")); + Anope::string this_name = source.command; + for (BotInfo::command_map::iterator it = source.owner->commands.begin(), it_end = source.owner->commands.end(); it != it_end; ++it) + { + const Anope::string &c_name = it->first; + CommandInfo &info = it->second; + + if (c_name.find_ci(this_name + " ") == 0) + { + service_reference command(info.name); + if (command) + { + source.command = c_name; + command->OnServHelp(source); + } + } + } + source.Reply(_("Type \002%s%s HELP SASET \037option\037\002 for more information\n" + "on a specific option. The options will be set on the given\n" + "\037nickname\037."), Config->UseStrictPrivMsgString.c_str(), Config->NickServ.c_str()); + return true; + } +}; + +class CommandNSSASetDisplay : public Command +{ + public: + CommandNSSASetDisplay(Module *creator) : Command(creator, "nickserv/saset/display", 2, 2) + { + this->SetDesc(_("Set the display of the group in Services")); + this->SetSyntax(_("\037nickname\037 \037new-display\037")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + NickAlias *setter_na = findnick(params[0]); + if (setter_na == NULL) + { + source.Reply(NICK_X_NOT_REGISTERED, params[0].c_str()); + return; + } + NickCore *nc = setter_na->nc; + + NickAlias *na = findnick(params[1]); + if (!na || na->nc != nc) + { + source.Reply(_("The new display for \002%s\002 MUST be a nickname of the nickname group!"), nc->display.c_str()); + return; + } + + change_core_display(nc, params[1]); + source.Reply(NICK_SET_DISPLAY_CHANGED, nc->display.c_str()); + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &) + { + this->SendSyntax(source); + 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 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 ¶ms) + { + User *u = source.u; + NickAlias *setter_na = findnick(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->NSSecureAdmins && u->Account() != nc && nc->IsServicesOper()) + { + source.Reply(ACCESS_DENIED); + return; + } + else if (nc->display.equals_ci(params[1]) || (Config->StrictPasswords && len < 5)) + { + source.Reply(MORE_OBSCURE_PASSWORD); + return; + } + else if (len > Config->PassLen) + { + source.Reply(PASSWORD_TOO_LONG); + return; + } + + enc_encrypt(params[1], nc->pass); + Anope::string tmp_pass; + if (enc_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()); + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Changes the password used to identify as the nick's owner.")); + return true; + } +}; + +class NSSASet : public Module +{ + CommandNSSASet commandnssaset; + CommandNSSASetDisplay commandnssasetdisplay; + CommandNSSASetPassword commandnssasetpassword; + + public: + NSSASet(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandnssaset(this), commandnssasetdisplay(this), commandnssasetpassword(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandnssaset); + ModuleManager::RegisterService(&commandnssasetdisplay); + ModuleManager::RegisterService(&commandnssasetpassword); + } +}; + +MODULE_INIT(NSSASet) diff --git a/modules/commands/ns_saset_noexpire.cpp b/modules/commands/ns_saset_noexpire.cpp new file mode 100644 index 000000000..1bff9b461 --- /dev/null +++ b/modules/commands/ns_saset_noexpire.cpp @@ -0,0 +1,76 @@ +/* NickServ core functions + * + * (C) 2003-2011 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 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 ¶ms) + { + NickAlias *na = findnick(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")) + { + na->SetFlag(NS_NO_EXPIRE); + source.Reply(_("Nick %s \002will not\002 expire."), na->nick.c_str()); + } + else if (param.equals_ci("OFF")) + { + na->UnsetFlag(NS_NO_EXPIRE); + source.Reply(_("Nick %s \002will\002 expire."), na->nick.c_str()); + } + else + this->OnSyntaxError(source, "NOEXPIRE"); + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &) + { + 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 NSSASetNoexpire : public Module +{ + CommandNSSASetNoexpire commandnssasetnoexpire; + + public: + NSSASetNoexpire(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandnssasetnoexpire(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandnssasetnoexpire); + } +}; + +MODULE_INIT(NSSASetNoexpire) diff --git a/modules/commands/ns_sendpass.cpp b/modules/commands/ns_sendpass.cpp new file mode 100644 index 000000000..ca185c68b --- /dev/null +++ b/modules/commands/ns_sendpass.cpp @@ -0,0 +1,108 @@ +/* NickServ core functions + * + * (C) 2003-2011 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 SendPassMail(User *u, NickAlias *na, BotInfo *bi, const Anope::string &pass); + +class CommandNSSendPass : public Command +{ + public: + CommandNSSendPass(Module *creator) : Command(creator, "nickserv/sendpass", 1, 1) + { + this->SetFlag(CFLAG_ALLOW_UNREGISTERED); + this->SetDesc(_("Forgot your password? Try this")); + this->SetSyntax(_("\037nickname\037")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + const Anope::string &nick = params[0]; + NickAlias *na; + + if (Config->RestrictMail && (!u->Account() || !u->HasCommand("nickserv/sendpass"))) + source.Reply(ACCESS_DENIED); + else if (!(na = findnick(nick))) + source.Reply(NICK_X_NOT_REGISTERED, nick.c_str()); + else + { + Anope::string tmp_pass; + if (enc_decrypt(na->nc->pass, tmp_pass) == 1) + { + if (SendPassMail(u, na, source.owner, tmp_pass)) + { + Log(Config->RestrictMail ? LOG_ADMIN : LOG_COMMAND, u, this) << "for " << na->nick; + source.Reply(_("Password of \002%s\002 has been sent."), nick.c_str()); + } + } + else + source.Reply(_("SENDPASS command unavailable because encryption is in use.")); + } + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Send the password of the given nickname to the e-mail address\n" + "set in the nickname record. This command is really useful\n" + "to deal with lost passwords.\n" + " \n" + "May be limited to \002IRC operators\002 on certain networks.")); + return true; + } +}; + +class NSSendPass : public Module +{ + CommandNSSendPass commandnssendpass; + + public: + NSSendPass(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandnssendpass(this) + { + this->SetAuthor("Anope"); + + if (!Config->UseMail) + throw ModuleException("Not using mail."); + + Anope::string tmp_pass = "plain:tmp"; + if (enc_decrypt(tmp_pass, tmp_pass) == -1) + throw ModuleException("Incompatible with the encryption module being used"); + + ModuleManager::RegisterService(&commandnssendpass); + } +}; + +static bool SendPassMail(User *u, NickAlias *na, BotInfo *bi, const Anope::string &pass) +{ + char subject[BUFSIZE], message[BUFSIZE]; + + snprintf(subject, sizeof(subject), translate(na->nc, _("Nickname password (%s)")), na->nick.c_str()); + snprintf(message, sizeof(message), translate(na->nc, _( + "Hi,\n" + " \n" + "You have requested to receive the password of nickname %s by e-mail.\n" + "The password is %s. For security purposes, you should change it as soon as you receive this mail.\n" + " \n" + "If you don't know why this mail was sent to you, please ignore it silently.\n" + " \n" + "%s administrators.")), na->nick.c_str(), pass.c_str(), Config->NetworkName.c_str()); + + return Mail(u, na->nc, bi, subject, message); +} + +MODULE_INIT(NSSendPass) diff --git a/modules/commands/ns_set.cpp b/modules/commands/ns_set.cpp new file mode 100644 index 000000000..ad7acb5a4 --- /dev/null +++ b/modules/commands/ns_set.cpp @@ -0,0 +1,158 @@ +/* NickServ core functions + * + * (C) 2003-2011 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 ¶ms) + { + this->OnSyntaxError(source, ""); + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Sets various nickname options. \037option\037 can be one of:")); + Anope::string this_name = source.command; + for (BotInfo::command_map::iterator it = source.owner->commands.begin(), it_end = source.owner->commands.end(); it != it_end; ++it) + { + const Anope::string &c_name = it->first; + CommandInfo &info = it->second; + + if (c_name.find_ci(this_name + " ") == 0) + { + service_reference 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."), Config->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str(), source.command.c_str()); + return true; + } +}; + +class CommandNSSetDisplay : public Command +{ + public: + CommandNSSetDisplay(Module *creator) : Command(creator, "nickserv/set/display", 1) + { + this->SetDesc(_("Set the display of your group in Services")); + this->SetSyntax(_("\037new-display\037")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + NickAlias *na = findnick(params[1]); + + if (!na || na->nc != u->Account()) + { + source.Reply(_("The new display MUST be a nickname of your nickname group!")); + return; + } + + change_core_display(u->Account(), params[1]); + source.Reply(NICK_SET_DISPLAY_CHANGED, u->Account()->display.c_str()); + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &) + { + 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 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 ¶ms) + { + User *u = source.u; + + const Anope::string ¶m = params[1]; + unsigned len = param.length(); + + if (u->Account()->display.equals_ci(param) || (Config->StrictPasswords && len < 5)) + { + source.Reply(MORE_OBSCURE_PASSWORD); + return; + } + else if (len > Config->PassLen) + { + source.Reply(PASSWORD_TOO_LONG); + return; + } + + enc_encrypt(param, u->Account()->pass); + Anope::string tmp_pass; + if (enc_decrypt(u->Account()->pass, tmp_pass) == 1) + source.Reply(_("Password for \002%s\002 changed to \002%s\002."), u->Account()->display.c_str(), tmp_pass.c_str()); + else + source.Reply(_("Password for \002%s\002 changed."), u->Account()->display.c_str()); + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Changes the password used to identify you as the nick's\n" + "owner.")); + return true; + } +}; + +class NSSet : public Module +{ + CommandNSSet commandnsset; + CommandNSSetDisplay commandnssetdisplay; + CommandNSSetPassword commandnssetpassword; + + public: + NSSet(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandnsset(this), commandnssetdisplay(this), commandnssetpassword(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandnsset); + ModuleManager::RegisterService(&commandnssetdisplay); + ModuleManager::RegisterService(&commandnssetpassword); + } +}; + +MODULE_INIT(NSSet) diff --git a/modules/commands/ns_set_autoop.cpp b/modules/commands/ns_set_autoop.cpp new file mode 100644 index 000000000..ff178141f --- /dev/null +++ b/modules/commands/ns_set_autoop.cpp @@ -0,0 +1,107 @@ +/* NickServ core functions + * + * (C) 2003-2011 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 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(_("Should services op you automatically.")); + this->SetSyntax(_("{ON | OFF}")); + } + + void Run(CommandSource &source, const Anope::string &user, const Anope::string ¶m) + { + NickAlias *na = findnick(user); + if (na == NULL) + { + source.Reply(NICK_X_NOT_REGISTERED, user.c_str()); + return; + } + NickCore *nc = na->nc; + + if (param.equals_ci("ON")) + { + nc->SetFlag(NI_AUTOOP); + source.Reply(_("Services will now autoop %s in channels."), nc->display.c_str()); + } + else if (param.equals_ci("OFF")) + { + nc->UnsetFlag(NI_AUTOOP); + source.Reply(_("Services will no longer autoop %s in channels."), nc->display.c_str()); + } + else + this->OnSyntaxError(source, "AUTOOP"); + + return; + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + this->Run(source, source.u->Account()->display, params[0]); + } + + bool OnHelp(CommandSource &source, const Anope::string &) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Sets whether you will be opped automatically. Set to ON to \n" + "allow ChanServ to op you automatically when entering channels.")); + 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 ¶ms) + { + this->Run(source, params[0], params[1]); + } + + bool OnHelp(CommandSource &source, const Anope::string &) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Sets whether the given nickname will be opped automatically.\n" + "Set to \002ON\002 to allow ChanServ to op the given nickname \n" + "omatically when joining channels.")); + return true; + } +}; + +class NSSetAutoOp : public Module +{ + CommandNSSetAutoOp commandnssetautoop; + CommandNSSASetAutoOp commandnssasetautoop; + + public: + NSSetAutoOp(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandnssetautoop(this), commandnssasetautoop(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandnssetautoop); + ModuleManager::RegisterService(&commandnssasetautoop); + } +}; + +MODULE_INIT(NSSetAutoOp) diff --git a/modules/commands/ns_set_email.cpp b/modules/commands/ns_set_email.cpp new file mode 100644 index 000000000..f0914eb51 --- /dev/null +++ b/modules/commands/ns_set_email.cpp @@ -0,0 +1,186 @@ +/* NickServ core functions + * + * (C) 2003-2011 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 SendConfirmMail(User *u, BotInfo *bi) +{ + int chars[] = { + ' ', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', + 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', + 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', + 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', + 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' + }; + int idx, min = 1, max = 62; + Anope::string code; + for (idx = 0; idx < 9; ++idx) + code += chars[1 + static_cast((static_cast(max - min)) * getrandom16() / 65536.0) + min]; + u->Account()->Extend("ns_set_email_passcode", new ExtensibleItemRegular(code)); + + Anope::string subject = _("Email confirmation"); + Anope::string message = Anope::printf(_("Hi,\n" + " \n" + "You have requested to change your email address to %s.\n" + "Please type \" %s%s confirm %s \" to confirm this change.\n" + " \n" + "If you don't know why this mail was sent to you, please ignore it silently.\n" + " \n" + "%s administrators."), u->Account()->email.c_str(), Config->UseStrictPrivMsgString.c_str(), Config->NickServ.c_str(), code.c_str(), Config->NetworkName.c_str()); + + return Mail(u, u->Account(), bi, subject, message); +} + +class CommandNSSetEmail : public Command +{ + 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 ¶m) + { + User *u = source.u; + NickAlias *na = findnick(user); + if (!na) + { + source.Reply(NICK_X_NOT_REGISTERED, user.c_str()); + return; + } + NickCore *nc = na->nc; + + if (param.empty() && Config->NSForceEmail) + { + source.Reply(_("You cannot unset the e-mail on this network.")); + return; + } + else if (Config->NSSecureAdmins && u->Account() != nc && nc->IsServicesOper()) + { + source.Reply(ACCESS_DENIED); + return; + } + else if (!param.empty() && !MailValidate(param)) + { + source.Reply(MAIL_X_INVALID, param.c_str()); + return; + } + + if (!param.empty() && Config->NSConfirmEmailChanges && !u->IsServicesOper()) + { + u->Account()->Extend("ns_set_email", new ExtensibleItemRegular(param)); + Anope::string old = u->Account()->email; + u->Account()->email = param; + if (SendConfirmMail(u, source.owner)) + source.Reply(_("A confirmation email has been sent to \002%s\002. Follow the instructions in it to change your email address."), param.c_str()); + u->Account()->email = old; + } + else + { + if (!param.empty()) + { + 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 + { + nc->email.clear(); + source.Reply(_("E-mail address for \002%s\002 unset."), nc->display.c_str()); + } + } + + return; + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + this->Run(source, source.u->Account()->display, params.size() ? params[0] : ""); + } + + bool OnHelp(CommandSource &source, const Anope::string &) + { + 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 ¶ms) + { + this->Run(source, params[0], params.size() > 1 ? params[1] : ""); + } + + bool OnHelp(CommandSource &source, const Anope::string &) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Associates the given E-mail address with the nickname.")); + return true; + } +}; + +class NSSetEmail : public Module +{ + CommandNSSetEmail commandnssetemail; + CommandNSSASetEmail commandnssasetemail; + + public: + NSSetEmail(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandnssetemail(this), commandnssasetemail(this) + { + this->SetAuthor("Anope"); + + ModuleManager::Attach(I_OnPreCommand, this); + + ModuleManager::RegisterService(&commandnssetemail); + ModuleManager::RegisterService(&commandnssasetemail); + } + + EventReturn OnPreCommand(CommandSource &source, Command *command, std::vector ¶ms) + { + User *u = source.u; + if (command->name == "nickserv/confirm" && !params.empty() && u->IsIdentified()) + { + Anope::string new_email, passcode; + if (u->Account()->GetExtRegular("ns_set_email", new_email) && u->Account()->GetExtRegular("ns_set_email_passcode", passcode)) + { + if (params[0] == passcode) + { + u->Account()->email = new_email; + Log(LOG_COMMAND, u, command) << "to confirm their email address change to " << u->Account()->email; + source.Reply(_("Your email address has been changed to \002%s\002."), u->Account()->email.c_str()); + u->Account()->Shrink("ns_set_email"); + u->Account()->Shrink("ns_set_email_passcode"); + return EVENT_STOP; + } + } + } + + return EVENT_CONTINUE; + } +}; + +MODULE_INIT(NSSetEmail) diff --git a/modules/commands/ns_set_greet.cpp b/modules/commands/ns_set_greet.cpp new file mode 100644 index 000000000..03bcd3834 --- /dev/null +++ b/modules/commands/ns_set_greet.cpp @@ -0,0 +1,108 @@ +/* NickServ core functions + * + * (C) 2003-2011 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 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 ¶m) + { + NickAlias *na = findnick(user); + if (!na) + { + source.Reply(NICK_X_NOT_REGISTERED, user.c_str()); + return; + } + NickCore *nc = na->nc; + + if (!param.empty()) + { + nc->greet = param; + source.Reply(_("Greet message for \002%s\002 changed to \002%s\002."), nc->display.c_str(), nc->greet.c_str()); + } + else + { + nc->greet.clear(); + source.Reply(_("Greet message for \002%s\002 unset."), nc->display.c_str()); + } + + return; + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + this->Run(source, source.u->Account()->display, params.size() > 0 ? params[0] : ""); + } + + bool OnHelp(CommandSource &source, const Anope::string &) + { + 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 ¶ms) + { + this->Run(source, params[0], params.size() > 1 ? params[1] : ""); + } + + bool OnHelp(CommandSource &source, const Anope::string &) + { + 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 NSSetGreet : public Module +{ + CommandNSSetGreet commandnssetgreet; + CommandNSSASetGreet commandnssasetgreet; + + public: + NSSetGreet(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandnssetgreet(this), commandnssasetgreet(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandnssetgreet); + ModuleManager::RegisterService(&commandnssasetgreet); + } +}; + +MODULE_INIT(NSSetGreet) diff --git a/modules/commands/ns_set_hide.cpp b/modules/commands/ns_set_hide.cpp new file mode 100644 index 000000000..e0fb0e85e --- /dev/null +++ b/modules/commands/ns_set_hide.cpp @@ -0,0 +1,149 @@ +/* NickServ core functions + * + * (C) 2003-2011 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 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 ¶m, const Anope::string &arg) + { + NickAlias *na = findnick(user); + if (!na) + { + source.Reply(NICK_X_NOT_REGISTERED, user.c_str()); + return; + } + NickCore *nc = na->nc; + + Anope::string onmsg, offmsg; + NickCoreFlag flag; + + if (param.equals_ci("EMAIL")) + { + flag = NI_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 = NI_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 = NI_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 = NI_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")) + { + nc->SetFlag(flag); + source.Reply(onmsg.c_str(), nc->display.c_str(), Config->NickServ.c_str()); + } + else if (arg.equals_ci("OFF")) + { + nc->UnsetFlag(flag); + source.Reply(offmsg.c_str(), nc->display.c_str(), Config->NickServ.c_str()); + } + else + this->OnSyntaxError(source, "HIDE"); + + return; + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + this->Run(source, source.u->Account()->display, params[0], params[1]); + } + + bool OnHelp(CommandSource &source, const Anope::string &) + { + 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)."), Config->NickServ.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 ¶ms) + { + this->ClearSyntax(); + this->Run(source, params[0], params[1], params[2]); + } + + bool OnHelp(CommandSource &source, const Anope::string &) + { + 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)."), Config->NickServ.c_str()); + return true; + } +}; + +class NSSetHide : public Module +{ + CommandNSSetHide commandnssethide; + CommandNSSASetHide commandnssasethide; + + public: + NSSetHide(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandnssethide(this), commandnssasethide(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandnssethide); + ModuleManager::RegisterService(&commandnssasethide); + } +}; + +MODULE_INIT(NSSetHide) diff --git a/modules/commands/ns_set_kill.cpp b/modules/commands/ns_set_kill.cpp new file mode 100644 index 000000000..f57a93893 --- /dev/null +++ b/modules/commands/ns_set_kill.cpp @@ -0,0 +1,149 @@ +/* NickServ core functions + * + * (C) 2003-2011 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 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 ¶m) + { + NickAlias *na = findnick(user); + if (!na) + { + source.Reply(NICK_X_NOT_REGISTERED, user.c_str()); + return; + } + NickCore *nc = na->nc; + + if (param.equals_ci("ON")) + { + nc->SetFlag(NI_KILLPROTECT); + nc->UnsetFlag(NI_KILL_QUICK); + nc->UnsetFlag(NI_KILL_IMMED); + source.Reply(_("Protection is now \002on\002 for \002%s\002."), nc->display.c_str()); + } + else if (param.equals_ci("QUICK")) + { + nc->SetFlag(NI_KILLPROTECT); + nc->SetFlag(NI_KILL_QUICK); + nc->UnsetFlag(NI_KILL_IMMED); + 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->NSAllowKillImmed) + { + nc->SetFlag(NI_KILLPROTECT); + nc->SetFlag(NI_KILL_IMMED); + nc->UnsetFlag(NI_KILL_QUICK); + 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->UnsetFlag(NI_KILLPROTECT); + nc->UnsetFlag(NI_KILL_QUICK); + nc->UnsetFlag(NI_KILL_IMMED); + 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 ¶ms) + { + this->Run(source, source.u->Account()->display, params[0]); + } + + bool OnHelp(CommandSource &source, const Anope::string &) + { + 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, 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."), Config->NickServ.c_str()); + return true; + } +}; + +class CommandNSSASetKill : public CommandNSSetKill +{ + public: + CommandNSSASetKill(Module *creator) : CommandNSSetKill(creator, "nickserv/saset/kill", 2) + { + this->SetSyntax(_("\037nickname\037 {ON | QUICK | IMMED | OFF}")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + this->ClearSyntax(); + this->Run(source, params[0], params[1]); + } + + bool OnHelp(CommandSource &source, const Anope::string &) + { + 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."), Config->NickServ.c_str()); + return true; + } +}; + +class NSSetKill : public Module +{ + CommandNSSetKill commandnssetkill; + CommandNSSASetKill commandnssasetkill; + + public: + NSSetKill(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandnssetkill(this), commandnssasetkill(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandnssetkill); + ModuleManager::RegisterService(&commandnssasetkill); + } +}; + +MODULE_INIT(NSSetKill) diff --git a/modules/commands/ns_set_language.cpp b/modules/commands/ns_set_language.cpp new file mode 100644 index 000000000..3429ad688 --- /dev/null +++ b/modules/commands/ns_set_language.cpp @@ -0,0 +1,121 @@ +/* NickServ core functions + * + * (C) 2003-2011 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 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 ¶m) + { + NickAlias *na = findnick(user); + if (!na) + { + source.Reply(NICK_X_NOT_REGISTERED, user.c_str()); + return; + } + NickCore *nc = na->nc; + + for (unsigned j = 0; j < languages.size(); ++j) + { + if (param == "en" || languages[j] == param) + break; + else if (j + 1 == languages.size()) + { + this->OnSyntaxError(source, ""); + return; + } + } + + nc->language = param != "en" ? param : ""; + source.Reply(_("Language changed to \002English\002.")); + + return; + } + + void Execute(CommandSource &source, const std::vector ¶m) + { + this->Run(source, source.u->Account()->display, param[0]); + } + + bool OnHelp(CommandSource &source, const Anope::string &) + { + 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 (English)"); + for (unsigned j = 0; j < languages.size(); ++j) + { + const Anope::string &langname = anope_gettext(languages[j].c_str(), _("English")); + if (langname == "English") + continue; + source.Reply(" %s (%s)", 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 ¶ms) + { + this->Run(source, params[0], params[1]); + } + + bool OnHelp(CommandSource &source, const Anope::string &) + { + 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:")); + return true; + } +}; + +class NSSetLanguage : public Module +{ + CommandNSSetLanguage commandnssetlanguage; + CommandNSSASetLanguage commandnssasetlanguage; + + public: + NSSetLanguage(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandnssetlanguage(this), commandnssasetlanguage(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandnssetlanguage); + ModuleManager::RegisterService(&commandnssasetlanguage); + } +}; + +MODULE_INIT(NSSetLanguage) diff --git a/modules/commands/ns_set_message.cpp b/modules/commands/ns_set_message.cpp new file mode 100644 index 000000000..677002c78 --- /dev/null +++ b/modules/commands/ns_set_message.cpp @@ -0,0 +1,114 @@ +/* NickServ core functions + * + * (C) 2003-2011 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 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 ¶m) + { + NickAlias *na = findnick(user); + if (!na) + { + source.Reply(NICK_X_NOT_REGISTERED, user.c_str()); + return; + } + NickCore *nc = na->nc; + + if (!Config->UsePrivmsg) + { + source.Reply(_("Option \002%s\02 cannot be set on this network."), "MSG"); + return; + } + + if (param.equals_ci("ON")) + { + nc->SetFlag(NI_MSG); + source.Reply(_("Services will now reply to \002%s\002 with \002messages\002."), nc->display.c_str()); + } + else if (param.equals_ci("OFF")) + { + nc->UnsetFlag(NI_MSG); + source.Reply(_("Services will now reply to \002%s\002 with \002notices\002."), nc->display.c_str()); + } + else + this->OnSyntaxError(source, "MSG"); + + return; + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + this->Run(source, source.u->Account()->display, params[0]); + } + + bool OnHelp(CommandSource &source, const Anope::string &) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Allows you to choose the way Services are communicating with \n" + "you. With \002MSG\002 set, Services will use messages, else they'll \n" + "use notices.")); + return true; + } +}; + +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 &) + { + 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 ¶ms) + { + this->Run(source, params[0], params[1]); + } +}; + +class NSSetMessage : public Module +{ + CommandNSSetMessage commandnssetmessage; + CommandNSSASetMessage commandnssasetmessage; + + public: + NSSetMessage(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandnssetmessage(this), commandnssasetmessage(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandnssetmessage); + ModuleManager::RegisterService(&commandnssasetmessage); + } +}; + +MODULE_INIT(NSSetMessage) diff --git a/modules/commands/ns_set_misc.cpp b/modules/commands/ns_set_misc.cpp new file mode 100644 index 000000000..f0ee269cc --- /dev/null +++ b/modules/commands/ns_set_misc.cpp @@ -0,0 +1,126 @@ +/* + * + * (C) 2003-2011 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 CommandNSSetMisc : public Command +{ + public: + CommandNSSetMisc(Module *creator, const Anope::string &cname = "nickserv/set/misc", size_t min = 1) : Command(creator, cname, min, min + 1) + { + this->SetSyntax(_("\037parameter\037")); + } + + void Run(CommandSource &source, const Anope::string &user, const Anope::string ¶m) + { + NickAlias *na = findnick(user); + if (!na) + { + source.Reply(NICK_X_NOT_REGISTERED, user.c_str()); + return; + } + NickCore *nc = na->nc; + + nc->Shrink("ns_set_misc:" + source.command.replace_all_cs(" ", "_")); + if (!param.empty()) + { + nc->Extend("ns_set_misc:" + source.command.replace_all_cs(" ", "_"), new ExtensibleItemRegular(param)); + source.Reply(CHAN_SETTING_CHANGED, source.command.c_str(), nc->display.c_str(), param.c_str()); + } + else + source.Reply(CHAN_SETTING_UNSET, source.command.c_str(), nc->display.c_str()); + + return; + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + this->Run(source, source.u->Account()->display, params[0]); + } +}; + +class CommandNSSASetMisc : public CommandNSSetMisc +{ + public: + CommandNSSASetMisc(Module *creator) : CommandNSSetMisc(creator, "nickserv/saset/misc", 2) + { + this->ClearSyntax(); + this->SetSyntax(_("\037nickname\037 \037parameter\037")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + this->Run(source, params[0], params[1]); + } +}; + +class NSSetMisc : public Module +{ + CommandNSSetMisc commandnssetmisc; + CommandNSSASetMisc commandnssasetmisc; + + public: + NSSetMisc(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, SUPPORTED), + commandnssetmisc(this), commandnssasetmisc(this) + { + this->SetAuthor("Anope"); + + Implementation i[] = { I_OnNickInfo, I_OnDatabaseWriteMetadata, I_OnDatabaseReadMetadata }; + ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); + + ModuleManager::RegisterService(&this->commandnssetmisc); + ModuleManager::RegisterService(&this->commandnssasetmisc); + } + + void OnNickInfo(CommandSource &source, NickAlias *na, bool ShowHidden) + { + std::deque list; + na->nc->GetExtList(list); + + for (unsigned i = 0; i < list.size(); ++i) + { + if (list[i].find("ns_set_misc:") != 0) + continue; + + Anope::string value; + if (na->nc->GetExtRegular(list[i], value)) + source.Reply(" %s: %s", list[i].substr(12).replace_all_cs("_", " ").c_str(), value.c_str()); + } + } + + void OnDatabaseWriteMetadata(void (*WriteMetadata)(const Anope::string &, const Anope::string &), NickCore *nc) + { + std::deque list; + nc->GetExtList(list); + + for (unsigned i = 0; i < list.size(); ++i) + { + if (list[i].find("ns_set_misc:") != 0) + continue; + + Anope::string value; + if (nc->GetExtRegular(list[i], value)) + WriteMetadata(list[i], ":" + value); + } + } + + EventReturn OnDatabaseReadMetadata(NickCore *nc, const Anope::string &key, const std::vector ¶ms) + { + if (key.find("ns_set_misc:") == 0) + nc->Extend(key, new ExtensibleItemRegular(params[0])); + + return EVENT_CONTINUE; + } +}; + +MODULE_INIT(NSSetMisc) diff --git a/modules/commands/ns_set_private.cpp b/modules/commands/ns_set_private.cpp new file mode 100644 index 000000000..63fec57d6 --- /dev/null +++ b/modules/commands/ns_set_private.cpp @@ -0,0 +1,114 @@ +/* NickServ core functions + * + * (C) 2003-2011 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 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(Anope::printf(_("Prevent the nickname from appearing in a \002%s%s LIST\002"), Config->UseStrictPrivMsgString.c_str(), Config->NickServ.c_str())); + this->SetSyntax(_("{ON | OFF}")); + } + + void Run(CommandSource &source, const Anope::string &user, const Anope::string ¶m) + { + NickAlias *na = findnick(user); + if (!na) + { + source.Reply(NICK_X_NOT_REGISTERED, user.c_str()); + return; + } + NickCore *nc = na->nc; + + if (param.equals_ci("ON")) + { + nc->SetFlag(NI_PRIVATE); + source.Reply(_("Private option is now \002on\002 for \002%s\002."), nc->display.c_str()); + } + else if (param.equals_ci("OFF")) + { + nc->UnsetFlag(NI_PRIVATE); + source.Reply(_("Private option is now \002off\002 for \002%s\002."), nc->display.c_str()); + } + else + this->OnSyntaxError(source, "PRIVATE"); + + return; + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + this->Run(source, source.u->Account()->display, params[0]); + } + + bool OnHelp(CommandSource &source, const Anope::string &) + { + 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.)"), + Config->NickServ.c_str(), Config->NickServ.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 ¶ms) + { + this->Run(source, params[0], params[1]); + } + + bool OnHelp(CommandSource &source, const Anope::string &) + { + 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.)"), + Config->NickServ.c_str(), Config->NickServ.c_str()); + return true; + } +}; + +class NSSetPrivate : public Module +{ + CommandNSSetPrivate commandnssetprivate; + CommandNSSASetPrivate commandnssasetprivate; + + public: + NSSetPrivate(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandnssetprivate(this), commandnssasetprivate(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandnssetprivate); + ModuleManager::RegisterService(&commandnssasetprivate); + } +}; + +MODULE_INIT(NSSetPrivate) diff --git a/modules/commands/ns_set_secure.cpp b/modules/commands/ns_set_secure.cpp new file mode 100644 index 000000000..31314a5a5 --- /dev/null +++ b/modules/commands/ns_set_secure.cpp @@ -0,0 +1,114 @@ +/* NickServ core functions + * + * (C) 2003-2011 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 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 ¶m) + { + NickAlias *na = findnick(user); + if (!na) + { + source.Reply(NICK_X_NOT_REGISTERED, user.c_str()); + return; + } + NickCore *nc = na->nc; + + if (param.equals_ci("ON")) + { + nc->SetFlag(NI_SECURE); + source.Reply(_("Secure option is now \002on\002 for \002%s\002."), nc->display.c_str()); + } + else if (param.equals_ci("OFF")) + { + nc->UnsetFlag(NI_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 ¶ms) + { + this->Run(source, source.u->Account()->display, params[0]); + } + + bool OnHelp(CommandSource &source, const Anope::string &) + { + 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."), Config->NickServ.c_str(), Config->NickServ.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 ¶ms) + { + this->Run(source, params[0], params[1]); + } + + bool OnHelp(CommandSource &source, const Anope::string &) + { + 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."), Config->NickServ.c_str(), Config->NickServ.c_str()); + return true; + } +}; + +class NSSetSecure : public Module +{ + CommandNSSetSecure commandnssetsecure; + CommandNSSASetSecure commandnssasetsecure; + + public: + NSSetSecure(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandnssetsecure(this), commandnssasetsecure(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandnssetsecure); + ModuleManager::RegisterService(&commandnssasetsecure); + } +}; + +MODULE_INIT(NSSetSecure) diff --git a/modules/commands/ns_status.cpp b/modules/commands/ns_status.cpp new file mode 100644 index 000000000..9d8b58fcb --- /dev/null +++ b/modules/commands/ns_status.cpp @@ -0,0 +1,94 @@ +/* NickServ core functions + * + * (C) 2003-2011 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 CommandNSStatus : public Command +{ + public: + CommandNSStatus(Module *creator) : Command(creator, "nickserv/status", 0, 16) + { + this->SetFlag(CFLAG_ALLOW_UNREGISTERED); + this->SetDesc(_("Returns the owner status of the given nickname")); + this->SetSyntax(_("\037nickname\037...")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + const Anope::string &nick = !params.empty() ? params[0] : u->nick; + NickAlias *na = findnick(nick); + spacesepstream sep(nick); + Anope::string nickbuf; + + while (sep.GetToken(nickbuf)) + { + User *u2 = finduser(nickbuf); + 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->IsRecognized()) /* Nick is recognised, but NOT identified */ + source.Reply(_("STATUS %s %d %s"), nickbuf.c_str(), 2, u2->Account() ? u2->Account()->display.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() : ""); + } + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Returns whether the user using the given nickname is\n" + "recognized as the owner of the nickname. The response has\n" + "this format:\n" + " \n" + " \037nickname\037 \037status-code\037 \037account\037\n" + " \n" + "where \037nickname\037 is the nickname sent with the command,\n" + "\037status-code\037 is one of the following, and \037account\037\n" + "is the account they are logged in as.\n" + " \n" + " 0 - no such user online \002or\002 nickname not registered\n" + " 1 - user not recognized as nickname's owner\n" + " 2 - user recognized as owner via access list only\n" + " 3 - user recognized as owner via password identification\n" + " \n" + "Up to sixteen nicknames may be sent with each command; the\n" + "rest will be ignored. If no nickname is given, your status\n" + "will be returned.")); + return true; + } +}; + +class NSStatus : public Module +{ + CommandNSStatus commandnsstatus; + + public: + NSStatus(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandnsstatus(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandnsstatus); + } +}; + +MODULE_INIT(NSStatus) diff --git a/modules/commands/ns_suspend.cpp b/modules/commands/ns_suspend.cpp new file mode 100644 index 000000000..50cf039ce --- /dev/null +++ b/modules/commands/ns_suspend.cpp @@ -0,0 +1,160 @@ +/* NickServ core functions + * + * (C) 2003-2011 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 CommandNSSuspend : public Command +{ + public: + CommandNSSuspend(Module *creator) : Command(creator, "nickserv/suspend", 2, 2) + { + this->SetDesc(_("Suspend a given nick")); + this->SetSyntax(_("\037nickname\037 \037reason\037")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + + const Anope::string &nick = params[0]; + const Anope::string &reason = params[1]; + + if (readonly) + { + source.Reply(READ_ONLY_MODE); + return; + } + + NickAlias *na = findnick(nick); + if (!na) + { + source.Reply(NICK_X_NOT_REGISTERED, nick.c_str()); + return; + } + + if (Config->NSSecureAdmins && na->nc->IsServicesOper()) + { + source.Reply(ACCESS_DENIED); + return; + } + + na->nc->SetFlag(NI_SUSPENDED); + na->nc->SetFlag(NI_SECURE); + na->nc->UnsetFlag(NI_KILLPROTECT); + na->nc->UnsetFlag(NI_KILL_QUICK); + na->nc->UnsetFlag(NI_KILL_IMMED); + + for (std::list::iterator it = na->nc->aliases.begin(), it_end = na->nc->aliases.end(); it != it_end; ++it) + { + NickAlias *na2 = *it; + + if (na2->nc == na->nc) + { + na2->last_quit = reason; + + User *u2 = finduser(na2->nick); + if (u2) + { + u2->Logout(); + u2->Collide(na2); + } + } + } + + Log(LOG_ADMIN, u, this) << "for " << nick << " (" << (!reason.empty() ? reason : "No reason") << ")"; + source.Reply(_("Nick %s is now suspended."), nick.c_str()); + + FOREACH_MOD(I_OnNickSuspended, OnNickSuspend(na)); + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Suspends a registered nickname, which prevents from being used\n" + "while keeping all the data for that nick.")); + 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 ¶ms) + { + User *u = source.u; + const Anope::string &nick = params[0]; + + if (readonly) + { + source.Reply(READ_ONLY_MODE); + return; + } + + NickAlias *na = findnick(nick); + if (!na) + { + source.Reply(NICK_X_NOT_REGISTERED, nick.c_str()); + return; + } + + if (Config->NSSecureAdmins && na->nc->IsServicesOper()) + { + source.Reply(ACCESS_DENIED); + return; + } + + na->nc->UnsetFlag(NI_SUSPENDED); + + Log(LOG_ADMIN, u, this) << "for " << na->nick; + source.Reply(_("Nick %s is now released."), nick.c_str()); + + FOREACH_MOD(I_OnNickUnsuspended, OnNickUnsuspended(na)); + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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; + + public: + NSSuspend(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandnssuspend(this), commandnsunsuspend(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandnssuspend); + ModuleManager::RegisterService(&commandnsunsuspend); + } +}; + +MODULE_INIT(NSSuspend) diff --git a/modules/commands/ns_update.cpp b/modules/commands/ns_update.cpp new file mode 100644 index 000000000..d870e3532 --- /dev/null +++ b/modules/commands/ns_update.cpp @@ -0,0 +1,70 @@ +/* NickServ core functions + * + * (C) 2003-2011 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->SetSyntax(""); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + NickAlias *na = findnick(u->nick); + + if (!na) + { + source.Reply(NICK_NOT_REGISTERED); + return; + } + + na->last_realname = u->realname; + na->last_seen = Anope::CurTime; + + FOREACH_MOD(I_OnNickUpdate, OnNickUpdate(u)); + + source.Reply(_("Status updated (memos, vhost, chmodes, flags)."), Config->NickServ.c_str()); + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &) + { + 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, CORE), + commandnsupdate(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandnsupdate); + } +}; + +MODULE_INIT(NSUpdate) diff --git a/modules/commands/os_akill.cpp b/modules/commands/os_akill.cpp new file mode 100644 index 000000000..05602a746 --- /dev/null +++ b/modules/commands/os_akill.cpp @@ -0,0 +1,464 @@ +/* OperServ core functions + * + * (C) 2003-2011 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 service_reference akills("xlinemanager/sgline"); + +class AkillDelCallback : public NumberList +{ + CommandSource &source; + unsigned Deleted; + public: + AkillDelCallback(CommandSource &_source, const Anope::string &numlist) : NumberList(numlist, true), source(_source), Deleted(0) + { + } + + ~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) + { + if (!Number) + return; + + XLine *x = akills->GetEntry(Number - 1); + + if (!x) + return; + + ++Deleted; + DoDel(source, x); + } + + static void DoDel(CommandSource &source, XLine *x) + { + akills->DelXLine(x); + } +}; + +class AkillListCallback : public NumberList +{ + protected: + CommandSource &source; + bool SentHeader; + public: + AkillListCallback(CommandSource &_source, const Anope::string &numlist) : NumberList(numlist, false), source(_source), SentHeader(false) + { + } + + ~AkillListCallback() + { + if (!SentHeader) + source.Reply(_("No matching entries on the AKILL list.")); + else + source.Reply(END_OF_ANY_LIST, "Akill"); + } + + void HandleNumber(unsigned Number) + { + if (!Number) + return; + + XLine *x = akills->GetEntry(Number - 1); + + if (!x) + return; + + if (!SentHeader) + { + SentHeader = true; + source.Reply(_("Current AKILL list:\n" + " Num Mask Reason")); + } + + DoList(source, x, Number); + } + + static void DoList(CommandSource &source, XLine *x, unsigned Number) + { + source.Reply(OPER_LIST_FORMAT, Number + 1, x->Mask.c_str(), x->Reason.c_str()); + } +}; + +class AkillViewCallback : public AkillListCallback +{ + public: + AkillViewCallback(CommandSource &_source, const Anope::string &numlist) : AkillListCallback(_source, numlist) + { + } + + void HandleNumber(unsigned Number) + { + if (!Number) + return; + + XLine *x = akills->GetEntry(Number - 1); + + if (!x) + return; + + if (!SentHeader) + { + SentHeader = true; + source.Reply(_("Current AKILL list:")); + } + + DoList(source, x, Number); + } + + static void DoList(CommandSource &source, XLine *x, unsigned Number) + { + source.Reply(OPER_VIEW_FORMAT, Number + 1, x->Mask.c_str(), x->By.c_str(), do_strftime(x->Created).c_str(), expire_left(source.u->Account(), x->Expires).c_str(), x->Reason.c_str()); + } +}; + +class CommandOSAKill : public Command +{ + private: + void DoAdd(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + unsigned last_param = 2; + Anope::string expiry, mask; + time_t expires; + + mask = params.size() > 1 ? params[1] : ""; + if (!mask.empty() && mask[0] == '+') + { + expiry = mask; + mask = params.size() > 2 ? params[2] : ""; + last_param = 3; + } + + expires = !expiry.empty() ? dotime(expiry) : Config->AutokillExpiry; + /* 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()) + { + std::pair canAdd = akills->CanAdd(mask, expires); + if (mask.find('!') != Anope::string::npos) + source.Reply(_("\002Reminder\002: AKILL masks cannot contain nicknames; make sure you have \002not\002 included a nick portion in your mask.")); + else if (mask.find('@') == Anope::string::npos) + source.Reply(BAD_USERHOST_MASK); + else if (mask.find_first_not_of("~@.*?") == Anope::string::npos) + source.Reply(USERHOST_MASK_TOO_WIDE, mask.c_str()); + else if (canAdd.first == 1) + source.Reply(_("\002%s\002 already exists on the AKILL list."), canAdd.second->Mask.c_str()); + else if (canAdd.first == 2) + source.Reply(_("Expiry time of \002%s\002 changed."), canAdd.second->Mask.c_str()); + else if (canAdd.first == 3) + source.Reply(_("\002%s\002 is already covered by %s."), mask.c_str(), canAdd.second->Mask.c_str()); + else + { + User *user = finduser(mask); + if (user) + mask = "*@" + user->host; + unsigned int affected = 0; + for (Anope::insensitive_map::iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it) + if (Anope::Match(it->second->GetIdent() + "@" + it->second->host, mask)) + ++affected; + float percent = static_cast(affected) / static_cast(UserListByNick.size()) * 100.0; + + if (percent > 95) + { + source.Reply(USERHOST_MASK_TOO_WIDE, mask.c_str()); + Log(LOG_ADMIN, u, this) << "tried to akill " << percent << "% of the network (" << affected << " users)"; + return; + } + + XLine *x = akills->Add(mask, u->nick, expires, reason); + + EventReturn MOD_RESULT; + FOREACH_RESULT(I_OnAddXLine, OnAddXLine(u, x, akills)); + if (MOD_RESULT == EVENT_STOP) + { + delete x; + return; + } + + source.Reply(_("\002%s\002 added to the AKILL list."), mask.c_str()); + + Log(LOG_ADMIN, u, this) << "on " << mask << " (" << reason << ") expires in " << (expires ? duration(expires - Anope::CurTime) : "never") << " [affects " << affected << " user(s) (" << percent << "%)]"; + + if (readonly) + source.Reply(READ_ONLY_MODE); + } + } + else + this->OnSyntaxError(source, "ADD"); + + return; + } + + void DoDel(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + 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); + 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; + } + + FOREACH_MOD(I_OnDelXLine, OnDelXLine(u, x, akills)); + + AkillDelCallback::DoDel(source, x); + source.Reply(_("\002%s\002 deleted from the AKILL list."), mask.c_str()); + } + + if (readonly) + source.Reply(READ_ONLY_MODE); + + return; + } + + void DoList(CommandSource &source, const std::vector ¶ms) + { + if (akills->GetList().empty()) + { + source.Reply(_("AKILL list is empty.")); + 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) + { + AkillListCallback list(source, mask); + list.Process(); + } + else + { + bool SentHeader = false; + + for (unsigned i = 0, end = akills->GetCount(); i < end; ++i) + { + XLine *x = akills->GetEntry(i); + + if (mask.empty() || mask.equals_ci(x->Mask) || Anope::Match(x->Mask, mask)) + { + if (!SentHeader) + { + SentHeader = true; + source.Reply(_("Current AKILL list:\n" + " Num Mask Reason")); + } + + AkillListCallback::DoList(source, x, i); + } + } + + if (!SentHeader) + source.Reply(_("No matching entries on the AKILL list.")); + else + source.Reply(END_OF_ANY_LIST, "Akill"); + } + + return; + } + + void DoView(CommandSource &source, const std::vector ¶ms) + { + if (akills->GetList().empty()) + { + source.Reply(_("AKILL list is empty.")); + 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) + { + AkillViewCallback list(source, mask); + list.Process(); + } + else + { + bool SentHeader = false; + + for (unsigned i = 0, end = akills->GetCount(); i < end; ++i) + { + XLine *x = akills->GetEntry(i); + + if (mask.empty() || mask.equals_ci(x->Mask) || Anope::Match(x->Mask, mask)) + { + if (!SentHeader) + { + SentHeader = true; + source.Reply(_("Current AKILL list:")); + } + + AkillViewCallback::DoList(source, x, i); + } + } + + if (!SentHeader) + source.Reply(_("No matching entries on the AKILL list.")); + } + + return; + } + + void DoClear(CommandSource &source) + { + User *u = source.u; + FOREACH_MOD(I_OnDelXLine, OnDelXLine(u, NULL, akills)); + akills->Clear(); + source.Reply(_("The AKILL list has been cleared.")); + + return; + } + public: + CommandOSAKill(Module *creator) : Command(creator, "operserv/akill", 1, 4) + { + 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}")); + this->SetSyntax(_("LIST [\037mask\037 | \037list\037]")); + this->SetSyntax(_("VIEW [\037mask\037 | \037list\037]")); + this->SetSyntax(_("CLEAR")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + 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) + { + 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 (K-line) for\n" + "the mask which the user matched.\n" + " \n" + "\002AKILL ADD\002 adds the given nick or user@host/ip mask to the AKILL\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 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.\n" + " \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, CORE), + commandosakill(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandosakill); + } +}; + +MODULE_INIT(OSAKill) diff --git a/modules/commands/os_chankill.cpp b/modules/commands/os_chankill.cpp new file mode 100644 index 000000000..9ce895c59 --- /dev/null +++ b/modules/commands/os_chankill.cpp @@ -0,0 +1,120 @@ +/* OperServ core functions + * + * (C) 2003-2011 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 service_reference akills("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 ¶ms) + { + if (!akills) + return; + + User *u = source.u; + Anope::string expiry, channel; + time_t expires; + unsigned last_param = 1; + Channel *c; + + channel = params[0]; + if (!channel.empty() && channel[0] == '+') + { + expiry = channel; + channel = params[1]; + last_param = 2; + } + + expires = !expiry.empty() ? dotime(expiry) : Config->ChankillExpiry; + 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->AddAkiller) + realreason = "[" + u->nick + "] " + reason; + else + realreason = reason; + + if ((c = findchan(channel))) + { + for (CUserList::iterator it = c->users.begin(), it_end = c->users.end(); it != it_end; ) + { + UserContainer *uc = *it++; + + if (uc->user->HasMode(UMODE_OPER)) + continue; + + akills->Add("*@" + uc->user->host, u->nick, expires, realreason); + akills->Check(uc->user); + } + + Log(LOG_ADMIN, u, this) << "(" << realreason << ")"; + } + else + source.Reply(CHAN_X_NOT_IN_USE, channel.c_str()); + } + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Puts an AKILL for every nick on the specified channel. It\n" + "uses the entire and complete real ident@host for every nick,\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, CORE), + commandoschankill(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandoschankill); + } +}; + +MODULE_INIT(OSChanKill) diff --git a/modules/commands/os_config.cpp b/modules/commands/os_config.cpp new file mode 100644 index 000000000..1d1fead33 --- /dev/null +++ b/modules/commands/os_config.cpp @@ -0,0 +1,217 @@ +/* OperServ core functions + * + * (C) 2003-2011 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 CommandOSConfig : public Command +{ + void ChangeHash(ConfigDataHash &hash, const Anope::string &block, const Anope::string &iname, const Anope::string &value) + { + ConfigDataHash::iterator it = hash.find(block); + + KeyValList &list = it->second; + for (unsigned i = 0; i < list.size(); ++i) + { + const Anope::string &item = list[i].first; + if (item == iname) + { + list[i] = std::make_pair(item, value); + break; + } + } + } + + public: + CommandOSConfig(Module *creator) : Command(creator, "operserv/config", 1, 4) + { + this->SetDesc(_("View and change configuration file settings")); + this->SetSyntax(_("{\037MODIFY\037|\037VIEW\037} [\037block name\037 \037item name\037 \037item value\037]")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + const Anope::string &what = params[0]; + + if (what.equals_ci("MODIFY") && params.size() > 3) + { + ConfigItems configitems(Config); + + for (unsigned i = 0; !configitems.Values[i].tag.empty(); ++i) + { + ConfigItems::Item *v = &configitems.Values[i]; + if (v->tag.equals_cs(params[1]) && v->value.equals_cs(params[2])) + { + try + { + ValueItem vi(params[3]); + if (!v->validation_function(Config, v->tag, v->value, vi)) + throw ConfigException("Parameter failed to validate."); + + int dt = v->datatype; + + if (dt & DT_NORELOAD) + throw ConfigException("This item can not be changed while Anope is running."); + bool allow_wild = dt & DT_ALLOW_WILD; + dt &= ~(DT_ALLOW_NEWLINE | DT_ALLOW_WILD); + + /* Yay for *massive* copypaste from config.cpp */ + switch (dt) + { + case DT_NOSPACES: + { + ValueContainerString *vcs = debug_cast(v->val); + Config->ValidateNoSpaces(vi.GetValue(), v->tag, v->value); + vcs->Set(vi.GetValue()); + break; + } + case DT_HOSTNAME: + { + ValueContainerString *vcs = debug_cast(v->val); + Config->ValidateHostname(vi.GetValue(), v->tag, v->value); + vcs->Set(vi.GetValue()); + break; + } + case DT_IPADDRESS: + { + ValueContainerString *vcs = debug_cast(v->val); + Config->ValidateIP(vi.GetValue(), v->tag, v->value, allow_wild); + vcs->Set(vi.GetValue()); + break; + } + case DT_STRING: + { + ValueContainerString *vcs = debug_cast(v->val); + vcs->Set(vi.GetValue()); + break; + } + case DT_INTEGER: + { + int val = vi.GetInteger(); + ValueContainerInt *vci = debug_cast(v->val); + vci->Set(&val, sizeof(int)); + break; + } + case DT_UINTEGER: + { + unsigned val = vi.GetInteger(); + ValueContainerUInt *vci = debug_cast(v->val); + vci->Set(&val, sizeof(unsigned)); + break; + } + case DT_LUINTEGER: + { + unsigned long val = vi.GetInteger(); + ValueContainerLUInt *vci = debug_cast(v->val); + vci->Set(&val, sizeof(unsigned long)); + break; + } + case DT_TIME: + { + time_t time = dotime(vi.GetValue()); + ValueContainerTime *vci = debug_cast(v->val); + vci->Set(&time, sizeof(time_t)); + break; + } + case DT_BOOLEAN: + { + bool val = vi.GetBool(); + ValueContainerBool *vcb = debug_cast(v->val); + vcb->Set(&val, sizeof(bool)); + break; + } + default: + break; + } + } + catch (const ConfigException &ex) + { + source.Reply(_("Error changing configuration value: ") + ex.GetReason()); + return; + } + + ChangeHash(Config->config_data, params[1], params[2], params[3]); + + Log(LOG_ADMIN, source.u, 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()); + return; + } + } + + source.Reply("There is no configuration value named %s:%s", params[1].c_str(), params[2].c_str()); + } + else if (what.equals_ci("VIEW")) + { + /* Blocks we should show */ + const Anope::string show_blocks[] = { "botserv", "chanserv", "defcon", "global", "memoserv", "nickserv", "networkinfo", "operserv", "options", "" }; + + Log(LOG_ADMIN, source.u, this) << "VIEW"; + + for (ConfigDataHash::const_iterator it = Config->config_data.begin(), it_end = Config->config_data.end(); it != it_end; ++it) + { + const Anope::string &bname = it->first; + const KeyValList &list = it->second; + + bool ok = false; + for (unsigned i = 0; !show_blocks[i].empty(); ++i) + if (bname == show_blocks[i]) + ok = true; + if (ok == false) + continue; + + source.Reply(_("%s settings:"), bname.c_str()); + for (unsigned i = 0; i < list.size(); ++i) + { + const Anope::string &first = list[i].first, second = list[i].second; + + source.Reply(_(" Name: %-15s Value: %s"), first.c_str(), second.c_str()); + } + } + + source.Reply(_("End of configuration.")); + } + else + this->OnSyntaxError(source, what); + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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 RELOAD command is used.\n" + " \n" + "Example:\n" + " \002MODIFY nickserv forcemail no\002")); + return true; + } +}; + +class OSConfig : public Module +{ + CommandOSConfig commandosconfig; + + public: + OSConfig(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandosconfig(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandosconfig); + } +}; + +MODULE_INIT(OSConfig) diff --git a/modules/commands/os_defcon.cpp b/modules/commands/os_defcon.cpp new file mode 100644 index 000000000..ea513e1dc --- /dev/null +++ b/modules/commands/os_defcon.cpp @@ -0,0 +1,663 @@ +/* OperServ core functions + * + * (C) 2003-2011 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 "global.h" +#include "os_session.h" + +enum DefconLevel +{ + DEFCON_NO_NEW_CHANNELS, + DEFCON_NO_NEW_NICKS, + DEFCON_NO_MLOCK_CHANGE, + DEFCON_FORCE_CHAN_MODES, + DEFCON_REDUCE_SESSION, + DEFCON_NO_NEW_CLIENTS, + DEFCON_OPER_ONLY, + DEFCON_SILENT_OPER_ONLY, + DEFCON_AKILL_NEW_CLIENTS, + DEFCON_NO_NEW_MEMOS +}; + +bool DefConModesSet = false; + +struct DefconConfig +{ + std::vector > DefCon; + Flags DefConModesOn; + Flags DefConModesOff; + std::map DefConModesOnParams; + + int defaultlevel, sessionlimit; + Anope::string chanmodes, message, offmessage, akillreason; + std::vector defcons; + time_t akillexpire, timeout; + bool globalondefcon; + + DefconConfig() + { + this->DefCon.resize(6); + this->defcons.resize(5); + } + + bool Check(DefconLevel level) + { + return this->Check(this->defaultlevel, level); + } + + bool Check(int dlevel, DefconLevel level) + { + return this->DefCon[dlevel].test(level); + } + + void Add(int dlevel, DefconLevel level) + { + this->DefCon[dlevel][level] = true; + } + + void Del(int dlevel, DefconLevel level) + { + this->DefCon[dlevel][level] = false; + } + + bool SetDefConParam(ChannelModeName Name, const Anope::string &buf) + { + return DefConModesOnParams.insert(std::make_pair(Name, buf)).second; + } + + void UnsetDefConParam(ChannelModeName Name) + { + DefConModesOnParams.erase(Name); + } + + bool GetDefConParam(ChannelModeName Name, Anope::string &buf) + { + std::map::iterator it = DefConModesOnParams.find(Name); + + buf.clear(); + + if (it != DefConModesOnParams.end()) + { + buf = it->second; + return true; + } + + return false; + } +}; + +static DefconConfig DConfig; + +/**************************************************************************/ + +void defcon_sendlvls(CommandSource &source); +void runDefCon(); +static Anope::string defconReverseModes(const Anope::string &modes); + +class DefConTimeout : public CallBack +{ + int level; + + public: + DefConTimeout(Module *mod, int newlevel) : CallBack(mod, DConfig.timeout), level(newlevel) { } + + void Tick(time_t) + { + if (DConfig.defaultlevel != level) + { + DConfig.defaultlevel = level; + FOREACH_MOD(I_OnDefconLevel, OnDefconLevel(level)); + Log(findbot(Config->OperServ), "operserv/defcon") << "Defcon level timeout, returning to level " << level; + + if (DConfig.globalondefcon) + { + if (!DConfig.offmessage.empty()) + global->SendGlobal(findbot(Config->Global), "", DConfig.offmessage); + else + global->SendGlobal(findbot(Config->Global), "", Anope::printf(translate(_("The Defcon Level is now at Level: \002%d\002")), DConfig.defaultlevel)); + + if (!DConfig.message.empty()) + global->SendGlobal(findbot(Config->Global), "", DConfig.message); + } + + runDefCon(); + } + } +}; +static DefConTimeout *timeout; + +class CommandOSDefcon : public Command +{ + void SendLevels(CommandSource &source) + { + if (DConfig.Check(DEFCON_NO_NEW_CHANNELS)) + source.Reply(_("* No new channel registrations")); + if (DConfig.Check(DEFCON_NO_NEW_NICKS)) + source.Reply(_("* No new nick registrations")); + if (DConfig.Check(DEFCON_NO_MLOCK_CHANGE)) + source.Reply(_("* No MLOCK changes")); + if (DConfig.Check(DEFCON_FORCE_CHAN_MODES) && !DConfig.chanmodes.empty()) + source.Reply(_("* Force Chan Modes (%s) to be set on all channels"), DConfig.chanmodes.c_str()); + if (DConfig.Check(DEFCON_REDUCE_SESSION)) + source.Reply(_("* Use the reduced session limit of %d"), DConfig.sessionlimit); + if (DConfig.Check(DEFCON_NO_NEW_CLIENTS)) + source.Reply(_("* Kill any NEW clients connecting")); + if (DConfig.Check(DEFCON_OPER_ONLY)) + source.Reply(_("* Ignore any non-opers with message")); + if (DConfig.Check(DEFCON_SILENT_OPER_ONLY)) + source.Reply(_("* Silently ignore non-opers")); + if (DConfig.Check(DEFCON_AKILL_NEW_CLIENTS)) + source.Reply(_("* AKILL any new clients connecting")); + if (DConfig.Check(DEFCON_NO_NEW_MEMOS)) + source.Reply(_("* No new memos sent")); + } + + public: + CommandOSDefcon(Module *creator) : Command(creator, "operserv/defcon", 1, 1) + { + this->SetDesc(_("Manipulate the DefCon system")); + this->SetSyntax(_("[\0021\002|\0022\002|\0023\002|\0024\002|\0025\002]")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + const Anope::string &lvl = params[0]; + + if (lvl.empty()) + { + source.Reply(_("Services are now at DEFCON \002%d\002"), DConfig.defaultlevel); + this->SendLevels(source); + return; + } + + int newLevel = 0; + try + { + newLevel = convertTo(lvl); + } + catch (const ConvertException &) { } + + if (newLevel < 1 || newLevel > 5) + { + this->OnSyntaxError(source, ""); + return; + } + + DConfig.defaultlevel = newLevel; + + FOREACH_MOD(I_OnDefconLevel, OnDefconLevel(newLevel)); + + if (timeout) + { + delete timeout; + timeout = NULL; + } + + if (DConfig.timeout) + timeout = new DefConTimeout(this->module, 5); + + source.Reply(_("Services are now at DEFCON \002%d\002"), DConfig.defaultlevel); + this->SendLevels(source); + Log(LOG_ADMIN, u, 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.defaultlevel == 5 && !DConfig.offmessage.empty()) + global->SendGlobal(findbot(Config->Global), "", DConfig.offmessage); + else if (DConfig.defaultlevel != 5) + { + global->SendGlobal(findbot(Config->Global), "", Anope::printf(_("The Defcon level is now at: \002%d\002"), DConfig.defaultlevel)); + if (!DConfig.message.empty()) + global->SendGlobal(findbot(Config->Global), "", DConfig.message); + } + } + + /* Run any defcon functions, e.g. FORCE CHAN MODE */ + runDefCon(); + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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.")); + return true; + } +}; + +class OSDefcon : public Module +{ + service_reference session_service; + service_reference akills; + CommandOSDefcon commandosdefcon; + + void ParseModeString() + { + int add = -1; /* 1 if adding, 0 if deleting, -1 if neither */ + unsigned char mode; + ChannelMode *cm; + ChannelModeParam *cmp; + Anope::string modes, param; + + spacesepstream ss(DConfig.chanmodes); + + DConfig.DefConModesOn.ClearFlags(); + DConfig.DefConModesOff.ClearFlags(); + ss.GetToken(modes); + + /* Loop while there are modes to set */ + for (unsigned i = 0, end = modes.length(); i < end; ++i) + { + mode = modes[i]; + + switch (mode) + { + case '+': + add = 1; + continue; + case '-': + add = 0; + continue; + default: + if (add < 0) + continue; + } + + if ((cm = ModeManager::FindChannelModeByChar(mode))) + { + if (cm->Type == MODE_STATUS || cm->Type == MODE_LIST || !cm->CanSet(NULL)) + { + Log() << "DefConChanModes mode character '" << mode << "' cannot be locked"; + continue; + } + else if (add) + { + DConfig.DefConModesOn.SetFlag(cm->Name); + DConfig.DefConModesOff.UnsetFlag(cm->Name); + + if (cm->Type == MODE_PARAM) + { + cmp = debug_cast(cm); + + if (!ss.GetToken(param)) + { + Log() << "DefConChanModes mode character '" << mode << "' has no parameter while one is expected"; + continue; + } + + if (!cmp->IsValid(param)) + continue; + + DConfig.SetDefConParam(cmp->Name, param); + } + } + else if (DConfig.DefConModesOn.HasFlag(cm->Name)) + { + DConfig.DefConModesOn.UnsetFlag(cm->Name); + + if (cm->Type == MODE_PARAM) + DConfig.UnsetDefConParam(cm->Name); + } + } + } + + /* We can't mlock +L if +l is not mlocked as well. */ + if ((cm = ModeManager::FindChannelModeByName(CMODE_REDIRECT)) && DConfig.DefConModesOn.HasFlag(cm->Name) && !DConfig.DefConModesOn.HasFlag(CMODE_LIMIT)) + { + DConfig.DefConModesOn.UnsetFlag(CMODE_REDIRECT); + + Log() << "DefConChanModes must lock mode +l as well to lock mode +L"; + } + + /* Some ircd we can't set NOKNOCK without INVITE */ + /* So check if we need there is a NOKNOCK MODE and that we need INVITEONLY */ + if (ircd->knock_needs_i && (cm = ModeManager::FindChannelModeByName(CMODE_NOKNOCK)) && DConfig.DefConModesOn.HasFlag(cm->Name) && !DConfig.DefConModesOn.HasFlag(CMODE_INVITE)) + { + DConfig.DefConModesOn.UnsetFlag(CMODE_NOKNOCK); + Log() << "DefConChanModes must lock mode +i as well to lock mode +K"; + } + } + + public: + OSDefcon(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, SUPPORTED), session_service("session"), akills("xlinemanager/sgline"), commandosdefcon(this) + { + this->SetAuthor("Anope"); + + Implementation i[] = { I_OnReload, I_OnChannelModeSet, I_OnChannelModeUnset, I_OnPreCommand, I_OnUserConnect, I_OnChannelModeAdd, I_OnChannelCreate }; + ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); + + ModuleManager::RegisterService(&commandosdefcon); + + try + { + this->OnReload(); + } + catch (const ConfigException &ex) + { + throw ModuleException(ex.GetReason()); + } + } + + void OnReload() + { + ConfigReader config; + DefconConfig dconfig; + + dconfig.defaultlevel = config.ReadInteger("defcon", "defaultlevel", 0, 0); + dconfig.defcons[4] = config.ReadValue("defcon", "level4", 0); + dconfig.defcons[3] = config.ReadValue("defcon", "level3", 0); + dconfig.defcons[2] = config.ReadValue("defcon", "level2", 0); + dconfig.defcons[1] = config.ReadValue("defcon", "level1", 0); + dconfig.sessionlimit = config.ReadInteger("defcon", "sessionlimit", 0, 0); + dconfig.akillreason = config.ReadValue("defcon", "akillreason", 0); + dconfig.akillexpire = dotime(config.ReadValue("defcon", "akillexpire", 0)); + dconfig.chanmodes = config.ReadValue("defcon", "chanmodes", 0); + dconfig.timeout = dotime(config.ReadValue("defcon", "timeout", 0)); + dconfig.globalondefcon = config.ReadFlag("defcon", "globalondefcon", 0); + dconfig.message = config.ReadValue("defcon", "message", 0); + dconfig.offmessage = config.ReadValue("defcon", "offmessage", 0); + + if (dconfig.defaultlevel < 1 || dconfig.defaultlevel > 5) + throw ConfigException("The value for must be between 1 and 5"); + else if (dconfig.akillexpire <= 0) + throw ConfigException("The value for must be greater than zero!"); + + for (unsigned level = 1; level < 5; ++level) + { + spacesepstream operations(dconfig.defcons[level]); + Anope::string operation; + while (operations.GetToken(operation)) + { + if (operation.equals_ci("nonewchannels")) + dconfig.Add(level, DEFCON_NO_NEW_CHANNELS); + else if (operation.equals_ci("nonewnicks")) + dconfig.Add(level, DEFCON_NO_NEW_NICKS); + else if (operation.equals_ci("nomlockchanges")) + dconfig.Add(level, DEFCON_NO_MLOCK_CHANGE); + else if (operation.equals_ci("forcechanmodes")) + dconfig.Add(level, DEFCON_FORCE_CHAN_MODES); + else if (operation.equals_ci("reducedsessions")) + dconfig.Add(level, DEFCON_REDUCE_SESSION); + else if (operation.equals_ci("nonewclients")) + dconfig.Add(level, DEFCON_NO_NEW_CLIENTS); + else if (operation.equals_ci("operonly")) + dconfig.Add(level, DEFCON_OPER_ONLY); + else if (operation.equals_ci("silentoperonly")) + dconfig.Add(level, DEFCON_SILENT_OPER_ONLY); + else if (operation.equals_ci("akillnewclients")) + dconfig.Add(level, DEFCON_AKILL_NEW_CLIENTS); + else if (operation.equals_ci("nonewmemos")) + dconfig.Add(level, DEFCON_NO_NEW_MEMOS); + } + + if (dconfig.Check(level, DEFCON_REDUCE_SESSION) && dconfig.sessionlimit <= 0) + throw ConfigException("The value for must be greater than zero!"); + else if (dconfig.Check(level, DEFCON_AKILL_NEW_CLIENTS) && dconfig.akillreason.empty()) + throw ConfigException("The value for must not be empty!"); + else if (dconfig.Check(level, DEFCON_FORCE_CHAN_MODES) && dconfig.chanmodes.empty()) + throw ConfigException("The value for must not be empty!"); + } + + DConfig = dconfig; + this->ParseModeString(); + } + + EventReturn OnUserConnect(User *u, bool &exempt) + { + if (!exempt && u->server->IsSynced() && DConfig.Check(DEFCON_AKILL_NEW_CLIENTS) && !u->server->IsULined()) + { + if (DConfig.Check(DEFCON_AKILL_NEW_CLIENTS) && akills) + { + Log(findbot(Config->OperServ), "operserv/defcon") << "DEFCON: adding akill for *@" << u->host; + XLine *x = akills->Add("*@" + u->host, Config->OperServ, Anope::CurTime + DConfig.akillexpire, DConfig.akillreason); + if (x) + x->By = Config->OperServ; + } + + if (DConfig.Check(DEFCON_NO_NEW_CLIENTS) || DConfig.Check(DEFCON_AKILL_NEW_CLIENTS)) + u->Kill(Config->OperServ, DConfig.akillreason); + + return EVENT_STOP; + } + + return EVENT_CONTINUE; + } + + EventReturn OnChannelModeSet(Channel *c, ChannelModeName Name, const Anope::string ¶m) + { + ChannelMode *cm = ModeManager::FindChannelModeByName(Name); + + if (DConfig.Check(DEFCON_FORCE_CHAN_MODES) && cm && DConfig.DefConModesOff.HasFlag(Name)) + { + c->RemoveMode(findbot(Config->OperServ), Name, param); + + return EVENT_STOP; + } + + return EVENT_CONTINUE; + } + + EventReturn OnChannelModeUnset(Channel *c, ChannelModeName Name, const Anope::string &) + { + ChannelMode *cm = ModeManager::FindChannelModeByName(Name); + + if (DConfig.Check(DEFCON_FORCE_CHAN_MODES) && cm && DConfig.DefConModesOn.HasFlag(Name)) + { + Anope::string param; + + if (DConfig.GetDefConParam(Name, param)) + c->SetMode(findbot(Config->OperServ), Name, param); + else + c->SetMode(findbot(Config->OperServ), Name); + + return EVENT_STOP; + + } + + return EVENT_CONTINUE; + } + + EventReturn OnPreCommand(CommandSource &source, Command *command, std::vector ¶ms) + { + if (command->name == "nickserv/register" || command->name == "nickserv/group") + { + if (DConfig.Check(DEFCON_NO_NEW_NICKS)) + { + source.Reply(_("Services are in Defcon mode, Please try again later.")); + return EVENT_STOP; + } + } + else if (command->name == "chanserv/set/mlock") + { + if (DConfig.Check(DEFCON_NO_MLOCK_CHANGE)) + { + source.Reply(_("Services are in Defcon mode, Please try again later.")); + return EVENT_STOP; + } + } + else if (command->name == "chanserv/register") + { + if (DConfig.Check(DEFCON_NO_NEW_CHANNELS)) + { + source.Reply(_("Services are in Defcon mode, Please try again later.")); + return EVENT_STOP; + } + } + else if (command->name == "memoserv/send") + { + if (DConfig.Check(DEFCON_NO_NEW_MEMOS)) + { + source.Reply(_("Services are in Defcon mode, Please try again later.")); + return EVENT_STOP; + } + } + + return EVENT_CONTINUE; + } + + void OnUserConnect(dynamic_reference &u, bool &exempt) + { + if (exempt || !u || !u->server->IsSynced() || u->server->IsULined()) + return; + + if (DConfig.Check(DEFCON_AKILL_NEW_CLIENTS) && akills) + { + Log(findbot(Config->OperServ), "operserv/defcon") << "DEFCON: adding akill for *@" << u->host; + XLine *x = akills->Add("*@" + u->host, Config->OperServ, Anope::CurTime + DConfig.akillexpire, DConfig.akillreason); + if (x) + x->By = Config->OperServ; + } + if (DConfig.Check(DEFCON_NO_NEW_CLIENTS) || DConfig.Check(DEFCON_AKILL_NEW_CLIENTS)) + { + u->Kill(Config->OperServ, DConfig.akillreason); + return; + } + + if (!DConfig.sessionlimit) + return; + + if (DConfig.Check(DEFCON_AKILL_NEW_CLIENTS) && akills) + { + Log(findbot(Config->OperServ), "operserv/defcon") << "DEFCON: adding akill for *@" << u->host; + XLine *x = akills->Add("*@" + u->host, Config->OperServ, Anope::CurTime + DConfig.akillexpire, !DConfig.akillreason.empty() ? DConfig.akillreason : "DEFCON AKILL"); + if (x) + x->By = Config->OperServ; + } + + if (DConfig.Check(DEFCON_NO_NEW_CLIENTS) || DConfig.Check(DEFCON_AKILL_NEW_CLIENTS)) + { + u->Kill(Config->OperServ, DConfig.akillreason); + return; + } + + Session *session = session_service->FindSession(u->host); + Exception *exception = session_service->FindException(u); + + if (DConfig.Check(DEFCON_REDUCE_SESSION) && !exception) + { + if (session && session->count > DConfig.sessionlimit) + { + if (!Config->SessionLimitExceeded.empty()) + ircdproto->SendMessage(findbot(Config->OperServ), u->nick, Config->SessionLimitExceeded.c_str(), u->host.c_str()); + if (!Config->SessionLimitDetailsLoc.empty()) + ircdproto->SendMessage(findbot(Config->OperServ), u->nick, "%s", Config->SessionLimitDetailsLoc.c_str()); + + u->Kill(Config->OperServ, "Defcon session limit exceeded"); + ++session->hits; + if (akills && Config->MaxSessionKill && session->hits >= Config->MaxSessionKill) + { + akills->Add("*@" + u->host, Config->OperServ, Anope::CurTime + Config->SessionAutoKillExpiry, "Defcon session limit exceeded"); + ircdproto->SendGlobops(findbot(Config->OperServ), "[DEFCON] Added a temporary AKILL for \2*@%s\2 due to excessive connections", u->host.c_str()); + } + } + } + } + + void OnChannelModeAdd(ChannelMode *cm) + { + if (DConfig.chanmodes.find(cm->ModeChar) != Anope::string::npos) + this->ParseModeString(); + } + + void OnChannelCreate(Channel *c) + { + if (DConfig.Check(DEFCON_FORCE_CHAN_MODES)) + c->SetModes(findbot(Config->OperServ), false, "%s", DConfig.chanmodes.c_str()); + } +}; + +/** + * Send a message to the oper about which precautions are "active" for this level + **/ +void defcon_sendlvls(CommandSource &source) +{ + if (DConfig.Check(DEFCON_NO_NEW_CHANNELS)) + source.Reply(_("* No new channel registrations")); + if (DConfig.Check(DEFCON_NO_NEW_NICKS)) + source.Reply(_("* No new nick registrations")); + if (DConfig.Check(DEFCON_NO_MLOCK_CHANGE)) + source.Reply(_("* No MLOCK changes")); + if (DConfig.Check(DEFCON_FORCE_CHAN_MODES) && !DConfig.chanmodes.empty()) + source.Reply(_("* Force Chan Modes (%s) to be set on all channels"), DConfig.chanmodes.c_str()); + if (DConfig.Check(DEFCON_REDUCE_SESSION)) + source.Reply(_("* Use the reduced session limit of %d"), DConfig.sessionlimit); + if (DConfig.Check(DEFCON_NO_NEW_CLIENTS)) + source.Reply(_("* Kill any NEW clients connecting")); + if (DConfig.Check(DEFCON_OPER_ONLY)) + source.Reply(_("* Ignore any non-opers with message")); + if (DConfig.Check(DEFCON_SILENT_OPER_ONLY)) + source.Reply(_("* Silently ignore non-opers")); + if (DConfig.Check(DEFCON_AKILL_NEW_CLIENTS)) + source.Reply(_("* AKILL any new clients connecting")); + if (DConfig.Check(DEFCON_NO_NEW_MEMOS)) + source.Reply(_("* No new memos sent")); +} + +void runDefCon() +{ + if (DConfig.Check(DEFCON_FORCE_CHAN_MODES)) + { + if (!DConfig.chanmodes.empty() && !DefConModesSet) + { + if (DConfig.chanmodes[0] == '+' || DConfig.chanmodes[0] == '-') + { + Log(findbot(Config->OperServ), "operserv/defcon") << "DEFCON: setting " << DConfig.chanmodes << " on all channels"; + DefConModesSet = true; + for (channel_map::const_iterator it = ChannelList.begin(), it_end = ChannelList.end(); it != it_end; ++it) + it->second->SetModes(findbot(Config->OperServ), false, "%s", DConfig.chanmodes.c_str()); + } + } + } + else + { + if (!DConfig.chanmodes.empty() && DefConModesSet) + { + if (DConfig.chanmodes[0] == '+' || DConfig.chanmodes[0] == '-') + { + DefConModesSet = false; + Anope::string newmodes = defconReverseModes(DConfig.chanmodes); + if (!newmodes.empty()) + { + Log(findbot(Config->OperServ), "operserv/defcon") << "DEFCON: setting " << newmodes << " on all channels"; + for (channel_map::const_iterator it = ChannelList.begin(), it_end = ChannelList.end(); it != it_end; ++it) + it->second->SetModes(findbot(Config->OperServ), false, "%s", newmodes.c_str()); + } + } + } + } +} + +static Anope::string defconReverseModes(const Anope::string &modes) +{ + if (modes.empty()) + return ""; + Anope::string newmodes; + for (unsigned i = 0, end = modes.length(); i < end; ++i) + { + if (modes[i] == '+') + newmodes += '-'; + else if (modes[i] == '-') + newmodes += '+'; + else + newmodes += modes[i]; + } + return newmodes; +} + +MODULE_INIT(OSDefcon) diff --git a/modules/commands/os_forbid.cpp b/modules/commands/os_forbid.cpp new file mode 100644 index 000000000..d4b4c2b41 --- /dev/null +++ b/modules/commands/os_forbid.cpp @@ -0,0 +1,342 @@ +/* OperServ core functions + * + * (C) 2003-2011 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 "os_forbid.h" + +class MyForbidService : public ForbidService +{ + std::vector forbidData; + + public: + MyForbidService(Module *m) : ForbidService(m) { } + + void AddForbid(ForbidData *d) + { + this->forbidData.push_back(d); + } + + void RemoveForbid(ForbidData *d) + { + std::vector::iterator it = std::find(this->forbidData.begin(), this->forbidData.end(), d); + if (it != this->forbidData.end()) + this->forbidData.erase(it); + delete d; + } + + ForbidData *FindForbid(const Anope::string &mask, ForbidType type) + { + const std::vector &forbids = this->GetForbids(); + for (unsigned i = forbids.size(); i > 0; --i) + { + ForbidData *d = this->forbidData[i - 1]; + + if ((type == FT_NONE || type == d->type) && Anope::Match(mask, d->mask)) + return d; + } + return NULL; + } + + const std::vector &GetForbids() + { + for (unsigned i = this->forbidData.size(); i > 0; --i) + { + ForbidData *d = this->forbidData[i - 1]; + + if (d->expires && Anope::CurTime >= d->expires) + { + Anope::string type = "none"; + if (d->type == FT_NICK) + type = "nick"; + else if (d->type == FT_CHAN) + type = "chan"; + else if (d->type == FT_EMAIL) + type = "email"; + + Log(LOG_NORMAL, Config->OperServ + "/forbid") << "Expiring forbid for " << d->mask << " type " << type; + this->forbidData.erase(this->forbidData.begin() + i - 1); + delete d; + } + } + + return this->forbidData; + } +}; + +class CommandOSForbid : public Command +{ + service_reference fs; + public: + CommandOSForbid(Module *creator) : Command(creator, "operserv/forbid", 1, 5), fs("forbid") + { + this->SetDesc(_("Forbid usage of nicknames, channels, and emails")); + this->SetSyntax(_("ADD {NICK|CHAN|EMAIL} [+\037expiry\037] \037entry\037\002 [\037reason\037]")); + this->SetSyntax(_("DEL {NICK|CHAN|EMAIL} \037entry\037")); + this->SetSyntax(_("LIST (NICK|CHAN|EMAIL)")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + if (!this->fs) + return; + + const Anope::string &command = params[0]; + const Anope::string &subcommand = params.size() > 1 ? params[1] : ""; + + ForbidType type = FT_NONE; + if (subcommand.equals_ci("NICK")) + type = FT_NICK; + else if (subcommand.equals_ci("CHAN")) + type = FT_CHAN; + else if (subcommand.equals_ci("EMAIL")) + type = FT_EMAIL; + + if (command.equals_ci("ADD") && params.size() > 2 && type != FT_NONE) + { + const Anope::string &expiry = params[2][0] == '+' ? params[2] : ""; + const Anope::string &entry = params.size() > 3 && !expiry.empty() ? params[3] : params[2]; + Anope::string reason = !expiry.empty() && params.size() > 4 ? params[4] : (params.size() > 3 ? params[3] : ""); + + time_t expiryt = 0; + if (!expiry.empty()) + { + expiryt = dotime(expiry); + if (expiryt) + expiryt += Anope::CurTime; + } + + ForbidData *d = this->fs->FindForbid(entry, type); + bool created = false; + if (d == NULL) + { + d = new ForbidData(); + created = true; + } + + d->mask = entry; + d->creator = source.u->nick; + d->reason = reason; + d->created = Anope::CurTime; + d->expires = expiryt; + d->type = type; + if (created) + this->fs->AddForbid(d); + + Log(LOG_ADMIN, source.u, this) << "to add a forbid on " << entry << " of type " << subcommand; + source.Reply(_("Added a%s forbid on %s to expire on %s"), type == FT_CHAN ? "n" : "", entry.c_str(), d->expires ? do_strftime(d->expires).c_str() : "never"); + } + else if (command.equals_ci("DEL") && params.size() > 2 && type != FT_NONE) + { + const Anope::string &entry = params[2]; + + ForbidData *d = this->fs->FindForbid(entry, type); + if (d != NULL) + { + Log(LOG_ADMIN, source.u, this) << "to remove forbid " << d->mask << " of type " << subcommand; + source.Reply(_("%s deleted from the %s forbid list."), d->mask.c_str(), subcommand.c_str()); + } + else + source.Reply(_("Forbid on %s was not found."), entry.c_str()); + } + else if (command.equals_ci("LIST")) + { + const std::vector &forbids = this->fs->GetForbids(); + if (forbids.empty()) + source.Reply(_("Forbid list is empty.")); + else + { + source.Reply(_("Forbid list:")); + source.Reply(_("Mask Type Reason")); + + for (unsigned i = 0; i < forbids.size(); ++i) + { + ForbidData *d = forbids[i]; + + Anope::string ftype; + if (d->type == FT_NICK) + ftype = "NICK"; + else if (d->type == FT_CHAN) + ftype = "CHAN"; + else if (d->type == FT_EMAIL) + ftype = "EMAIL"; + else + continue; + + source.Reply("%-10s %-5s %s", d->mask.c_str(), ftype.c_str(), d->reason.c_str()); + source.Reply(_("By %s, expires on %s"), d->creator.c_str(), d->expires ? do_strftime(d->expires).c_str() : "never"); + } + + source.Reply(_("End of forbid list.")); + } + } + else + this->OnSyntaxError(source, command); + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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.")); + return true; + } +}; + +class OSForbid : public Module +{ + MyForbidService forbidService; + CommandOSForbid commandosforbid; + + public: + OSForbid(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + forbidService(this), commandosforbid(this) + { + this->SetAuthor("Anope"); + + Implementation i[] = { I_OnUserConnect, I_OnUserNickChange, I_OnJoinChannel, I_OnPreCommand, I_OnDatabaseWrite, I_OnDatabaseRead }; + ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); + + ModuleManager::RegisterService(&this->forbidService); + + ModuleManager::RegisterService(&commandosforbid); + } + + void OnUserConnect(dynamic_reference &u, bool &exempt) + { + if (!u || exempt) + return; + + this->OnUserNickChange(u, ""); + } + + void OnUserNickChange(User *u, const Anope::string &) + { + if (u->HasMode(UMODE_OPER)) + return; + + ForbidData *d = this->forbidService.FindForbid(u->nick, FT_NICK); + if (d != NULL) + { + BotInfo *bi = findbot(Config->OperServ); + if (bi) + { + if (d->reason.empty()) + u->SendMessage(bi, _("This nickname has been forbidden.")); + else + u->SendMessage(bi, _("This nickname has been forbidden: %s"), d->reason.c_str()); + } + u->Collide(NULL); + } + } + + void OnJoinChannel(User *u, Channel *c) + { + if (u->HasMode(UMODE_OPER)) + return; + + BotInfo *bi = findbot(Config->OperServ); + ForbidData *d = this->forbidService.FindForbid(c->name, FT_CHAN); + if (bi != NULL && d != NULL) + { + if (!c->HasFlag(CH_INHABIT)) + { + /* Join ChanServ and set a timer for this channel to part ChanServ later */ + new ChanServTimer(c); + + /* Set +si to prevent rejoin */ + c->SetMode(NULL, CMODE_NOEXTERNAL); + c->SetMode(NULL, CMODE_TOPIC); + c->SetMode(NULL, CMODE_SECRET); + c->SetMode(NULL, CMODE_INVITE); + } + + if (d->reason.empty()) + c->Kick(bi, u, _("This channel has been forbidden.")); + else + c->Kick(bi, u, _("This channel has been forbidden: %s"), d->reason.c_str()); + } + } + + EventReturn OnPreCommand(CommandSource &source, Command *command, std::vector ¶ms) + { + if (source.u->HasMode(UMODE_OPER)) + return EVENT_CONTINUE; + else if (command->name == "nickserv/register" && params.size() > 1) + { + ForbidData *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; + } + } + + return EVENT_CONTINUE; + } + + void OnDatabaseWrite(void (*Write)(const Anope::string &)) + { + std::vector forbids = this->forbidService.GetForbids(); + for (unsigned i = 0; i < forbids.size(); ++i) + { + ForbidData *f = forbids[i]; + Anope::string ftype; + if (f->type == FT_NICK) + ftype = "NICK"; + else if (f->type == FT_CHAN) + ftype = "CHAN"; + else if (f->type == FT_EMAIL) + ftype = "EMAIL"; + Write("FORBID " + f->mask + " " + f->creator + " " + stringify(f->created) + " " + stringify(f->expires) + " " + ftype + " " + f->reason); + } + } + + EventReturn OnDatabaseRead(const std::vector ¶ms) + { + if (params.size() > 5 && params[0] == "FORBID") + { + ForbidData *f = new ForbidData(); + f->mask = params[1]; + f->creator = params[2]; + f->created = convertTo(params[3]); + f->expires = convertTo(params[4]); + if (params[5] == "NICK") + f->type = FT_NICK; + else if (params[5] == "CHAN") + f->type = FT_CHAN; + else if (params[5] == "EMAIL") + f->type = FT_EMAIL; + else + f->type = FT_NONE; + f->reason = params.size() > 6 ? params[6] : ""; + + this->forbidService.AddForbid(f); + } + + return EVENT_CONTINUE; + } +}; + +MODULE_INIT(OSForbid) diff --git a/modules/commands/os_forbid.h b/modules/commands/os_forbid.h new file mode 100644 index 000000000..b91aa3be8 --- /dev/null +++ b/modules/commands/os_forbid.h @@ -0,0 +1,37 @@ +#ifndef OS_FORBID_H +#define OS_FORBID_H + +enum ForbidType +{ + FT_NONE, + FT_NICK, + FT_CHAN, + FT_EMAIL +}; + +struct ForbidData +{ + Anope::string mask; + Anope::string creator; + Anope::string reason; + time_t created; + time_t expires; + ForbidType type; +}; + +class ForbidService : public Service +{ + public: + ForbidService(Module *m) : Service(m, "forbid") { } + + virtual void AddForbid(ForbidData *d) = 0; + + virtual void RemoveForbid(ForbidData *d) = 0; + + virtual ForbidData *FindForbid(const Anope::string &mask, ForbidType type) = 0; + + virtual const std::vector &GetForbids() = 0; +}; + +#endif + diff --git a/modules/commands/os_ignore.cpp b/modules/commands/os_ignore.cpp new file mode 100644 index 000000000..9f50ec967 --- /dev/null +++ b/modules/commands/os_ignore.cpp @@ -0,0 +1,350 @@ +/* OperServ core functions + * + * (C) 2003-2011 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 "os_ignore.h" + +class OSIgnoreService : public IgnoreService +{ + public: + OSIgnoreService(Module *o, const Anope::string &n) : IgnoreService(o, n) { } + + void AddIgnore(const Anope::string &mask, const Anope::string &creator, const Anope::string &reason, time_t delta = Anope::CurTime) + { + /* If it s an existing user, we ignore the hostmask. */ + Anope::string realmask = mask; + size_t user, host; + + User *u = finduser(mask); + if (u) + realmask = "*!*@" + u->host; + /* Determine whether we get a nick or a mask. */ + else if ((host = mask.find('@')) != Anope::string::npos) + { + /* Check whether we have a nick too.. */ + if ((user = mask.find('!')) != Anope::string::npos) + { + /* this should never happen */ + if (user > host) + return; + } + else + /* We have user@host. Add nick wildcard. */ + realmask = "*!" + mask; + } + /* We only got a nick.. */ + else + realmask = mask + "!*@*"; + + /* Check if we already got an identical entry. */ + IgnoreData *ign = this->Find(realmask); + if (ign != NULL) + { + if (!delta) + ign->time = 0; + else + ign->time = Anope::CurTime + delta; + } + /* Create new entry.. */ + else + { + IgnoreData newign; + newign.mask = realmask; + newign.creator = creator; + newign.reason = reason; + newign.time = delta ? Anope::CurTime + delta : 0; + this->ignores.push_back(newign); + } + } + + bool DelIgnore(const Anope::string &mask) + { + for (std::list::iterator it = this->ignores.begin(), it_end = this->ignores.end(); it != it_end; ++it) + { + IgnoreData &idn = *it; + if (idn.mask.equals_ci(mask)) + { + this->ignores.erase(it); + return true; + } + } + + return false; + } + + IgnoreData *Find(const Anope::string &mask) + { + User *u = finduser(mask); + std::list::iterator ign = this->ignores.begin(), ign_end = this->ignores.end(); + + if (u) + { + for (; ign != ign_end; ++ign) + { + Entry ignore_mask(CMODE_BEGIN, ign->mask); + if (ignore_mask.Matches(u)) + 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)) + break; + } + + /* Check whether the entry has timed out */ + if (ign != ign_end)// && (*ign)->time && (*ign)->time <= Anope::CurTime) + { + IgnoreData &id = *ign; + + if (id.time && id.time <= Anope::CurTime) + { + Log(LOG_DEBUG) << "Expiring ignore entry " << id.mask; + this->ignores.erase(ign); + } + else + return &id; + } + + return NULL; + } +}; + +class CommandOSIgnore : public Command +{ + private: + void DoAdd(CommandSource &source, const std::vector ¶ms) + { + service_reference ignore_service("ignore"); + 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 = dotime(time); + + if (t <= -1) + { + source.Reply(_("You have to enter a valid number as time.")); + return; + } + + ignore_service->AddIgnore(nick, source.u->nick, reason, t); + if (!t) + source.Reply(_("\002%s\002 will now permanently be ignored."), nick.c_str()); + else + source.Reply(_("\002%s\002 will now be ignored for \002%s\002."), nick.c_str(), time.c_str()); + } + + return; + } + + void DoList(CommandSource &source) + { + service_reference ignore_service("ignore"); + if (!ignore_service) + return; + + const std::list &ignores = ignore_service->GetIgnores(); + if (ignores.empty()) + source.Reply(_("Ignore list is empty.")); + else + { + source.Reply(_("Services ignore list:\n" + " Mask Creator Reason Expires")); + for (std::list::const_iterator ign = ignores.begin(), ign_end = ignores.end(); ign != ign_end; ++ign) + { + const IgnoreData &ignore = *ign; + + source.Reply(" %-11s %-11s %-11s %s", ignore.mask.c_str(), ignore.creator.c_str(), ignore.reason.c_str(), do_strftime(ignore.time).c_str()); + } + } + + return; + } + + void DoDel(CommandSource &source, const std::vector ¶ms) + { + service_reference ignore_service("ignore"); + if (!ignore_service) + return; + + const Anope::string nick = params.size() > 1 ? params[1] : ""; + if (nick.empty()) + this->OnSyntaxError(source, "DEL"); + else if (ignore_service->DelIgnore(nick)) + source.Reply(_("\002%s\002 will no longer be ignored."), nick.c_str()); + else + source.Reply(_("Nick \002%s\002 not found on ignore list."), nick.c_str()); + + return; + } + + void DoClear(CommandSource &source) + { + service_reference ignore_service("ignore"); + if (!ignore_service) + return; + + ignore_service->ClearIgnores(); + 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 \037time\037 \037nick\037 \037reason\037")); + this->SetSyntax(_("DEL \037nick\037")); + this->SetSyntax(_("LIST")); + this->SetSyntax(_("CLEAR")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + 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) + { + 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 user@host\n" + "or nick!user@host, everything else will be considered a nick.\n" + "Wildcards are permitted.\n" + " \n" + "Ignores will not be enforced on IRC Operators.")); + return true; + } +}; + +class OSIgnore : public Module +{ + OSIgnoreService osignoreservice; + CommandOSIgnore commandosignore; + + public: + OSIgnore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + osignoreservice(this, "ignore"), commandosignore(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandosignore); + + Implementation i[] = { I_OnDatabaseRead, I_OnDatabaseWrite, I_OnBotPrivmsg }; + ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); + + ModuleManager::RegisterService(&this->osignoreservice); + } + + EventReturn OnDatabaseRead(const std::vector ¶ms) + { + if (params.size() >= 4 && params[0].equals_ci("OS") && params[1].equals_ci("IGNORE")) + { + service_reference ignore_service("ignore"); + if (ignore_service) + { + const Anope::string &mask = params[2]; + time_t time = params[3].is_pos_number_only() ? convertTo(params[3]) : 0; + const Anope::string &creator = params.size() > 4 ? params[4] : ""; + const Anope::string &reason = params.size() > 5 ? params[5] : ""; + ignore_service->AddIgnore(mask, creator, reason, time - Anope::CurTime); + + return EVENT_STOP; + } + } + + return EVENT_CONTINUE; + } + + void OnDatabaseWrite(void (*Write)(const Anope::string &)) + { + for (std::list::iterator ign = this->osignoreservice.GetIgnores().begin(), ign_end = this->osignoreservice.GetIgnores().end(); ign != ign_end; ) + { + if (ign->time && ign->time <= Anope::CurTime) + { + Log(LOG_DEBUG) << "[os_ignore] Expiring ignore entry " << ign->mask; + ign = this->osignoreservice.GetIgnores().erase(ign); + } + else + { + std::stringstream buf; + buf << "OS IGNORE " << ign->mask << " " << ign->time << " " << ign->creator << " :" << ign->reason; + Write(buf.str()); + ++ign; + } + } + } + + EventReturn OnBotPrivmsg(User *u, BotInfo *bi, Anope::string &message) + { + if (this->osignoreservice.Find(u->nick)) + return EVENT_STOP; + + return EVENT_CONTINUE; + } +}; + +MODULE_INIT(OSIgnore) diff --git a/modules/commands/os_ignore.h b/modules/commands/os_ignore.h new file mode 100644 index 000000000..056161a79 --- /dev/null +++ b/modules/commands/os_ignore.h @@ -0,0 +1,39 @@ +/* OperServ ignore interface + * + * (C) 2003-2011 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. + */ + + +struct IgnoreData +{ + Anope::string mask; + Anope::string creator; + Anope::string reason; + time_t time; /* When do we stop ignoring them? */ +}; + +class IgnoreService : public Service +{ + protected: + std::list ignores; + + IgnoreService(Module *c, const Anope::string &n) : Service(c, n) { } + + public: + virtual void AddIgnore(const Anope::string &mask, const Anope::string &creator, const Anope::string &reason, time_t delta = Anope::CurTime) = 0; + + virtual bool DelIgnore(const Anope::string &mask) = 0; + + inline void ClearIgnores() { this->ignores.clear(); } + + virtual IgnoreData *Find(const Anope::string &mask) = 0; + + inline std::list &GetIgnores() { return this->ignores; } +}; + diff --git a/modules/commands/os_jupe.cpp b/modules/commands/os_jupe.cpp new file mode 100644 index 000000000..a689b33e2 --- /dev/null +++ b/modules/commands/os_jupe.cpp @@ -0,0 +1,79 @@ +/* OperServ core functions + * + * (C) 2003-2011 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 ¶ms) + { + User *u = source.u; + const Anope::string &jserver = params[0]; + const Anope::string &reason = params.size() > 1 ? params[1] : ""; + Server *server = Server::Find(jserver); + + if (!isValidHost(jserver, 3)) + source.Reply(_("Please use a valid server name when juping")); + else if (server && (server == Me || server == Me->GetLinks().front())) + source.Reply(_("You can not jupe your services server or your uplink server.")); + else + { + Anope::string rbuf = "Juped by " + u->nick + (!reason.empty() ? ": " + reason : ""); + if (server) + ircdproto->SendSquit(jserver, rbuf); + Server *juped_server = new Server(Me, jserver, 1, rbuf, ircd->ts6 ? ts6_sid_retrieve() : "", SERVER_JUPED); + ircdproto->SendServer(juped_server); + + Log(LOG_ADMIN, u, this) << "on " << jserver << " (" << rbuf << ")"; + } + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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 \", 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, CORE), + commandosjupe(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandosjupe); + } +}; + +MODULE_INIT(OSJupe) diff --git a/modules/commands/os_kick.cpp b/modules/commands/os_kick.cpp new file mode 100644 index 000000000..341ea1ee6 --- /dev/null +++ b/modules/commands/os_kick.cpp @@ -0,0 +1,83 @@ +/* OperServ core functions + * + * (C) 2003-2011 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 ¶ms) + { + User *u = source.u; + const Anope::string &chan = params[0]; + const Anope::string &nick = params[1]; + const Anope::string &s = params[2]; + Channel *c; + User *u2; + + if (!(c = findchan(chan))) + { + source.Reply(CHAN_X_NOT_IN_USE, chan.c_str()); + return; + } + else if (c->bouncy_modes) + { + source.Reply(_("Services is unable to change modes. Are your servers' U:lines configured correctly?")); + return; + } + else if (!(u2 = finduser(nick))) + { + source.Reply(NICK_X_NOT_IN_USE, nick.c_str()); + return; + } + + c->Kick(source.owner, u2, "%s (%s)", u->nick.c_str(), s.c_str()); + Log(LOG_ADMIN, u, this) << "on " << u2->nick << " in " << c->name; + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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.owner->nick.c_str()); + return true; + } +}; + +class OSKick : public Module +{ + CommandOSKick commandoskick; + + public: + OSKick(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandoskick(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandoskick); + } +}; + +MODULE_INIT(OSKick) diff --git a/modules/commands/os_kill.cpp b/modules/commands/os_kill.cpp new file mode 100644 index 000000000..11a0636ef --- /dev/null +++ b/modules/commands/os_kill.cpp @@ -0,0 +1,72 @@ +/* OperServ core functions + * + * (C) 2003-2011 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 CommandOSKill : public Command +{ + public: + CommandOSKill(Module *creator) : Command(creator, "operserv/kill", 1, 2) + { + this->SetDesc(_("Kill a user")); + this->SetSyntax(_("\037user\037 [\037reason\037]")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + const Anope::string &nick = params[0]; + Anope::string reason = params.size() > 1 ? params[1] : ""; + + User *u2 = finduser(nick); + if (u2 == NULL) + source.Reply(NICK_X_NOT_IN_USE, nick.c_str()); + else if (u2->IsProtected() || u2->server == Me) + source.Reply(ACCESS_DENIED); + else + { + if (reason.empty()) + reason = "No reason specified"; + if (Config->AddAkiller) + reason = "(" + u->nick + ") " + reason; + Log(LOG_ADMIN, u, this) << "on " << u2->nick << " for " << reason; + u2->Kill(Config->OperServ, reason); + } + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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.")); + return true; + } +}; + +class OSKill : public Module +{ + CommandOSKill commandoskill; + + public: + OSKill(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandoskill(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandoskill); + } +}; + +MODULE_INIT(OSKill) diff --git a/modules/commands/os_list.cpp b/modules/commands/os_list.cpp new file mode 100644 index 000000000..9a582f97b --- /dev/null +++ b/modules/commands/os_list.cpp @@ -0,0 +1,187 @@ +/* OperServ core functions + * + * (C) 2003-2011 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 CommandOSChanList : public Command +{ + public: + CommandOSChanList(Module *creator) : Command(creator, "operserv/chanlist", 0, 2) + { + this->SetDesc(_("Lists all channel records")); + this->SetSyntax(_("[{\037pattern\037 | \037nick\037} [\037SECRET\037]]")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + const Anope::string &pattern = !params.empty() ? params[0] : ""; + const Anope::string &opt = params.size() > 1 ? params[1] : ""; + std::list Modes; + User *u2; + + if (!opt.empty() && opt.equals_ci("SECRET")) + { + Modes.push_back(CMODE_SECRET); + Modes.push_back(CMODE_PRIVATE); + } + + if (!pattern.empty() && (u2 = finduser(pattern))) + { + source.Reply(_("\002%s\002 channel list:\n" + "Name Users Modes Topic"), u2->nick.c_str()); + + for (UChannelList::iterator uit = u2->chans.begin(), uit_end = u2->chans.end(); uit != uit_end; ++uit) + { + ChannelContainer *cc = *uit; + + if (!Modes.empty()) + for (std::list::iterator it = Modes.begin(), it_end = Modes.end(); it != it_end; ++it) + if (!cc->chan->HasMode(*it)) + continue; + + source.Reply(_("%-20s %4d +%-6s %s"), cc->chan->name.c_str(), cc->chan->users.size(), cc->chan->GetModes(true, true).c_str(), !cc->chan->topic.empty() ? cc->chan->topic.c_str() : ""); + } + } + else + { + source.Reply(_("Channel list:\n" + "Name Users Modes Topic")); + + for (channel_map::const_iterator cit = ChannelList.begin(), cit_end = ChannelList.end(); cit != cit_end; ++cit) + { + Channel *c = cit->second; + + if (!pattern.empty() && !Anope::Match(c->name, pattern)) + continue; + if (!Modes.empty()) + for (std::list::iterator it = Modes.begin(), it_end = Modes.end(); it != it_end; ++it) + if (!c->HasMode(*it)) + continue; + + source.Reply(_("%-20s %4d +%-6s %s"), c->name.c_str(), c->users.size(), c->GetModes(true, true).c_str(), !c->topic.empty() ? c->topic.c_str() : ""); + } + } + + source.Reply(_("End of channel list.")); + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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" + "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.")); + return true; + } +}; + +class CommandOSUserList : public Command +{ + public: + CommandOSUserList(Module *creator) : Command(creator, "operserv/userlist", 0, 2) + { + this->SetDesc(_("Lists all user records")); + this->SetSyntax(_("[{\037pattern\037 | \037channel\037} [\037INVISIBLE\037]]")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + const Anope::string &pattern = !params.empty() ? params[0] : ""; + const Anope::string &opt = params.size() > 1 ? params[1] : ""; + Channel *c; + std::list Modes; + + if (!opt.empty() && opt.equals_ci("INVISIBLE")) + Modes.push_back(UMODE_INVIS); + + if (!pattern.empty() && (c = findchan(pattern))) + { + source.Reply(_("\002%s\002 users list:\n" + "Nick Mask"), pattern.c_str()); + + for (CUserList::iterator cuit = c->users.begin(), cuit_end = c->users.end(); cuit != cuit_end; ++cuit) + { + UserContainer *uc = *cuit; + + if (!Modes.empty()) + for (std::list::iterator it = Modes.begin(), it_end = Modes.end(); it != it_end; ++it) + if (!uc->user->HasMode(*it)) + continue; + + source.Reply(_("%-20s %s@%s"), uc->user->nick.c_str(), uc->user->GetIdent().c_str(), uc->user->GetDisplayedHost().c_str()); + } + } + else + { + source.Reply(_("Users list:\n" + "Nick Mask")); + + for (Anope::insensitive_map::iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it) + { + User *u2 = it->second; + + if (!pattern.empty()) + { + Anope::string mask = u2->nick + "!" + u2->GetIdent() + "@" + u2->GetDisplayedHost(); + if (!Anope::Match(mask, pattern)) + continue; + if (!Modes.empty()) + for (std::list::iterator mit = Modes.begin(), mit_end = Modes.end(); mit != mit_end; ++mit) + if (!u2->HasMode(*mit)) + continue; + } + source.Reply(_("%-20s %s@%s"), u2->nick.c_str(), u2->GetIdent().c_str(), u2->GetDisplayedHost().c_str()); + } + } + + source.Reply(_("End of users list.")); + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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.")); + return true; + } +}; + +class OSList : public Module +{ + CommandOSChanList commandoschanlist; + CommandOSUserList commandosuserlist; + + public: + OSList(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandoschanlist(this), commandosuserlist(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandoschanlist); + ModuleManager::RegisterService(&commandosuserlist); + } +}; + +MODULE_INIT(OSList) diff --git a/modules/commands/os_login.cpp b/modules/commands/os_login.cpp new file mode 100644 index 000000000..94a82e334 --- /dev/null +++ b/modules/commands/os_login.cpp @@ -0,0 +1,97 @@ +/* OperServ core functions + * + * (C) 2003-2011 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->SetDesc(Anope::printf(_("Login to %s"), Config->OperServ.c_str())); + this->SetSyntax(_("\037password\037")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + const Anope::string &password = params[0]; + + Oper *o = source.u->Account()->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 (source.u->GetExt("os_login_password_correct")) + source.Reply(_("You are already identified.")); + else if (o->password != password) + { + source.Reply(PASSWORD_INCORRECT); + bad_password(source.u); + } + else + { + Log(LOG_ADMIN, source.u, this) << "and successfully identified to " << source.owner->nick; + source.u->Extend("os_login_password_correct"); + source.Reply(_("Password accepted.")); + } + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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.owner->nick.c_str()); + return true; + } +}; + +class OSLogin : public Module +{ + CommandOSLogin commandoslogin; + + public: + OSLogin(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandoslogin(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandoslogin); + + ModuleManager::Attach(I_IsServicesOper, this); + } + + ~OSLogin() + { + for (Anope::insensitive_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it) + it->second->Shrink("os_login_password_correct"); + } + + EventReturn IsServicesOper(User *u) + { + if (!u->Account()->o->password.empty()) + { + if (u->GetExt("os_login_password_correct")) + return EVENT_ALLOW; + return EVENT_STOP; + } + + return EVENT_CONTINUE; + } +}; + +MODULE_INIT(OSLogin) diff --git a/modules/commands/os_mode.cpp b/modules/commands/os_mode.cpp new file mode 100644 index 000000000..b3e62ca25 --- /dev/null +++ b/modules/commands/os_mode.cpp @@ -0,0 +1,109 @@ +/* OperServ core functions + * + * (C) 2003-2011 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 CommandOSMode : public Command +{ + public: + CommandOSMode(Module *creator) : Command(creator, "operserv/mode", 2, 2) + { + this->SetDesc(_("Change channel modes")); + this->SetSyntax(_("\037channel\037 \037modes\037")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + const Anope::string &target = params[0]; + const Anope::string &modes = params[1]; + + Channel *c = findchan(target); + if (!c) + source.Reply(CHAN_X_NOT_IN_USE, target.c_str()); + else if (c->bouncy_modes) + source.Reply(_("Services is unable to change modes. Are your servers' U:lines configured correctly?")); + else + { + c->SetModes(source.owner, false, modes.c_str()); + + Log(LOG_ADMIN, u, this) << modes << " on " << target; + } + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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.")); + return true; + } +}; + +class CommandOSUMode : public Command +{ + public: + CommandOSUMode(Module *creator) : Command(creator, "operserv/umode", 2, 2) + { + this->SetDesc(_("Change channel or user modes")); + this->SetSyntax(_("\037user\037 \037modes\037")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + const Anope::string &target = params[0]; + const Anope::string &modes = params[1]; + + User *u2 = finduser(target); + if (!u2) + source.Reply(NICK_X_NOT_IN_USE, target.c_str()); + else + { + u2->SetModes(source.owner, "%s", modes.c_str()); + source.Reply(_("Changed usermodes of \002%s\002 to %s."), u2->nick.c_str(), modes.c_str()); + + u2->SendMessage(source.owner, _("\002%s\002 changed your usermodes to %s."), u->nick.c_str(), modes.c_str()); + + Log(LOG_ADMIN, u, this) << modes << " on " << target; + } + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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.")); + return true; + } +}; + +class OSMode : public Module +{ + CommandOSMode commandosmode; + CommandOSUMode commandosumode; + + public: + OSMode(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandosmode(this), commandosumode(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandosmode); + ModuleManager::RegisterService(&commandosumode); + } +}; + +MODULE_INIT(OSMode) diff --git a/modules/commands/os_modinfo.cpp b/modules/commands/os_modinfo.cpp new file mode 100644 index 000000000..38d9cf905 --- /dev/null +++ b/modules/commands/os_modinfo.cpp @@ -0,0 +1,245 @@ +/* OperServ core functions + * + * (C) 2003-2011 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 CommandOSModInfo : public Command +{ + public: + CommandOSModInfo(Module *creator) : Command(creator, "operserv/modinfo", 1, 1) + { + this->SetDesc(_("Info about a loaded module")); + this->SetSyntax(_("\037modname\037")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + const Anope::string &file = params[0]; + + 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() : "?", do_strftime(m->created).c_str()); + + std::vector services = ModuleManager::GetServiceKeys(); + for (unsigned i = 0; i < services.size(); ++i) + { + Service *s = ModuleManager::GetService(services[i]); + if (!s || s->owner != m) + continue; + source.Reply(_(" Providing service: \002%s\002"), s->name.c_str()); + + for (botinfo_map::const_iterator it = BotListByNick.begin(), it_end = BotListByNick.end(); it != it_end; ++it) + { + BotInfo *bi = it->second; + + for (BotInfo::command_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 != s->name) + 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(), s->name.c_str()); + } + } + } + } + else + source.Reply(_("No information about module \002%s\002 is available"), file.c_str()); + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("This command lists information about the specified loaded module")); + return true; + } +}; + +class CommandOSModList : public Command +{ + public: + CommandOSModList(Module *creator) : Command(creator, "operserv/modlist", 0, 1) + { + this->SetDesc(_("List loaded modules")); + this->SetSyntax(_("[Core|3rd|protocol|encryption|supported]")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + const Anope::string ¶m = !params.empty() ? params[0] : ""; + + int count = 0; + int showCore = 0; + int showThird = 1; + int showProto = 1; + int showEnc = 1; + int showSupported = 1; + int showDB = 1; + + char core[] = "Core"; + char third[] = "3rd"; + char proto[] = "Protocol"; + char enc[] = "Encryption"; + char supported[] = "Supported"; + char db[] = "Database"; + + if (!param.empty()) + { + if (param.equals_ci(core)) + { + showCore = 1; + showThird = 0; + showProto = 0; + showEnc = 0; + showSupported = 0; + showDB = 0; + } + else if (param.equals_ci(third)) + { + showCore = 0; + showThird = 1; + showSupported = 0; + showProto = 0; + showEnc = 0; + showDB = 0; + } + else if (param.equals_ci(proto)) + { + showCore = 0; + showThird = 0; + showProto = 1; + showEnc = 0; + showSupported = 0; + showDB = 0; + } + else if (param.equals_ci(supported)) + { + showCore = 0; + showThird = 0; + showProto = 0; + showSupported = 1; + showEnc = 0; + showDB = 0; + } + else if (param.equals_ci(enc)) + { + showCore = 0; + showThird = 0; + showProto = 0; + showSupported = 0; + showEnc = 1; + showDB = 0; + } + else if (param.equals_ci(db)) + { + showCore = 0; + showThird = 0; + showProto = 0; + showSupported = 0; + showEnc = 0; + showDB = 1; + } + } + + source.Reply(_("Current Module list:")); + + for (std::list::iterator it = Modules.begin(), it_end = Modules.end(); it != it_end; ++it) + { + Module *m = *it; + + switch (m->type) + { + case CORE: + if (showCore) + { + source.Reply(_("Module: \002%s\002 [%s] [%s]"), m->name.c_str(), m->version.c_str(), core); + ++count; + } + break; + case THIRD: + if (showThird) + { + source.Reply(_("Module: \002%s\002 [%s] [%s]"), m->name.c_str(), m->version.c_str(), third); + ++count; + } + break; + case PROTOCOL: + if (showProto) + { + source.Reply(_("Module: \002%s\002 [%s] [%s]"), m->name.c_str(), m->version.c_str(), proto); + ++count; + } + break; + case SUPPORTED: + if (showSupported) + { + source.Reply(_("Module: \002%s\002 [%s] [%s]"), m->name.c_str(), m->version.c_str(), supported); + ++count; + } + break; + case ENCRYPTION: + if (showEnc) + { + source.Reply(_("Module: \002%s\002 [%s] [%s]"), m->name.c_str(), m->version.c_str(), enc); + ++count; + } + break; + case DATABASE: + if (showDB) + { + source.Reply(_("Module: \002%s\002 [%s] [%s]"), m->name.c_str(), m->version.c_str(), db); + ++count; + } + break; + default: + break; + } + } + if (!count) + source.Reply(_("No modules currently loaded")); + else + source.Reply(_("%d Modules loaded."), count); + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Lists all currently loaded modules.")); + return true; + } +}; + +class OSModInfo : public Module +{ + CommandOSModInfo commandosmodinfo; + CommandOSModList commandosmodlist; + + public: + OSModInfo(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandosmodinfo(this), commandosmodlist(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandosmodinfo); + ModuleManager::RegisterService(&commandosmodlist); + } +}; + +MODULE_INIT(OSModInfo) diff --git a/modules/commands/os_module.cpp b/modules/commands/os_module.cpp new file mode 100644 index 000000000..562e130aa --- /dev/null +++ b/modules/commands/os_module.cpp @@ -0,0 +1,202 @@ +/* OperServ core functions + * + * (C) 2003-2011 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 CommandOSModLoad : public Command +{ + public: + CommandOSModLoad(Module *creator) : Command(creator, "operserv/modload", 1, 1) + { + this->SetDesc(_("Load a module")); + this->SetSyntax(_("\037modname\037")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + const Anope::string &mname = params[0]; + + ModuleReturn status = ModuleManager::LoadModule(mname, u); + if (status == MOD_ERR_OK) + { + ircdproto->SendGlobops(source.owner, "%s loaded module %s", u->nick.c_str(), mname.c_str()); + source.Reply(_("Module \002%s\002 loaded"), mname.c_str()); + + /* If a user is loading this module, then the core databases have already been loaded + * so trigger the event manually + */ + Module *m = ModuleManager::FindModule(mname); + if (m) + m->OnPostLoadDatabases(); + } + else if (status == MOD_ERR_EXISTS) + source.Reply(_("Module \002%s\002 is already loaded."), mname.c_str()); + else + source.Reply(_("Unable to load module \002%s\002"), mname.c_str()); + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("This command loads the module named FileName from the modules\n" + "directory.")); + return true; + } +}; + +class CommandOSModReLoad : public Command +{ + public: + CommandOSModReLoad(Module *creator) : Command(creator, "operserv/modreload", 1, 1) + { + this->SetDesc(_("Reload a module")); + this->SetSyntax(_("\037modname\037")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + 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()); + return; + } + + if (!m->handle || m->GetPermanent()) + { + source.Reply(_("Unable to remove module \002%s\002"), m->name.c_str()); + return; + } + + /* Unrecoverable */ + bool fatal = m->type == PROTOCOL; + ModuleReturn status = ModuleManager::UnloadModule(m, u); + + if (status != MOD_ERR_OK) + { + source.Reply(_("Unable to remove module \002%s\002"), mname.c_str()); + return; + } + + status = ModuleManager::LoadModule(mname, u); + if (status == MOD_ERR_OK) + { + ircdproto->SendGlobops(source.owner, "%s reloaded module %s", u->nick.c_str(), mname.c_str()); + source.Reply(_("Module \002%s\002 reloaded"), mname.c_str()); + + /* If a user is loading this module, then the core databases have already been loaded + * so trigger the event manually + */ + m = ModuleManager::FindModule(mname); + if (m) + m->OnPostLoadDatabases(); + } + else + { + if (fatal) + throw FatalException("Unable to reload module " + mname); + else + source.Reply(_("Unable to load module \002%s\002"), mname.c_str()); + } + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("This command reloads the module named FileName.")); + return true; + } +}; + +class CommandOSModUnLoad : public Command +{ + public: + CommandOSModUnLoad(Module *creator) : Command(creator, "operserv/modunload", 1, 1) + { + this->SetDesc(_("Un-Load a module")); + this->SetSyntax(_("\037modname\037")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + 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()); + return; + } + + if (!m->handle || m->GetPermanent() || m->type == PROTOCOL) + { + source.Reply(_("Unable to remove module \002%s\002"), m->name.c_str()); + return; + } + + Log() << "Trying to unload module [" << mname << "]"; + + ModuleReturn status = ModuleManager::UnloadModule(m, u); + + if (status == MOD_ERR_OK) + { + source.Reply(_("Module \002%s\002 unloaded"), mname.c_str()); + ircdproto->SendGlobops(source.owner, "%s unloaded module %s", u->nick.c_str(), mname.c_str()); + } + else + source.Reply(_("Unable to remove module \002%s\002"), mname.c_str()); + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("This command unloads the module named FileName from the modules\n" + "directory.")); + return true; + } +}; + +class OSModule : public Module +{ + CommandOSModLoad commandosmodload; + CommandOSModReLoad commandosmodreload; + CommandOSModUnLoad commandosmodunload; + + public: + OSModule(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandosmodload(this), commandosmodreload(this), commandosmodunload(this) + { + this->SetAuthor("Anope"); + this->SetPermanent(true); + + ModuleManager::RegisterService(&commandosmodload); + ModuleManager::RegisterService(&commandosmodreload); + ModuleManager::RegisterService(&commandosmodunload); + } +}; + +MODULE_INIT(OSModule) diff --git a/modules/commands/os_news.cpp b/modules/commands/os_news.cpp new file mode 100644 index 000000000..9c9aae0a4 --- /dev/null +++ b/modules/commands/os_news.cpp @@ -0,0 +1,450 @@ +/* OperServ core functions + * + * (C) 2003-2011 Anope Team + * Contact us at info@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 "global.h" +#include "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.")} + } +}; + +class MyNewsService : public NewsService +{ + std::vector 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]; + } + + void AddNewsItem(NewsItem *n) + { + this->newsItems[n->type].push_back(n); + } + + void DelNewsItem(NewsItem *n) + { + std::vector &list = this->GetNewsList(n->type); + std::vector::iterator it = std::find(list.begin(), list.end(), n); + if (it != list.end()) + list.erase(it); + delete n; + } + + std::vector &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 +{ + service_reference ns; + + protected: + void DoList(CommandSource &source, NewsType type, const char **msgs) + { + std::vector &list = this->ns->GetNewsList(type); + if (list.empty()) + source.Reply(msgs[MSG_LIST_NONE]); + else + { + source.Reply(msgs[MSG_LIST_HEADER]); + for (unsigned i = 0, end = list.size(); i < end; ++i) + source.Reply(_("%5d (%s by %s)\n"" %s"), i + 1, do_strftime(list[i]->time).c_str(), !list[i]->who.empty() ? list[i]->who.c_str() : "", list[i]->text.c_str()); + source.Reply(END_OF_ANY_LIST, "News"); + } + + return; + } + + void DoAdd(CommandSource &source, const std::vector ¶ms, NewsType type, const char **msgs) + { + const Anope::string text = params.size() > 1 ? params[1] : ""; + + if (text.empty()) + this->OnSyntaxError(source, "ADD"); + else + { + if (readonly) + source.Reply(READ_ONLY_MODE); + + NewsItem *news = new NewsItem(); + news->type = type; + news->text = text; + news->time = Anope::CurTime; + news->who = source.u->nick; + + this->ns->AddNewsItem(news); + + source.Reply(msgs[MSG_ADDED]); + } + + return; + } + + void DoDel(CommandSource &source, const std::vector ¶ms, NewsType type, const char **msgs) + { + const Anope::string &text = params.size() > 1 ? params[1] : ""; + + if (text.empty()) + this->OnSyntaxError(source, "DEL"); + else + { + std::vector &list = this->ns->GetNewsList(type); + if (list.empty()) + source.Reply(msgs[MSG_LIST_NONE]); + else + { + if (readonly) + source.Reply(READ_ONLY_MODE); + if (!text.equals_ci("ALL")) + { + try + { + unsigned num = convertTo(text); + if (num > 0 && num <= list.size()) + { + this->ns->DelNewsItem(list[num - 1]); + source.Reply(msgs[MSG_DELETED], num); + 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]); + } + } + } + + return; + } + + void DoNews(CommandSource &source, const std::vector ¶ms, NewsType type) + { + if (!this->ns) + return; + + const Anope::string &cmd = params[0]; + + const char **msgs = findmsgs(type); + if (!msgs) + throw CoreException("news: Invalid type to do_news()"); + + if (cmd.equals_ci("LIST")) + return this->DoList(source, type, msgs); + else if (cmd.equals_ci("ADD")) + return this->DoAdd(source, params, type, msgs); + else if (cmd.equals_ci("DEL")) + return this->DoDel(source, params, type, msgs); + else + this->OnSyntaxError(source, ""); + + return; + } + public: + NewsBase(Module *creator, const Anope::string &newstype) : Command(creator, newstype, 1, 2), ns("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 ¶ms) = 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 ¶ms) + { + return this->DoNews(source, params, NEWS_LOGON); + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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.)\n" + "NewsCount can be configured in services.conf.\n" + " \n" + "LOGONNEWS may only be used by Services Operators."), Config->NewsCount); + 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 ¶ms) + { + return this->DoNews(source, params, NEWS_OPER); + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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.)\n" + "NewsCount can be configured in services.conf.\n" + " \n" + "OPERNEWS may only be used by Services Operators."), Config->NewsCount); + 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 ¶ms) + { + return this->DoNews(source, params, NEWS_RANDOM); + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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.\n" + " \n" + "RANDOMNEWS may only be used by Services Operators.")); + return true; + } +}; + +class OSNews : public Module +{ + MyNewsService newsservice; + + CommandOSLogonNews commandoslogonnews; + CommandOSOperNews commandosopernews; + CommandOSRandomNews commandosrandomnews; + + void DisplayNews(User *u, NewsType Type) + { + std::vector &newsList = this->newsservice.GetNewsList(Type); + if (newsList.empty()) + 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"); + + static unsigned cur_rand_news = 0; + unsigned displayed = 0; + for (unsigned i = 0, end = newsList.size(); i < end; ++i) + { + if (Type == NEWS_RANDOM && i != cur_rand_news) + continue; + + BotInfo *gl = findbot(Config->Global); + if (!gl && !BotListByNick.empty()) + gl = BotListByNick.begin()->second; + BotInfo *os = findbot(Config->OperServ); + if (!os) + os = gl; + if (gl) + u->SendMessage(Type != NEWS_OPER ? gl : os, msg.c_str(), do_strftime(newsList[i]->time).c_str(), newsList[i]->text.c_str()); + + ++displayed; + + if (Type == NEWS_RANDOM) + { + ++cur_rand_news; + break; + } + else if (displayed >= Config->NewsCount) + 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, CORE), + newsservice(this), commandoslogonnews(this), commandosopernews(this), commandosrandomnews(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandoslogonnews); + ModuleManager::RegisterService(&commandosopernews); + ModuleManager::RegisterService(&commandosrandomnews); + + Implementation i[] = { I_OnUserModeSet, I_OnUserConnect, I_OnDatabaseRead, I_OnDatabaseWrite }; + ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); + + ModuleManager::RegisterService(&this->newsservice); + } + + void OnUserModeSet(User *u, UserModeName Name) + { + if (Name == UMODE_OPER) + DisplayNews(u, NEWS_OPER); + } + + void OnUserConnect(dynamic_reference &user, bool &) + { + if (!user || !user->server->IsSynced()) + return; + + DisplayNews(user, NEWS_LOGON); + DisplayNews(user, NEWS_RANDOM); + } + + EventReturn OnDatabaseRead(const std::vector ¶ms) + { + if (params[0].equals_ci("OS") && params.size() >= 7 && params[1].equals_ci("NEWS")) + { + NewsItem *n = new NewsItem(); + // params[2] was news number + n->time = params[3].is_number_only() ? convertTo(params[3]) : 0; + n->who = params[4]; + if (params[5].equals_ci("LOGON")) + n->type = NEWS_LOGON; + else if (params[5].equals_ci("RANDOM")) + n->type = NEWS_RANDOM; + else if (params[5].equals_ci("OPER")) + n->type = NEWS_OPER; + n->text = params[6]; + + this->newsservice.AddNewsItem(n); + + return EVENT_STOP; + } + + return EVENT_CONTINUE; + } + + void OnDatabaseWrite(void (*Write)(const Anope::string &)) + { + for (unsigned i = 0; i < 3; ++i) + { + std::vector &list = this->newsservice.GetNewsList(static_cast(i)); + for (std::vector::iterator it = list.begin(); it != list.end(); ++it) + { + NewsItem *n = *it; + Anope::string ntype; + if (n->type == NEWS_LOGON) + ntype = "LOGON"; + else if (n->type == NEWS_RANDOM) + ntype = "RANDOM"; + else if (n->type == NEWS_OPER) + ntype = "OPER"; + Anope::string buf = "OS NEWS 0 " + stringify(n->time) + " " + n->who + " " + ntype + " :" + n->text; + Write(buf); + } + } + } +}; + +MODULE_INIT(OSNews) diff --git a/modules/commands/os_news.h b/modules/commands/os_news.h new file mode 100644 index 000000000..c55f56d42 --- /dev/null +++ b/modules/commands/os_news.h @@ -0,0 +1,39 @@ +#ifndef OS_NEWS +#define OS_NEWS + +enum NewsType +{ + NEWS_LOGON, + NEWS_RANDOM, + NEWS_OPER +}; + +struct NewsMessages +{ + NewsType type; + Anope::string name; + const char *msgs[10]; +}; + +struct NewsItem +{ + NewsType type; + Anope::string text; + Anope::string who; + time_t time; +}; + +class NewsService : public Service +{ + public: + NewsService(Module *m) : Service(m, "news") { } + + virtual void AddNewsItem(NewsItem *n) = 0; + + virtual void DelNewsItem(NewsItem *n) = 0; + + virtual std::vector &GetNewsList(NewsType t) = 0; +}; + +#endif // OS_NEWS + diff --git a/modules/commands/os_noop.cpp b/modules/commands/os_noop.cpp new file mode 100644 index 000000000..2eec522a3 --- /dev/null +++ b/modules/commands/os_noop.cpp @@ -0,0 +1,94 @@ +/* OperServ core functions + * + * (C) 2003-2011 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 CommandOSNOOP : public Command +{ + public: + CommandOSNOOP(Module *creator) : Command(creator, "operserv/noop", 2, 2) + { + this->SetDesc(_("Temporarily remove all O:lines of a server remotely")); + this->SetSyntax(_("SET \037server\037")); + this->SetSyntax(_("REVOKE \037server\037")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + const Anope::string &cmd = params[0]; + const Anope::string &server = params[1]; + + Server *s = Server::Find(server); + if (s == NULL) + source.Reply(_("Server %s does not exist."), server.c_str()); + else if (cmd.equals_ci("SET")) + { + /* Remove the O:lines */ + ircdproto->SendSVSNOOP(s, true); + + Log(LOG_ADMIN, u, this) << "SET on " << s->GetName(); + source.Reply(_("All O:lines of \002%s\002 have been removed."), s->GetName().c_str()); + + Anope::string reason = "NOOP command used by " + u->nick; + /* Kill all the IRCops of the server */ + for (Anope::insensitive_map::iterator it = UserListByNick.begin(); it != UserListByNick.end();) + { + User *u2 = it->second; + ++it; + + if (u2 && u2->HasMode(UMODE_OPER) && u2->server == s) + u2->Kill(Config->OperServ, reason); + } + } + else if (cmd.equals_ci("REVOKE")) + { + Log(LOG_ADMIN, u, this) << "REVOKE on " << s->GetName(); + ircdproto->SendSVSNOOP(s, false); + source.Reply(_("All O:lines of \002%s\002 have been reset."), s->GetName().c_str()); + } + else + this->OnSyntaxError(source, ""); + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("\002NOOP SET\002 remove all O:lines of the given\n" + "\002server\002 and kill all IRCops currently on it to\n" + "prevent them from rehashing the server (because this\n" + "would just cancel the effect).\n" + "\002NOOP REVOKE\002 makes all removed O:lines available again\n" + "on the given \002server\002.\n")); + return true; + } +}; + +class OSNOOP : public Module +{ + CommandOSNOOP commandosnoop; + + public: + OSNOOP(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandosnoop(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandosnoop); + } +}; + +MODULE_INIT(OSNOOP) diff --git a/modules/commands/os_oline.cpp b/modules/commands/os_oline.cpp new file mode 100644 index 000000000..adebc52a1 --- /dev/null +++ b/modules/commands/os_oline.cpp @@ -0,0 +1,83 @@ +/* OperServ core functions + * + * (C) 2003-2011 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 CommandOSOLine : public Command +{ + public: + CommandOSOLine(Module *creator) : Command(creator, "operserv/oline", 2, 2) + { + this->SetDesc(_("Give Operflags to a certain user")); + this->SetSyntax(_("\037nick\037 \037flags\037")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + const Anope::string &nick = params[0]; + const Anope::string &flag = params[1]; + User *u2 = NULL; + + /* let's check whether the user is online */ + if (!(u2 = finduser(nick))) + source.Reply(NICK_X_NOT_IN_USE, nick.c_str()); + else if (u2 && flag[0] == '+') + { + ircdproto->SendSVSO(Config->OperServ, nick, flag); + u2->SetMode(source.owner, UMODE_OPER); + u2->SendMessage(source.owner, _("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()); + ircdproto->SendGlobops(source.owner, "\2%s\2 used OLINE for %s", u->nick.c_str(), nick.c_str()); + } + else if (u2 && flag[0] == '-') + { + ircdproto->SendSVSO(Config->OperServ, nick, flag); + source.Reply(_("Operflags \002%s\002 have been added for \002%s\002."), flag.c_str(), nick.c_str()); + ircdproto->SendGlobops(source.owner, "\2%s\2 used OLINE for %s", u->nick.c_str(), nick.c_str()); + } + else + this->OnSyntaxError(source, ""); + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Allows Services Opers 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.")); + return true; + } +}; + +class OSOLine : public Module +{ + CommandOSOLine commandosoline; + + public: + OSOLine(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandosoline(this) + { + this->SetAuthor("Anope"); + + if (!ircd || !ircd->omode) + throw ModuleException("Your IRCd does not support OMODE."); + + ModuleManager::RegisterService(&commandosoline); + } +}; + +MODULE_INIT(OSOLine) diff --git a/modules/commands/os_oper.cpp b/modules/commands/os_oper.cpp new file mode 100644 index 000000000..237c0b5bc --- /dev/null +++ b/modules/commands/os_oper.cpp @@ -0,0 +1,219 @@ +/* OperServ core functions + * + * (C) 2003-2011 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 CommandOSOper : public Command +{ + 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 ¶ms) + { + const Anope::string &subcommand = params[0]; + + if (subcommand.equals_ci("ADD") && params.size() > 2) + { + const Anope::string &oper = params[1]; + const Anope::string &type = params[2]; + + NickAlias *na = findnick(oper); + if (na == NULL) + source.Reply(NICK_X_NOT_REGISTERED, oper.c_str()); + else + { + OperType *ot = OperType::Find(type); + if (ot == NULL) + source.Reply(_("Oper type \2%s\2 has not been configured."), type.c_str()); + else + { + if (na->nc->o) + delete na->nc->o; + na->nc->o = new Oper(na->nc->display, "", "", ot); + + Log(LOG_ADMIN, source.u, this) << "ADD " << na->nick << " as type " << ot->GetName(); + source.Reply("%s (%s) added to the \2%s\2 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]; + + NickAlias *na = findnick(oper); + if (na == NULL) + source.Reply(NICK_X_NOT_REGISTERED, oper.c_str()); + else if (!na->nc || !na->nc->o) + source.Reply(_("Nick \2%s\2 is not a services operator."), oper.c_str()); + else + { + delete na->nc->o; + na->nc->o = NULL; + + Log(LOG_ADMIN, source.u, 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) + { + NickCore *nc = it->second; + + if (!nc->o) + continue; + + source.Reply(_("%-8s %s"), nc->o->name.c_str(), nc->o->ot->GetName().c_str()); + for (std::list::const_iterator it2 = nc->aliases.begin(), it2_end = nc->aliases.end(); it2 != it2_end; ++it2) + if (Oper::Find((*it2)->nick) != NULL) + source.Reply(_(" This oper is configured in the configuration file as %s"), (*it2)->nick.c_str()); + for (std::list::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") && params.size() > 1) + { + Anope::string fulltype = params[1]; + if (params.size() > 2) + fulltype += " " + params[2]; + OperType *ot = OperType::Find(fulltype); + if (ot == NULL) + source.Reply(_("Oper type \2%s\2 has not been configured."), fulltype.c_str()); + else + { + if (ot->GetCommands().empty()) + source.Reply(_("Opertype \2%s\2 has no allowed commands."), ot->GetName().c_str()); + else + { + source.Reply(_("Available commands for \2%s\2:"), ot->GetName().c_str()); + Anope::string buf; + for (std::list::const_iterator it = ot->GetCommands().begin(), it_end = ot->GetCommands().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 \2%s\2 has no allowed privileges."), ot->GetName().c_str()); + else + { + source.Reply(_("Available privileges for \2%s\2:"), ot->GetName().c_str()); + Anope::string buf; + for (std::list::const_iterator it = ot->GetPrivs().begin(), it_end = ot->GetPrivs().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 \2%s\2 receives modes \2%s\2 once identifying."), ot->GetName().c_str(), ot->modes.c_str()); + } + } + else + this->OnSyntaxError(source, subcommand); + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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 +{ + CommandOSOper commandosoper; + + public: + OSOper(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandosoper(this) + { + this->SetAuthor("Anope"); + + Implementation i[] = { I_OnDatabaseWrite, I_OnDatabaseRead }; + ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); + + ModuleManager::RegisterService(&commandosoper); + } + + void OnDatabaseWrite(void (*Write)(const Anope::string &)) + { + for (nickcore_map::const_iterator it = NickCoreList.begin(), it_end = NickCoreList.end(); it != it_end; ++it) + { + NickCore *nc = it->second; + if (!nc->o) + continue; + bool found = false; + for (std::list::const_iterator it2 = nc->aliases.begin(), it2_end = nc->aliases.end(); it2 != it2_end; ++it2) + if (Oper::Find((*it2)->nick) != NULL) + found = true; + if (found == false) + { + Write("OPER " + nc->display + " :" + nc->o->ot->GetName()); + } + } + } + + EventReturn OnDatabaseRead(const std::vector ¶ms) + { + if (params[0] == "OPER" && params.size() > 2) + { + NickCore *nc = findcore(params[1]); + if (!nc || nc->o) + return EVENT_CONTINUE; + OperType *ot = OperType::Find(params[2]); + if (ot == NULL) + return EVENT_CONTINUE; + nc->o = new Oper(nc->display, "", "", ot); + Log(LOG_NORMAL, "operserv/oper") << "Tied oper " << nc->display << " to type " << ot->GetName(); + } + return EVENT_CONTINUE; + } +}; + +MODULE_INIT(OSOper) diff --git a/modules/commands/os_reload.cpp b/modules/commands/os_reload.cpp new file mode 100644 index 000000000..d4d8eac7c --- /dev/null +++ b/modules/commands/os_reload.cpp @@ -0,0 +1,72 @@ +/* OperServ core functions + * + * (C) 2003-2011 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")); + this->SetSyntax(""); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + ServerConfig *old_config = Config; + + try + { + Config = new ServerConfig(); + FOREACH_MOD(I_OnReload, OnReload()); + delete old_config; + source.Reply(_("Services' configuration file has been reloaded.")); + } + catch (const ConfigException &ex) + { + Config = old_config; + Log() << "Error reloading configuration file: " << ex.GetReason(); + source.Reply(_("Error reloading confguration file: ") + ex.GetReason()); + } + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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, CORE), + commandosreload(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandosreload); + } +}; + +MODULE_INIT(OSReload) diff --git a/modules/commands/os_session.cpp b/modules/commands/os_session.cpp new file mode 100644 index 000000000..990cd1b4b --- /dev/null +++ b/modules/commands/os_session.cpp @@ -0,0 +1,749 @@ +/* OperServ core functions + * + * (C) 2003-2011 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 "os_session.h" + +static service_reference sessionservice("session"); + +class MySessionService : public SessionService +{ + SessionMap Sessions; + ExceptionVector Exceptions; + public: + MySessionService(Module *m) : SessionService(m) { } + + void AddException(Exception *e) + { + this->Exceptions.push_back(e); + } + + void DelException(Exception *e) + { + 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) + { + for (std::vector::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) || (u->ip() && Anope::Match(u->ip.addr(), e->mask))) + return e; + } + return NULL; + } + + Exception *FindException(const Anope::string &host) + { + for (std::vector::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; + } + + return NULL; + } + + ExceptionVector &GetExceptions() + { + return this->Exceptions; + } + + void AddSession(Session *s) + { + this->Sessions[s->host] = s; + } + + void DelSession(Session *s) + { + this->Sessions.erase(s->host); + } + + Session *FindSession(const Anope::string &mask) + { + SessionMap::iterator it = this->Sessions.find(mask); + if (it != this->Sessions.end()) + return it->second; + return NULL; + } + + SessionMap &GetSessions() + { + return this->Sessions; + } +}; + +class ExpireTimer : public Timer +{ + public: + ExpireTimer() : Timer(Config->ExpireTimeout, Anope::CurTime, true) { } + + void Tick(time_t) + { + if (!sessionservice) + return; + for (unsigned i = sessionservice->GetExceptions().size(); i > 0; --i) + { + Exception *e = sessionservice->GetExceptions()[i - 1]; + + if (!e->expires || e->expires > Anope::CurTime) + continue; + BotInfo *bi = findbot(Config->OperServ); + if (Config->WallExceptionExpire && bi) + ircdproto->SendGlobops(bi, "Session exception for %s has expired.", e->mask.c_str()); + sessionservice->DelException(e); + delete e; + } + } +}; + +class ExceptionDelCallback : public NumberList +{ + protected: + CommandSource &source; + unsigned Deleted; + public: + ExceptionDelCallback(CommandSource &_source, const Anope::string &numlist) : NumberList(numlist, true), source(_source), Deleted(0) + { + } + + ~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) + { + if (!Number || Number > sessionservice->GetExceptions().size()) + return; + + ++Deleted; + + DoDel(source, Number - 1); + } + + static void DoDel(CommandSource &source, unsigned index) + { + Exception *e = sessionservice->GetExceptions()[index]; + FOREACH_MOD(I_OnExceptionDel, OnExceptionDel(source.u, e)); + + sessionservice->DelException(e); + delete e; + } +}; + +class ExceptionListCallback : public NumberList +{ + protected: + CommandSource &source; + bool SentHeader; + public: + ExceptionListCallback(CommandSource &_source, const Anope::string &numlist) : NumberList(numlist, false), source(_source), SentHeader(false) + { + } + + virtual void HandleNumber(unsigned Number) + { + if (!Number || Number > sessionservice->GetExceptions().size()) + return; + + if (!SentHeader) + { + SentHeader = true; + source.Reply(_("Current Session Limit Exception list:")); + source.Reply(_("Num Limit Host")); + } + + DoList(source, Number - 1); + } + + static void DoList(CommandSource &source, unsigned index) + { + if (index >= sessionservice->GetExceptions().size()) + return; + + source.Reply(_("%3d %4d %s"), index + 1, sessionservice->GetExceptions()[index]->limit, sessionservice->GetExceptions()[index]->mask.c_str()); + } +}; + +class ExceptionViewCallback : public ExceptionListCallback +{ + public: + ExceptionViewCallback(CommandSource &_source, const Anope::string &numlist) : ExceptionListCallback(_source, numlist) + { + } + + void HandleNumber(unsigned Number) + { + if (!Number || Number > sessionservice->GetExceptions().size()) + return; + + if (!SentHeader) + { + SentHeader = true; + source.Reply(_("Current Session Limit Exception list:")); + } + + DoList(source, Number - 1); + } + + static void DoList(CommandSource &source, unsigned index) + { + if (index >= sessionservice->GetExceptions().size()) + return; + + Anope::string expirebuf = expire_left(source.u->Account(), sessionservice->GetExceptions()[index]->expires); + + source.Reply(_("%3d. %s (by %s on %s; %s)\n " " Limit: %-4d - %s"), index + 1, sessionservice->GetExceptions()[index]->mask.c_str(), !sessionservice->GetExceptions()[index]->who.empty() ? sessionservice->GetExceptions()[index]->who.c_str() : "", do_strftime((sessionservice->GetExceptions()[index]->time ? sessionservice->GetExceptions()[index]->time : Anope::CurTime)).c_str(), expirebuf.c_str(), sessionservice->GetExceptions()[index]->limit, sessionservice->GetExceptions()[index]->reason.c_str()); + } +}; + +class CommandOSSession : public Command +{ + private: + void DoList(CommandSource &source, const std::vector ¶ms) + { + Anope::string param = params[1]; + + unsigned mincount = 0; + try + { + mincount = convertTo(param); + } + catch (const ConvertException &) { } + + if (mincount <= 1) + source.Reply(_("Invalid threshold value. It must be a valid integer greater than 1.")); + else + { + source.Reply(_("Hosts with at least \002%d\002 sessions:"), mincount); + source.Reply(_("Sessions Host")); + + for (SessionService::SessionMap::iterator it = sessionservice->GetSessions().begin(), it_end = sessionservice->GetSessions().end(); it != it_end; ++it) + { + Session *session = it->second; + + if (session->count >= mincount) + source.Reply(_("%6d %s"), session->count, session->host.c_str()); + } + } + + return; + } + + void DoView(CommandSource &source, const std::vector ¶ms) + { + Anope::string param = params[1]; + Session *session = sessionservice->FindSession(param); + + if (!session) + source.Reply(_("\002%s\002 not found on session list."), param.c_str()); + else + { + Exception *exception = sessionservice->FindException(param); + source.Reply(_("The host \002%s\002 currently has \002%d\002 sessions with a limit of \002%d\002."), param.c_str(), session->count, exception ? exception-> limit : Config->DefSessionLimit); + } + + return; + } + 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 ¶ms) + { + const Anope::string &cmd = params[0]; + + if (!Config->LimitSessions) + { + source.Reply(_("Session limiting is disabled.")); + return; + } + + 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, ""); + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Allows Services Operators to view the session list.\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" + "\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" + "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 ¶ms) + { + User *u = source.u; + 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() ? dotime(expiry) : Config->ExceptionExpiry; + if (expires < 0) + { + source.Reply(BAD_EXPIRY_TIME); + return; + } + else if (expires > 0) + expires += Anope::CurTime; + + int limit = -1; + try + { + limit = convertTo(limitstr); + } + catch (const ConvertException &) { } + + if (limit < 0 || limit > static_cast(Config->MaxSessionLimit)) + { + source.Reply(_("Invalid session limit. It must be a valid integer greater than or equal to zero and less than \002%d\002."), Config->MaxSessionLimit); + return; + } + else + { + if (mask.find('!') != Anope::string::npos || mask.find('@') != Anope::string::npos) + { + source.Reply(_("Invalid hostmask. Only real hostmasks are valid as sessionservice->GetExceptions() are not matched against nicks or usernames.")); + return; + } + + for (std::vector::iterator it = sessionservice->GetExceptions().begin(), it_end = sessionservice->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()); + } + } + + Exception *exception = new Exception(); + exception->mask = mask; + exception->limit = limit; + exception->reason = reason; + exception->time = Anope::CurTime; + exception->who = u->nick; + exception->expires = expires; + + EventReturn MOD_RESULT; + FOREACH_RESULT(I_OnExceptionAdd, OnExceptionAdd(exception)); + if (MOD_RESULT == EVENT_STOP) + delete exception; + else + { + sessionservice->AddException(exception); + source.Reply(_("Session limit for \002%s\002 set to \002%d\002."), mask.c_str(), limit); + if (readonly) + source.Reply(READ_ONLY_MODE); + } + } + + return; + } + + void DoDel(CommandSource &source, const std::vector ¶ms) + { + 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); + list.Process(); + } + else + { + unsigned i = 0, end = sessionservice->GetExceptions().size(); + for (; i < end; ++i) + if (mask.equals_ci(sessionservice->GetExceptions()[i]->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 (readonly) + source.Reply(READ_ONLY_MODE); + + return; + } + + void DoMove(CommandSource &source, const std::vector ¶ms) + { + const Anope::string &n1str = params.size() > 1 ? params[1] : ""; /* From position */ + const Anope::string &n2str = params.size() > 2 ? params[2] : ""; /* To position */ + int n1, n2; + + if (n2str.empty()) + { + this->OnSyntaxError(source, "MOVE"); + return; + } + + n1 = n2 = -1; + try + { + n1 = convertTo(n1str); + n2 = convertTo(n2str); + } + catch (const ConvertException &) { } + + if (n1 >= 0 && n1 < sessionservice->GetExceptions().size() && n2 >= 0 && n2 < sessionservice->GetExceptions().size() && n1 != n2) + { + Exception *temp = sessionservice->GetExceptions()[n1]; + sessionservice->GetExceptions()[n1] = sessionservice->GetExceptions()[n2]; + sessionservice->GetExceptions()[n2] = temp; + + source.Reply(_("Exception for \002%s\002 (#%d) moved to position \002%d\002."), sessionservice->GetExceptions()[n1]->mask.c_str(), n1 + 1, n2 + 1); + + if (readonly) + source.Reply(READ_ONLY_MODE); + } + else + this->OnSyntaxError(source, "MOVE"); + + return; + } + + void DoList(CommandSource &source, const std::vector ¶ms) + { + Anope::string mask = params.size() > 1 ? params[1] : ""; + + if (!mask.empty() && mask.find_first_not_of("1234567890,-") == Anope::string::npos) + { + ExceptionListCallback list(source, mask); + list.Process(); + } + else + { + bool SentHeader = false; + + for (unsigned i = 0, end = sessionservice->GetExceptions().size(); i < end; ++i) + if (mask.empty() || Anope::Match(sessionservice->GetExceptions()[i]->mask, mask)) + { + if (!SentHeader) + { + SentHeader = true; + source.Reply(_("Current Session Limit Exception list:")); + source.Reply(_("Num Limit Host")); + } + + ExceptionListCallback::DoList(source, i); + } + + if (!SentHeader) + source.Reply(_("No matching entries on session-limit exception list.")); + } + + return; + } + + void DoView(CommandSource &source, const std::vector ¶ms) + { + Anope::string mask = params.size() > 1 ? params[1] : ""; + + if (!mask.empty() && mask.find_first_not_of("1234567890,-") == Anope::string::npos) + { + ExceptionViewCallback list(source, mask); + list.Process(); + } + else + { + bool SentHeader = false; + + for (unsigned i = 0, end = sessionservice->GetExceptions().size(); i < end; ++i) + if (mask.empty() || Anope::Match(sessionservice->GetExceptions()[i]->mask, mask)) + { + if (!SentHeader) + { + SentHeader = true; + source.Reply(_("Current Session Limit Exception list:")); + } + + ExceptionViewCallback::DoList(source, i); + } + + if (!SentHeader) + source.Reply(_("No matching entries on session-limit exception list.")); + } + + return; + } + + 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 | \037list\037}")); + this->SetSyntax(_("MOVE \037num\037 \037position\037")); + this->SetSyntax(_("LIST [\037mask\037 | \037list\037]")); + this->SetSyntax(_("VIEW [\037mask\037 | \037list\037]")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + const Anope::string &cmd = params[0]; + + if (!Config->LimitSessions) + { + source.Reply(_("Session limiting is disabled.")); + 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("MOVE")) + return this->DoMove(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, ""); + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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, via a\n" + "/NOTICE from %s, of a source of help regarding session\n" + "limiting. The content of this notice is a config setting.\n"), + Config->OperServ.c_str()); + 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" + "\002EXCEPTION DEL\002 removes the given mask from the exception list.\n" + "\002EXCEPTION MOVE\002 moves exception \037num\037 to \037position\037. The\n" + "sessionservice->GetExceptions() inbetween will be shifted up or down to fill the gap.\n" + "\002EXCEPTION LIST\002 and \002EXCEPTION VIEW\002 show all current\n" + "sessionservice->GetExceptions(); if the optional mask is given, the list is limited\n" + "to those sessionservice->GetExceptions() 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 +{ + MySessionService ss; + ExpireTimer expiretimer; + CommandOSSession commandossession; + CommandOSException commandosexception; + service_reference akills; + + void AddSession(User *u, bool exempt) + { + Session *session = this->ss.FindSession(u->host); + + if (session) + { + bool kill = false; + if (Config->DefSessionLimit && session->count >= Config->DefSessionLimit) + { + 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 in do_kill or in do_quit - Adam + */ + ++session->count; + + if (kill && !exempt) + { + BotInfo *bi = findbot(Config->OperServ); + if (bi) + { + if (!Config->SessionLimitExceeded.empty()) + u->SendMessage(bi, Config->SessionLimitExceeded.c_str(), u->host.c_str()); + if (!Config->SessionLimitDetailsLoc.empty()) + u->SendMessage(bi, "%s", Config->SessionLimitDetailsLoc.c_str()); + } + + u->Kill(Config->OperServ, "Session limit exceeded"); + + ++session->hits; + if (Config->MaxSessionKill && session->hits >= Config->MaxSessionKill && akills) + { + const Anope::string &akillmask = "*@" + u->host; + const Anope::string &akillreason = "Session limit exceeded for " + u->host; + akills->Add(akillmask, Config->OperServ, Anope::CurTime + Config->SessionAutoKillExpiry, akillreason); + if (bi) + ircdproto->SendGlobops(bi, "Added a temporary AKILL for \2%s\2 due to excessive connections", akillmask.c_str()); + } + } + } + else + { + session = new Session(); + session->host = u->host; + session->count = 1; + session->hits = 0; + + this->ss.AddSession(session); + } + } + + void DelSession(User *u) + { + Session *session = this->ss.FindSession(u->host); + if (!session) + { + if (debug) + Log() << "session: Tried to delete non-existant session: " << u->host; + return; + } + + if (session->count > 1) + { + --session->count; + return; + } + + this->ss.DelSession(session); + delete session; + } + + public: + OSSession(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + ss(this), commandossession(this), commandosexception(this), akills("xlinemanager/sgline") + { + this->SetAuthor("Anope"); + + Implementation i[] = { I_OnUserConnect, I_OnUserLogoff }; + ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); + + ModuleManager::RegisterService(&commandossession); + ModuleManager::RegisterService(&commandosexception); + + ModuleManager::RegisterService(&this->ss); + } + + void OnUserConnect(dynamic_reference &user, bool &exempt) + { + if (user && Config->LimitSessions) + this->AddSession(user, exempt); + } + + void OnUserLogoff(User *u) + { + if (Config->LimitSessions && (!u->server || !u->server->IsULined())) + this->DelSession(u); + } +}; + +MODULE_INIT(OSSession) diff --git a/modules/commands/os_session.h b/modules/commands/os_session.h new file mode 100644 index 000000000..faab37934 --- /dev/null +++ b/modules/commands/os_session.h @@ -0,0 +1,32 @@ +#ifndef OS_SESSION_H +#define OS_SESSION_H + +class SessionService : public Service +{ + public: + typedef Anope::map SessionMap; + typedef std::vector ExceptionVector; + + SessionService(Module *m) : Service(m, "session") { } + + virtual void AddException(Exception *e) = 0; + + virtual void DelException(Exception *e) = 0; + + virtual Exception *FindException(User *u) = 0; + + virtual Exception *FindException(const Anope::string &host) = 0; + + virtual ExceptionVector &GetExceptions() = 0; + + virtual void AddSession(Session *s) = 0; + + virtual void DelSession(Session *s) = 0; + + virtual Session *FindSession(const Anope::string &mask) = 0; + + virtual SessionMap &GetSessions() = 0; +}; + +#endif + diff --git a/modules/commands/os_set.cpp b/modules/commands/os_set.cpp new file mode 100644 index 000000000..2a50f851c --- /dev/null +++ b/modules/commands/os_set.cpp @@ -0,0 +1,261 @@ +/* OperServ core functions + * + * (C) 2003-2011 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 CommandOSSet : public Command +{ + private: + void DoList(CommandSource &source) + { + Log(LOG_ADMIN, source.u, this); + + Anope::string index; + + index = readonly ? _("%s is enabled") : _("%s is disabled"); + source.Reply(index.c_str(), "READONLY"); + index = debug ? _("%s is enabled") : _("%s is disabled"); + source.Reply(index.c_str(), "DEBUG"); + index = noexpire ? _("%s is enabled") : _("%s is disabled"); + source.Reply(index.c_str(), "NOEXPIRE"); + + return; + } + + void DoSetReadOnly(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + const Anope::string &setting = params.size() > 1 ? params[1] : ""; + + if (setting.empty()) + { + this->OnSyntaxError(source, "READONLY"); + return; + } + + if (setting.equals_ci("ON")) + { + readonly = true; + Log(LOG_ADMIN, u, this) << "READONLY ON"; + source.Reply(_("Services are now in \002read-only\002 mode.")); + } + else if (setting.equals_ci("OFF")) + { + readonly = false; + Log(LOG_ADMIN, u, this) << "READONLY OFF"; + source.Reply(_("Services are now in \002read-write\002 mode.")); + } + else + source.Reply(_("Setting for READONLY must be \002on\002 or \002off\002.")); + + return; + } + + void DoSetSuperAdmin(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + const Anope::string &setting = params.size() > 1 ? params[1] : ""; + + if (setting.empty()) + { + this->OnSyntaxError(source, "SUPERADMIN"); + return; + } + + /** + * Allow the user to turn super admin on/off + * + * Rob + **/ + if (!Config->SuperAdmin) + source.Reply(_("SuperAdmin setting not enabled in services.conf")); + else if (setting.equals_ci("ON")) + { + u->isSuperAdmin = 1; + source.Reply(_("You are now a SuperAdmin")); + Log(LOG_ADMIN, u, this) << "SUPERADMIN ON"; + ircdproto->SendGlobops(source.owner, _("%s is now a Super-Admin"), u->nick.c_str()); + } + else if (setting.equals_ci("OFF")) + { + u->isSuperAdmin = 0; + source.Reply(_("You are no longer a SuperAdmin")); + Log(LOG_ADMIN, u, this) << "SUPERADMIN OFF"; + ircdproto->SendGlobops(source.owner, _("%s is no longer a Super-Admin"), u->nick.c_str()); + } + else + source.Reply(_("Setting for SuperAdmin must be \002on\002 or \002off\002 (must be enabled in services.conf)")); + + return; + } + + void DoSetDebug(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + const Anope::string &setting = params.size() > 1 ? params[1] : ""; + + if (setting.empty()) + { + this->OnSyntaxError(source, "DEBUG"); + return; + } + + if (setting.equals_ci("ON")) + { + debug = 1; + Log(LOG_ADMIN, u, this) << "DEBUG ON"; + source.Reply(_("Services are now in debug mode.")); + } + else if (setting.equals_ci("OFF") || (setting[0] == '0' && setting.is_number_only() && !convertTo(setting))) + { + Log(LOG_ADMIN, u, this) << "DEBUG OFF"; + debug = 0; + source.Reply(_("Services are now in non-debug mode.")); + } + else + { + try + { + debug = convertTo(setting); + Log(LOG_ADMIN, u, this) << "DEBUG " << debug; + source.Reply(_("Services are now in debug mode (level %d)."), debug); + return; + } + catch (const ConvertException &) { } + + source.Reply(_("Setting for DEBUG must be \002ON\002, \002OFF\002, or a positive number.")); + } + + return; + } + + void DoSetNoExpire(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + const Anope::string &setting = params.size() > 1 ? params[1] : ""; + + if (setting.empty()) + { + this->OnSyntaxError(source, "NOEXPIRE"); + return; + } + + if (setting.equals_ci("ON")) + { + noexpire = true; + Log(LOG_ADMIN, u, this) << "NOEXPIRE ON"; + source.Reply(_("Services are now in \002no expire\002 mode.")); + } + else if (setting.equals_ci("OFF")) + { + noexpire = false; + Log(LOG_ADMIN, u, this) << "NOEXPIRE OFF"; + source.Reply(_("Services are now in \002expire\002 mode.")); + } + else + source.Reply(_("Setting for NOEXPIRE must be \002on\002 or \002off\002.")); + + return; + } + public: + CommandOSSet(Module *creator) : Command(creator, "operserv/set", 1, 2) + { + this->SetDesc(_("Set various global Services options")); + this->SetSyntax(_("\037option\037 \037setting\037")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + const Anope::string &option = params[0]; + + if (option.equals_ci("LIST")) + return this->DoList(source); + else if (option.equals_ci("READONLY")) + return this->DoSetReadOnly(source, params); + else if (option.equals_ci("SUPERADMIN")) + return this->DoSetSuperAdmin(source, params); + else if (option.equals_ci("DEBUG")) + return this->DoSetDebug(source, params); + else if (option.equals_ci("NOEXPIRE")) + return this->DoSetNoExpire(source, params); + else + source.Reply(_("Unknown option \002%s\002."), option.c_str()); + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + if (subcommand.empty()) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Sets various global Services options. Option names\n" + "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" + " SUPERADMIN Activate or deactivate super-admin mode\n" + " LIST List the options")); + } + else if (subcommand.equals_ci("LIST")) + source.Reply(_("Syntax: \002LIST\n" + "Display the various %s settings"), Config->OperServ.c_str()); + 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 list and drop or forbid nicknames and\n" + "channels, but any such changes will not be saved unless\n" + "read-only mode is deactivated before Services is terminated\n" + "or restarted.\n" + " \n" + "This option is equivalent to the command-line option\n" + "\002-readonly\002.")); + else if (subcommand.equals_ci("NOEXPIRE")) + source.Reply(_("Syntax: \002NOEXPIRE {ON | OFF}\002\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" + "This option is equivalent to the command-line option\n" + "\002-noexpire\002.")); + else if (subcommand.equals_ci("SUPERADMIN")) + source.Reply(_("Syntax: \002SUPERADMIN {ON | OFF}\002\n" + "Setting this will grant you extra privileges such as the\n" + "ability to be \"founder\" on all channel's etc...\n" + "This option is \002not\002 persistent, and should only be used when\n" + "needed, and set back to OFF when no longer needed.")); + else + return false; + return true; + } +}; + +class OSSet : public Module +{ + CommandOSSet commandosset; + + public: + OSSet(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandosset(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandosset); + } +}; + +MODULE_INIT(OSSet) diff --git a/modules/commands/os_shutdown.cpp b/modules/commands/os_shutdown.cpp new file mode 100644 index 000000000..bc3a19353 --- /dev/null +++ b/modules/commands/os_shutdown.cpp @@ -0,0 +1,117 @@ +/* OperServ core functions + * + * (C) 2003-2011 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 CommandOSQuit : public Command +{ + public: + CommandOSQuit(Module *creator) : Command(creator, "operserv/quit", 0, 0) + { + this->SetDesc(_("Terminate Services WITHOUT saving")); + this->SetSyntax(""); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + quitmsg = "QUIT command received from " + u->nick; + quitting = true; + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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.")); + return true; + } +}; + +class CommandOSRestart : public Command +{ + public: + CommandOSRestart(Module *creator) : Command(creator, "operserv/restart", 0, 0) + { + this->SetDesc(_("Save databases and restart Services")); + this->SetSyntax(""); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + quitmsg = "RESTART command received from " + u->nick; + save_databases(); + quitting = restarting = true; + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(_("Causes Services to save all databases and then restart\n" + "(i.e. exit and immediately re-run the executable).")); + return true; + } +}; + +class CommandOSShutdown : public Command +{ + public: + CommandOSShutdown(Module *creator) : Command(creator, "operserv/shutdown", 0, 0) + { + this->SetDesc(_("Terminate services with save")); + this->SetSyntax(""); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + quitmsg = source.command + " command received from " + u->nick; + save_databases(); + quitting = true; + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Causes Services to save all databases and then shut down.")); + return true; + } +}; + +class OSShutdown : public Module +{ + CommandOSQuit commandosquit; + CommandOSRestart commandosrestart; + CommandOSShutdown commandosshutdown; + + public: + OSShutdown(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandosquit(this), commandosrestart(this), commandosshutdown(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandosquit); + ModuleManager::RegisterService(&commandosrestart); + ModuleManager::RegisterService(&commandosshutdown); + } +}; + +MODULE_INIT(OSShutdown) diff --git a/modules/commands/os_stats.cpp b/modules/commands/os_stats.cpp new file mode 100644 index 000000000..2c20395d0 --- /dev/null +++ b/modules/commands/os_stats.cpp @@ -0,0 +1,224 @@ +/* OperServ core functions + * + * (C) 2003-2011 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" + +/** + * 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 +{ + service_reference akills, snlines, sqlines, szlines; + private: + void DoStatsAkill(CommandSource &source) + { + int timeout; + if (akills) + { + /* AKILLs */ + source.Reply(_("Current number of AKILLs: \002%d\002"), akills->GetCount()); + timeout = Config->AutokillExpiry + 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 (ircd->snline && snlines) + { + /* SNLINEs */ + source.Reply(_("Current number of SNLINEs: \002%d\002"), snlines->GetCount()); + timeout = Config->SNLineExpiry + 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 (ircd->sqline && sqlines) + { + /* SQLINEs */ + source.Reply(_("Current number of SQLINEs: \002%d\002"), sqlines->GetCount()); + timeout = Config->SQLineExpiry + 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")); + } + if (ircd->szline && szlines) + { + /* SZLINEs */ + source.Reply(_("Current number of SZLINEs: \002%d\002"), szlines->GetCount()); + timeout = Config->SZLineExpiry + 59; + if (timeout >= 172800) + source.Reply(_("Default SZLINE expiry time: \002%d days\002"), timeout / 86400); + else if (timeout >= 86400) + source.Reply(_("Default SZLINE expiry time: \0021 day\002")); + else if (timeout >= 7200) + source.Reply(_("Default SZLINE expiry time: \002%d hours\002"), timeout / 3600); + else if (timeout >= 3600) + source.Reply(_("Default SZLINE expiry time: \0021 hour\002")); + else if (timeout >= 120) + source.Reply(_("Default SZLINE expiry time: \002%d minutes\002"), timeout / 60); + else if (timeout >= 60) + source.Reply(_("Default SZLINE expiry time: \0021 minute\002")); + else + source.Reply(_("Default SZLINE expiry time: \002No expiration\002")); + } + return; + } + + void DoStatsReset(CommandSource &source) + { + maxusercnt = usercnt; + source.Reply(_("Statistics reset.")); + return; + } + + void DoStatsUptime(CommandSource &source) + { + time_t uptime = Anope::CurTime - start_time; + source.Reply(_("Current users: \002%d\002 (\002%d\002 ops)"), usercnt, opcnt); + source.Reply(_("Maximum users: \002%d\002 (%s)"), maxusercnt, do_strftime(maxusertime).c_str()); + source.Reply(_("Services up %s"), duration(uptime).c_str()); + + return; + } + + void DoStatsUplink(CommandSource &source) + { + Anope::string buf; + + for (unsigned j = 0; !Capab_Info[j].Token.empty(); ++j) + if (Capab.HasFlag(Capab_Info[j].Flag)) + buf += " " + Capab_Info[j].Token; + + 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; + } + + public: + CommandOSStats(Module *creator) : Command(creator, "operserv/stats", 0, 1), + akills("xlinemanager/sgline"), snlines("xlinemanager/snline"), sqlines("xlinemanager/sqline"), szlines("xlinemanager/szline") + { + this->SetDesc(_("Show status of Services and network")); + this->SetSyntax(_("[AKILL | ALL | RESET | UPLINK]")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + Anope::string extra = !params.empty() ? params[0] : ""; + + if (extra.equals_ci("RESET")) + return this->DoStatsReset(source); + + if (extra.equals_ci("ALL") || extra.equals_ci("AKILL")) + this->DoStatsAkill(source); + + if (extra.empty() || extra.equals_ci("ALL") || extra.equals_ci("UPTIME")) + this->DoStatsUptime(source); + + if (extra.equals_ci("ALL") || extra.equals_ci("UPLINK")) + this->DoStatsUplink(source); + + if (!extra.empty() && !extra.equals_ci("ALL") && !extra.equals_ci("AKILL") && !extra.equals_ci("UPLINK")) + source.Reply(_("Unknown STATS option \002%s\002."), extra.c_str()); + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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 \002ALL\002 displays the user and uptime statistics, and\n" + "everything you'd see with the \002UPLINK\002 option.")); + return true; + } +}; + +class OSStats : public Module +{ + CommandOSStats commandosstats; + + public: + OSStats(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandosstats(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandosstats); + } +}; + +MODULE_INIT(OSStats) diff --git a/modules/commands/os_svsnick.cpp b/modules/commands/os_svsnick.cpp new file mode 100644 index 000000000..dc7c77a6c --- /dev/null +++ b/modules/commands/os_svsnick.cpp @@ -0,0 +1,92 @@ +/* OperServ core functions + * + * (C) 2003-2011 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 CommandOSSVSNick : public Command +{ + public: + CommandOSSVSNick(Module *creator) : Command(creator, "operserv/svsnick", 2, 2) + { + this->SetDesc(_("Forcefully change a user's nickname")); + this->SetSyntax(_("\037nick\037 \037newnick\037")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + const Anope::string &nick = params[0]; + Anope::string newnick = params[1]; + User *u2; + + /* Truncate long nicknames to Config->NickLen characters */ + if (newnick.length() > Config->NickLen) + { + source.Reply(_("Nick \002%s\002 was truncated to %d characters."), newnick.c_str(), Config->NickLen, newnick.c_str()); + newnick = params[1].substr(0, Config->NickLen); + } + + /* Check for valid characters */ + if (newnick[0] == '-' || isdigit(newnick[0])) + { + source.Reply(_("Nick \002%s\002 is an illegal nickname and cannot be used."), newnick.c_str()); + return; + } + for (unsigned i = 0, end = newnick.length(); i < end; ++i) + if (!isvalidnick(newnick[i])) + { + source.Reply(_("Nick \002%s\002 is an illegal nickname and cannot be used."), newnick.c_str()); + return; + } + + /* Check for a nick in use or a forbidden/suspended nick */ + if (!(u2 = finduser(nick))) + source.Reply(NICK_X_NOT_IN_USE, nick.c_str()); + else if (finduser(newnick)) + source.Reply(_("Nick \002%s\002 is currently in use."), newnick.c_str()); + else + { + source.Reply(_("The nick \002%s\002 is now being changed to \002%s\002."), nick.c_str(), newnick.c_str()); + ircdproto->SendGlobops(source.owner, "%s used SVSNICK to change %s to %s", u->nick.c_str(), nick.c_str(), newnick.c_str()); + ircdproto->SendForceNickChange(u2, newnick, Anope::CurTime); + } + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Forcefully changes a user's nickname from nick to newnick.")); + return true; + } +}; + +class OSSVSNick : public Module +{ + CommandOSSVSNick commandossvsnick; + + public: + OSSVSNick(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandossvsnick(this) + { + this->SetAuthor("Anope"); + + if (!ircd || !ircd->svsnick) + throw ModuleException("Your IRCd does not support SVSNICK"); + + ModuleManager::RegisterService(&commandossvsnick); + } +}; + +MODULE_INIT(OSSVSNick) diff --git a/modules/commands/os_sxline.cpp b/modules/commands/os_sxline.cpp new file mode 100644 index 000000000..f949cb7d2 --- /dev/null +++ b/modules/commands/os_sxline.cpp @@ -0,0 +1,807 @@ +/* OperServ core functions + * + * (C) 2003-2011 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."), this->command->name.c_str()); + else if (Deleted == 1) + source.Reply(_("Deleted 1 entry from the %s list."), this->command->name.c_str()); + else + source.Reply(_("Deleted %d entries from the %s list."), Deleted, this->command->name.c_str()); + } + + void HandleNumber(unsigned Number) + { + if (!Number) + return; + + XLine *x = this->xlm->GetEntry(Number - 1); + + if (!x) + return; + + ++Deleted; + DoDel(this->xlm, source, x); + } + + static void DoDel(XLineManager *xlm, CommandSource &source, XLine *x) + { + xlm->DelXLine(x); + } +}; + +class SXLineListCallback : public NumberList +{ + protected: + XLineManager *xlm; + Command *command; + CommandSource &source; + bool SentHeader; + public: + SXLineListCallback(XLineManager *x, Command *c, CommandSource &_source, const Anope::string &numlist) : NumberList(numlist, false), xlm(x), command(c), source(_source), SentHeader(false) + { + } + + ~SXLineListCallback() + { + if (!SentHeader) + source.Reply(_("No matching entries on the %s list."), this->command->name.c_str()); + } + + virtual void HandleNumber(unsigned Number) + { + if (!Number) + return; + + XLine *x = this->xlm->GetEntry(Number - 1); + + if (!x) + return; + + if (!SentHeader) + { + SentHeader = true; + source.Reply(_("Current %s list:\n Num Mask Reason"), this->command->name.c_str()); + } + + DoList(source, x, Number - 1); + } + + static void DoList(CommandSource &source, XLine *x, unsigned Number) + { + source.Reply(OPER_LIST_FORMAT, Number + 1, x->Mask.c_str(), x->Reason.c_str()); + } +}; + +class SXLineViewCallback : public SXLineListCallback +{ + public: + SXLineViewCallback(XLineManager *x, Command *c, CommandSource &_source, const Anope::string &numlist) : SXLineListCallback(x, c, _source, numlist) + { + } + + void HandleNumber(unsigned Number) + { + if (!Number) + return; + + XLine *x = this->xlm->GetEntry(Number - 1); + + if (!x) + return; + + if (!SentHeader) + { + SentHeader = true; + source.Reply(_("Current %s list:"), this->command->name.c_str()); + } + + DoList(source, x, Number - 1); + } + + static void DoList(CommandSource &source, XLine *x, unsigned Number) + { + Anope::string expirebuf = expire_left(source.u->Account(), x->Expires); + source.Reply(OPER_VIEW_FORMAT, Number + 1, x->Mask.c_str(), x->By.c_str(), do_strftime(x->Created).c_str(), expirebuf.c_str(), x->Reason.c_str()); + } +}; + +class CommandOSSXLineBase : public Command +{ + private: + virtual XLineManager* xlm() = 0; + + virtual void OnAdd(CommandSource &source, const std::vector ¶ms) = 0; + + void OnDel(CommandSource &source, const std::vector ¶ms) + { + User *u = source.u; + + if (!this->xlm() || this->xlm()->GetList().empty()) + { + source.Reply(_("%s list is empty."), this->name.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(), this->name.c_str()); + return; + } + + FOREACH_MOD(I_OnDelXLine, OnDelXLine(u, x, this->xlm())); + + SXLineDelCallback::DoDel(this->xlm(), source, x); + source.Reply(_("\002%s\002 deleted from the %s list."), mask.c_str(), this->name.c_str()); + } + + if (readonly) + source.Reply(READ_ONLY_MODE); + + return; + } + + void OnList(CommandSource &source, const std::vector ¶ms) + { + if (!this->xlm() || this->xlm()->GetList().empty()) + { + source.Reply(_("%s list is empty."), this->name.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) + { + SXLineListCallback list(this->xlm(), this, source, mask); + list.Process(); + } + else + { + bool SentHeader = false; + + for (unsigned i = 0, end = this->xlm()->GetCount(); i < end; ++i) + { + XLine *x = this->xlm()->GetEntry(i); + + if (mask.empty() || mask.equals_ci(x->Mask) || Anope::Match(x->Mask, mask)) + { + if (!SentHeader) + { + SentHeader = true; + source.Reply(_("Current %s list:\n Num Mask Reason"), this->name.c_str()); + } + + SXLineListCallback::DoList(source, x, i); + } + } + + if (!SentHeader) + source.Reply(_("No matching entries on the %s list."), this->name.c_str()); + else + source.Reply(END_OF_ANY_LIST, this->name.c_str()); + } + + return; + } + + void OnView(CommandSource &source, const std::vector ¶ms) + { + if (!this->xlm () || this->xlm()->GetList().empty()) + { + source.Reply(_("%s list is empty."), this->name.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) + { + SXLineViewCallback list(this->xlm(), this, source, mask); + list.Process(); + } + else + { + bool SentHeader = false; + + for (unsigned i = 0, end = this->xlm()->GetCount(); i < end; ++i) + { + XLine *x = this->xlm()->GetEntry(i); + + if (mask.empty() || mask.equals_ci(x->Mask) || Anope::Match(x->Mask, mask)) + { + if (!SentHeader) + { + SentHeader = true; + source.Reply(_("Current %s list:"), this->name.c_str()); + } + + SXLineViewCallback::DoList(source, x, i); + } + } + + if (!SentHeader) + source.Reply(_("No matching entries on the %s list."), this->name.c_str()); + } + + return; + } + + void OnClear(CommandSource &source) + { + User *u = source.u; + FOREACH_MOD(I_OnDelXLine, OnDelXLine(u, NULL, this->xlm())); + this->xlm()->Clear(); + source.Reply(_("The %s list has been cleared."), this->name.c_str()); + + return; + } + public: + CommandOSSXLineBase(Module *creator, const Anope::string &cmd) : Command(creator, cmd, 1, 3) + { + this->SetDesc(Anope::printf(_("Manipulate the %s list"), cmd.c_str())); + this->SetSyntax(_("ADD [+\037expiry\037] \037mask\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]")); + this->SetSyntax(_("CLEAR")); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + 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 ¶ms) + { + if (!this->xlm()) + return; + + User *u = source.u; + unsigned last_param = 2; + Anope::string param, expiry; + time_t expires; + + param = params.size() > 1 ? params[1] : ""; + if (!param.empty() && param[0] == '+') + { + expiry = param; + param = params.size() > 2 ? params[2] : ""; + last_param = 3; + } + + expires = !expiry.empty() ? dotime(expiry) : Config->SNLineExpiry; + /* 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()) + { + std::pair canAdd = this->xlm()->CanAdd(mask, expires); + if (mask.find_first_not_of("*?") == Anope::string::npos) + source.Reply(USERHOST_MASK_TOO_WIDE, mask.c_str()); + else if (canAdd.first == 1) + source.Reply(_("\002%s\002 already exists on the %s list."), canAdd.second->Mask.c_str(), this->name.c_str()); + else if (canAdd.first == 2) + source.Reply(_("Expiry time of \002%s\002 changed."), canAdd.second->Mask.c_str()); + else if (canAdd.first == 3) + source.Reply(_("\002%s\002 is already covered by %s."), mask.c_str(), canAdd.second->Mask.c_str()); + else + { + /* 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); + unsigned int affected = 0; + for (Anope::insensitive_map::iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it) + if (Anope::Match(it->second->realname, mask)) + ++affected; + float percent = static_cast(affected) / static_cast(UserListByNick.size()) * 100.0; + + if (percent > 95) + { + source.Reply(USERHOST_MASK_TOO_WIDE, mask.c_str()); + Log(LOG_ADMIN, u, this) << "tried to " << this->name << " " << percent << "% of the network (" << affected << " users)"; + return; + } + + XLine *x = this->xlm()->Add(mask, u->nick, expires, reason); + + EventReturn MOD_RESULT; + FOREACH_RESULT(I_OnAddXLine, OnAddXLine(u, x, this->xlm())); + if (MOD_RESULT == EVENT_STOP) + { + delete x; + return; + } + + source.Reply(_("\002%s\002 added to the %s list."), mask.c_str(), this->name.c_str()); + Log(LOG_ADMIN, u, this) << "on " << mask << " (" << reason << ") expires in " << (expires ? duration(expires - Anope::CurTime) : "never") << " [affects " << affected << " user(s) (" << percent << "%)]"; + + if (readonly) + source.Reply(READ_ONLY_MODE); + } + + } + else + this->OnSyntaxError(source, "ADD"); + + return; + } + + service_reference snlines; + public: + CommandOSSNLine(Module *creator) : CommandOSSXLineBase(creator, "operserv/snline"), snlines("xlinemanager/snline") + { + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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.\n")); + 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" + "Note: because the realname mask may contain spaces, the\n" + "separator between it and the reason is a colon.\n")); + 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 ¶ms) + { + if (!this->xlm()) + return; + + User *u = source.u; + unsigned last_param = 2; + Anope::string expiry, mask; + time_t expires; + + mask = params.size() > 1 ? params[1] : ""; + if (!mask.empty() && mask[0] == '+') + { + expiry = mask; + mask = params.size() > 2 ? params[2] : ""; + last_param = 3; + } + + expires = !expiry.empty() ? dotime(expiry) : Config->SQLineExpiry; + /* 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()) + { + std::pair canAdd = this->sqlines->CanAdd(mask, expires); + if (mask.find_first_not_of("*") == Anope::string::npos) + source.Reply(USERHOST_MASK_TOO_WIDE, mask.c_str()); + else if (canAdd.first == 1) + source.Reply(_("\002%s\002 already exists on the SQLINE list."), canAdd.second->Mask.c_str()); + else if (canAdd.first == 2) + source.Reply(_("Expiry time of \002%s\002 changed."), canAdd.second->Mask.c_str()); + else if (canAdd.first == 3) + source.Reply(_("\002%s\002 is already covered by %s."), mask.c_str(), canAdd.second->Mask.c_str()); + else + { + unsigned int affected = 0; + for (Anope::insensitive_map::iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it) + if (Anope::Match(it->second->nick, mask)) + ++affected; + float percent = static_cast(affected) / static_cast(UserListByNick.size()) * 100.0; + + if (percent > 95) + { + source.Reply(USERHOST_MASK_TOO_WIDE, mask.c_str()); + Log(LOG_ADMIN, u, this) << "tried to SQLine " << percent << "% of the network (" << affected << " users)"; + return; + } + + XLine *x = this->sqlines->Add(mask, u->nick, expires, reason); + + EventReturn MOD_RESULT; + FOREACH_RESULT(I_OnAddXLine, OnAddXLine(u, x, this->xlm())); + if (MOD_RESULT == EVENT_STOP) + { + delete x; + return; + } + + source.Reply(_("\002%s\002 added to the SQLINE list."), mask.c_str()); + Log(LOG_ADMIN, u, this) << "on " << mask << " (" << reason << ") expires in " << (expires ? duration(expires - Anope::CurTime) : "never") << " [affects " << affected << " user(s) (" << percent << "%)]"; + + if (readonly) + source.Reply(READ_ONLY_MODE); + } + + } + else + this->OnSyntaxError(source, "ADD"); + + return; + } + + service_reference sqlines; + public: + CommandOSSQLine(Module *creator) : CommandOSSXLineBase(creator, "operserv/sqline"), sqlines("xlinemanager/sqline") + { + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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 (on IRCds that \n" + "support it).\n")); + 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.\n")); + 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 CommandOSSZLine : public CommandOSSXLineBase +{ + XLineManager *xlm() + { + return this->szlines; + } + + void OnAdd(CommandSource &source, const std::vector ¶ms) + { + if (!this->xlm()) + return; + + User *u = source.u; + unsigned last_param = 2; + Anope::string expiry, mask; + time_t expires; + + mask = params.size() > 1 ? params[1] : ""; + if (!mask.empty() && mask[0] == '+') + { + expiry = mask; + mask = params.size() > 2 ? params[2] : ""; + last_param = 3; + } + + expires = !expiry.empty() ? dotime(expiry) : Config->SZLineExpiry; + /* 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()) + { + std::pair canAdd = this->szlines->CanAdd(mask, expires); + if (mask.find('!') != Anope::string::npos || mask.find('@') != Anope::string::npos) + source.Reply(_("You can only add IP masks to the SZLINE list.")); + else if (mask.find_first_not_of("*?") == Anope::string::npos) + source.Reply(USERHOST_MASK_TOO_WIDE, mask.c_str()); + else if (canAdd.first == 1) + source.Reply(_("\002%s\002 already exists on the SZLINE list."), canAdd.second->Mask.c_str()); + else if (canAdd.first == 2) + source.Reply(_("Expiry time of \002%s\002 changed."), canAdd.second->Mask.c_str()); + else if (canAdd.first == 3) + source.Reply(_("\002%s\002 is already covered by %s."), mask.c_str(), canAdd.second->Mask.c_str()); + else + { + User *user = finduser(mask); + if (user && user->ip()) + mask = user->ip.addr(); + unsigned int affected = 0; + for (Anope::insensitive_map::iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it) + if (it->second->ip() && Anope::Match(it->second->ip.addr(), mask)) + ++affected; + float percent = static_cast(affected) / static_cast(UserListByNick.size()) * 100.0; + + if (percent > 95) + { + source.Reply(USERHOST_MASK_TOO_WIDE, mask.c_str()); + Log(LOG_ADMIN, u, this) << "tried to SZLine " << percent << "% of the network (" << affected << " users)"; + return; + } + + XLine *x = this->szlines->Add(mask, u->nick, expires, reason); + + EventReturn MOD_RESULT; + FOREACH_RESULT(I_OnAddXLine, OnAddXLine(u, x, this->xlm())); + if (MOD_RESULT == EVENT_STOP) + { + delete x; + return; + } + + source.Reply(_("\002%s\002 added to the SZLINE list."), mask.c_str()); + Log(LOG_ADMIN, u, this) << "on " << mask << " (" << reason << ") expires in " << (expires ? duration(expires - Anope::CurTime) : "never") << " [affects " << affected << " user(s) (" << percent << "%)]"; + + if (readonly) + source.Reply(READ_ONLY_MODE); + } + + } + else + this->OnSyntaxError(source, "ADD"); + + return; + } + + service_reference szlines; + public: + CommandOSSZLine(Module *creator) : CommandOSSXLineBase(creator, "operserv/szline"), szlines("xlinemanager/szline") + { + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Allows Services operators to manipulate the SZLINE list. If\n" + "a user with an IP matching an SZLINE mask attempts to \n" + "connect, Services will not allow it to pursue his IRC\n" + "session (and this, whether the IP has a PTR RR or not).\n" + " \n")); + source.Reply(_("\002SZLINE ADD\002 adds the given (nick's) IP mask to the SZLINE\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 SZLINE 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 SZLINE default expiry time can be found with the\n" + "\002STATS AKILL\002 command.\n")); + source.Reply(_(" \n" + "The \002SZLINE DEL\002 command removes the given mask from the\n" + "SZLINE 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 \002SZLINE LIST\002 command displays the SZLINE 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" + " \002SZLINE LIST 2-5,7-9\002\n" + " Lists SZLINE entries numbered 2 through 5 and 7 \n" + " through 9.\n" + " \n" + "\002SZLINE VIEW\002 is a more verbose version of \002SZLINE LIST\002, and \n" + "will show who added an SZLINE, the date it was added, and when\n" + "it expires, as well as the IP mask and reason.\n" + " \n" + "\002SZLINE CLEAR\002 clears all entries of the SZLINE list.")); + return true; + } +}; + +class OSSXLine : public Module +{ + CommandOSSNLine commandossnline; + CommandOSSQLine commandossqline; + CommandOSSZLine commandosszline; + + public: + OSSXLine(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + commandossnline(this), commandossqline(this), commandosszline(this) + { + this->SetAuthor("Anope"); + + if (ircd && ircd->snline) + ModuleManager::RegisterService(&commandossnline); + if (ircd && ircd->sqline) + ModuleManager::RegisterService(&commandossqline); + if (ircd && ircd->szline) + ModuleManager::RegisterService(&commandosszline); + } +}; + +MODULE_INIT(OSSXLine) diff --git a/modules/commands/os_update.cpp b/modules/commands/os_update.cpp new file mode 100644 index 000000000..776835cf7 --- /dev/null +++ b/modules/commands/os_update.cpp @@ -0,0 +1,56 @@ +/* OperServ core functions + * + * (C) 2003-2011 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")); + this->SetSyntax(""); + } + + void Execute(CommandSource &source, const std::vector ¶ms) + { + source.Reply(_("Updating databases.")); + save_databases(); + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + 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, CORE), + commandosupdate(this) + { + this->SetAuthor("Anope"); + + ModuleManager::RegisterService(&commandosupdate); + } +}; + +MODULE_INIT(OSUpdate) diff --git a/modules/core/botserv.h b/modules/core/botserv.h deleted file mode 100644 index 5fa03f870..000000000 --- a/modules/core/botserv.h +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef BOTSERV_H -#define BOTSERV_H - -struct UserData -{ - UserData() - { - this->Clear(); - } - - void Clear() - { - last_use = last_start = Anope::CurTime; - lines = times = 0; - lastline.clear(); - } - - /* Data validity */ - time_t last_use; - - /* for flood kicker */ - int16 lines; - time_t last_start; - - /* for repeat kicker */ - Anope::string lastline; - Anope::string lasttarget; - int16 times; -}; - -struct BanData -{ - Anope::string mask; - time_t last_use; - int16 ttb[TTB_SIZE]; - - BanData() - { - this->Clear(); - } - - void Clear() - { - last_use = 0; - for (int i = 0; i < TTB_SIZE; ++i) - this->ttb[i] = 0; - } -}; - -class BotServService : public Service -{ - public: - BotServService(Module *m) : Service(m, "BotServ") { } - - virtual UserData *GetUserData(User *u, Channel *c) = 0; - - virtual BanData *GetBanData(User *u, Channel *c) = 0; -}; - -static service_reference botserv("BotServ"); - -#endif // BOTSERV_H - diff --git a/modules/core/bs_assign.cpp b/modules/core/bs_assign.cpp deleted file mode 100644 index 803368994..000000000 --- a/modules/core/bs_assign.cpp +++ /dev/null @@ -1,168 +0,0 @@ -/* BotServ core functions - * - * (C) 2003-2011 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 ¶ms) - { - const Anope::string &chan = params[0]; - const Anope::string &nick = params[1]; - - User *u = source.u; - - if (readonly) - { - source.Reply(BOT_ASSIGN_READONLY); - return; - } - - ChannelInfo *ci = cs_findchan(params[0]); - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - BotInfo *bi = findbot(nick); - if (!bi) - { - source.Reply(BOT_DOES_NOT_EXIST, nick.c_str()); - return; - } - - if (ci->botflags.HasFlag(BS_NOBOT) || (!ci->HasPriv(u, CA_ASSIGN) && !u->HasPriv("botserv/administration"))) - { - source.Reply(ACCESS_DENIED); - return; - } - - if (bi->HasFlag(BI_PRIVATE) && !u->HasCommand("botserv/botserv/assign/private")) - { - 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 = !ci->HasPriv(u, CA_ASSIGN); - Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "for " << bi->nick; - - bi->Assign(u, 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) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Assigns a bot pointed out by nick to the channel chan. 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 \037nick\037")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - - if (readonly) - { - source.Reply(BOT_ASSIGN_READONLY); - return; - } - - ChannelInfo *ci = cs_findchan(params[0]); - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - if (!u->HasPriv("botserv/administration") && !ci->HasPriv(u, CA_ASSIGN)) - { - source.Reply(ACCESS_DENIED); - return; - } - - if (!ci->bi) - { - source.Reply(BOT_NOT_ASSIGNED); - return; - } - - if (ci->HasFlag(CI_PERSIST) && !ModeManager::FindChannelModeByName(CMODE_PERM)) - { - source.Reply(_("You can not unassign bots while persist is set on the channel.")); - return; - } - - bool override = !ci->HasPriv(u, CA_ASSIGN); - Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "for " << ci->bi->nick; - - ci->bi->UnAssign(u, ci); - source.Reply(_("There is no bot assigned to %s anymore."), ci->name.c_str()); - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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 have to reconfigure\n" - "it entirely.")); - return true; - } -}; - -class BSAssign : public Module -{ - CommandBSAssign commandbsassign; - CommandBSUnassign commandbsunassign; - - public: - BSAssign(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandbsassign(this), commandbsunassign(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandbsassign); - ModuleManager::RegisterService(&commandbsunassign); - } -}; - -MODULE_INIT(BSAssign) diff --git a/modules/core/bs_badwords.cpp b/modules/core/bs_badwords.cpp deleted file mode 100644 index a27dca2d4..000000000 --- a/modules/core/bs_badwords.cpp +++ /dev/null @@ -1,331 +0,0 @@ -/* BotServ core functions - * - * (C) 2003-2011 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 BadwordsListCallback : public NumberList -{ - CommandSource &source; - ChannelInfo *ci; - bool SentHeader; - public: - BadwordsListCallback(CommandSource &_source, ChannelInfo *_ci, const Anope::string &list) : NumberList(list, false), source(_source), ci(_ci), SentHeader(false) - { - } - - ~BadwordsListCallback() - { - if (!SentHeader) - source.Reply(_("No matching entries on %s bad words list."), ci->name.c_str()); - } - - void HandleNumber(unsigned Number) - { - if (!Number || Number > ci->GetBadWordCount()) - return; - - if (!SentHeader) - { - SentHeader = true; - source.Reply(_("Bad words list for %s:\n" - " Num Word Type"), ci->name.c_str()); - } - - DoList(source, Number - 1, ci->GetBadWord(Number - 1)); - } - - static void DoList(CommandSource &source, unsigned Number, BadWord *bw) - { - source.Reply(_(" %3d %-30s %s"), Number + 1, bw->word.c_str(), bw->type == BW_SINGLE ? "(SINGLE)" : (bw->type == BW_START ? "(START)" : (bw->type == BW_END ? "(END)" : ""))); - } -}; - -class BadwordsDelCallback : public NumberList -{ - CommandSource &source; - ChannelInfo *ci; - 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 (!ci->HasPriv(source.u, CA_BADWORDS) && source.u->HasPriv("botserv/administration")) - this->override = true; - } - - ~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) - { - if (!Number || Number > ci->GetBadWordCount()) - return; - - Log(override ? LOG_OVERRIDE : LOG_COMMAND, source.u, c, ci) << "DEL " << ci->GetBadWord(Number - 1)->word; - ++Deleted; - ci->EraseBadWord(Number - 1); - } -}; - -class CommandBSBadwords : public Command -{ - private: - void DoList(CommandSource &source, ChannelInfo *ci, const Anope::string &word) - { - bool override = !ci->HasPriv(source.u, CA_BADWORDS); - Log(override ? LOG_OVERRIDE : LOG_COMMAND, source.u, this, ci) << "LIST"; - - if (!ci->GetBadWordCount()) - source.Reply(_("%s bad words list is empty."), ci->name.c_str()); - else if (!word.empty() && word.find_first_not_of("1234567890,-") == Anope::string::npos) - { - BadwordsListCallback list(source, ci, word); - list.Process(); - } - else - { - bool SentHeader = false; - - for (unsigned i = 0, end = ci->GetBadWordCount(); i < end; ++i) - { - BadWord *bw = ci->GetBadWord(i); - - if (!word.empty() && !Anope::Match(bw->word, word)) - continue; - - if (!SentHeader) - { - SentHeader = true; - source.Reply(_("Bad words list for %s:\n" - " Num Word Type"), ci->name.c_str()); - - } - - BadwordsListCallback::DoList(source, i, bw); - } - - if (!SentHeader) - source.Reply(_("No matching entries on %s bad words list."), ci->name.c_str()); - } - - return; - } - - void DoAdd(CommandSource &source, ChannelInfo *ci, const Anope::string &word) - { - size_t pos = word.rfind(' '); - BadWordType type = 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")) - type = BW_SINGLE; - else if (opt.equals_ci("START")) - type = BW_START; - else if (opt.equals_ci("END")) - type = BW_END; - } - realword = word.substr(0, pos); - } - - if (ci->GetBadWordCount() >= Config->BSBadWordsMax) - { - source.Reply(_("Sorry, you can only have %d bad words entries on a channel."), Config->BSBadWordsMax); - return; - } - - for (unsigned i = 0, end = ci->GetBadWordCount(); i < end; ++i) - { - BadWord *bw = ci->GetBadWord(i); - - if (!bw->word.empty() && ((Config->BSCaseSensitive && realword.equals_cs(bw->word)) || (!Config->BSCaseSensitive && 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 = !ci->HasPriv(source.u, CA_BADWORDS); - Log(override ? LOG_OVERRIDE : LOG_COMMAND, source.u, this, ci) << "ADD " << realword; - ci->AddBadWord(realword, type); - - source.Reply(_("\002%s\002 added to %s bad words list."), realword.c_str(), ci->name.c_str()); - - return; - } - - void DoDelete(CommandSource &source, ChannelInfo *ci, const Anope::string &word) - { - /* 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; - BadWord *badword; - - for (i = 0, end = ci->GetBadWordCount(); i < end; ++i) - { - badword = ci->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 = !ci->HasPriv(source.u, CA_BADWORDS); - Log(override ? LOG_OVERRIDE : LOG_COMMAND, source.u, this, ci) << "DEL " << badword->word; - - source.Reply(_("\002%s\002 deleted from %s bad words list."), badword->word.c_str(), ci->name.c_str()); - - ci->EraseBadWord(i); - } - - return; - } - - void DoClear(CommandSource &source, ChannelInfo *ci) - { - bool override = !ci->HasPriv(source.u, CA_BADWORDS); - Log(override ? LOG_OVERRIDE : LOG_COMMAND, source.u, this, ci) << "CLEAR"; - - ci->ClearBadWords(); - source.Reply(_("Bad words list is now empty.")); - return; - } - public: - CommandBSBadwords(Module *creator) : Command(creator, "botserv/badwords", 2, 3) - { - this->SetDesc(_("Maintains 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 ¶ms) - { - const Anope::string &cmd = params[1]; - const Anope::string &word = params.size() > 2 ? params[2] : ""; - User *u = source.u; - bool need_args = cmd.equals_ci("LIST") || cmd.equals_ci("CLEAR"); - - if (!need_args && word.empty()) - { - this->OnSyntaxError(source, cmd); - return; - } - - ChannelInfo *ci = cs_findchan(params[0]); - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - - if (!ci->HasPriv(u, CA_BADWORDS) && (!need_args || !u->HasPriv("botserv/administration"))) - { - source.Reply(ACCESS_DENIED); - return; - } - - if (readonly) - { - source.Reply(_("Sorry, channel 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) - { - 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" - "badword 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->UseStrictPrivMsgString.c_str(), source.owner->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 of the\n" - "bad words list.")); - return true; - } -}; - -class BSBadwords : public Module -{ - CommandBSBadwords commandbsbadwords; - - public: - BSBadwords(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandbsbadwords(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandbsbadwords); - } -}; - -MODULE_INIT(BSBadwords) diff --git a/modules/core/bs_bot.cpp b/modules/core/bs_bot.cpp deleted file mode 100644 index 7500d1926..000000000 --- a/modules/core/bs_bot.cpp +++ /dev/null @@ -1,414 +0,0 @@ -/* BotServ core functions - * - * (C) 2003-2011 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 ¶ms) - { - const Anope::string &nick = params[1]; - const Anope::string &user = params[2]; - const Anope::string &host = params[3]; - const Anope::string &real = params[4]; - BotInfo *bi; - - if (findbot(nick)) - { - source.Reply(_("Bot \002%s\002 already exists."), nick.c_str()); - return; - } - - if (nick.length() > Config->NickLen) - { - source.Reply(_("Bot Nicks may only contain valid nick characters.")); - return; - } - - if (user.length() > Config->UserLen) - { - source.Reply(_("Bot Idents may only contain %d characters."), Config->UserLen); - return; - } - - if (host.length() > Config->HostLen) - { - source.Reply(_("Bot Hosts may only contain %d characters."), Config->HostLen); - return; - } - - /* Check the nick is valid re RFC 2812 */ - if (isdigit(nick[0]) || nick[0] == '-') - { - source.Reply(_("Bot Nicks may only contain valid nick characters.")); - return; - } - - for (unsigned i = 0, end = nick.length(); i < end && i < Config->NickLen; ++i) - if (!isvalidnick(nick[i])) - { - source.Reply(_("Bot Nicks may only contain valid nick characters.")); - return; - } - - /* check for hardcored ircd forbidden nicks */ - if (!ircdproto->IsNickValid(nick)) - { - source.Reply(_("Bot Nicks may only contain valid nick characters.")); - return; - } - - /* Check the host is valid re RFC 2812 */ - if (!isValidHost(host, 3)) - { - source.Reply(_("Bot Hosts may only contain valid host characters.")); - return; - } - - for (unsigned i = 0, end = user.length(); i < end && i < Config->UserLen; ++i) - if (!isalnum(user[i])) - { - source.Reply(_("Bot Idents may only contain valid characters."), Config->UserLen); - 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 (findnick(nick)) - { - source.Reply(NICK_ALREADY_REGISTERED, nick.c_str()); - return; - } - - bi = new BotInfo(nick, user, host, real); - - Log(LOG_ADMIN, source.u, 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(I_OnBotCreate, OnBotCreate(bi)); - return; - } - - void DoChange(CommandSource &source, const std::vector ¶ms) - { - 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] : ""; - BotInfo *bi; - - if (oldnick.empty() || nick.empty()) - { - this->OnSyntaxError(source, "CHANGE"); - return; - } - - if (!(bi = findbot(oldnick))) - { - source.Reply(BOT_DOES_NOT_EXIST, oldnick.c_str()); - return; - } - - if (!oldnick.equals_ci(nick) && nickIsServices(oldnick, false)) - { - source.Reply(BOT_DOES_NOT_EXIST, oldnick.c_str()); - return; - } - - if (nick.length() > Config->NickLen) - { - source.Reply(_("Bot Nicks may only contain valid nick characters.")); - return; - } - - if (!user.empty() && user.length() > Config->UserLen) - { - source.Reply(_("Bot Idents may only contain %d characters."), Config->UserLen); - return; - } - - if (!host.empty() && host.length() > Config->HostLen) - { - source.Reply(_("Bot Hosts may only contain %d characters."), Config->HostLen); - return; - } - - if (!oldnick.equals_ci(nick) && nickIsServices(nick, false)) - { - source.Reply(BOT_DOES_NOT_EXIST, oldnick.c_str()); - 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(_("Old info is equal to the new one.")); - return; - } - - /* Check the nick is valid re RFC 2812 */ - if (isdigit(nick[0]) || nick[0] == '-') - { - source.Reply(_("Bot Nicks may only contain valid nick characters.")); - return; - } - - for (unsigned i = 0, end = nick.length(); i < end && i < Config->NickLen; ++i) - if (!isvalidnick(nick[i])) - { - source.Reply(_("Bot Nicks may only contain valid nick characters.")); - return; - } - - /* check for hardcored ircd forbidden nicks */ - if (!ircdproto->IsNickValid(nick)) - { - source.Reply(_("Bot Nicks may only contain valid nick characters.")); - return; - } - - if (!host.empty() && !isValidHost(host, 3)) - { - source.Reply(_("Bot Hosts may only contain valid host characters.")); - return; - } - - if (!user.empty()) - for (unsigned i = 0, end = user.length(); i < end && i < Config->UserLen; ++i) - if (!isalnum(user[i])) - { - source.Reply(_("Bot Idents may only contain valid characters."), Config->UserLen); - return; - } - - if (!nick.equals_ci(bi->nick) && findbot(nick)) - { - source.Reply(_("Bot \002%s\002 already exists."), 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 (findnick(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. */ - if (ircd->sqline) - { - XLine x(bi->nick); - ircdproto->SendSQLineDel(&x); - } - - /* Add a Q line for the new nick */ - XLine x(nick, "Reserved for services"); - ircdproto->SendSQLine(NULL, &x); - } - - if (!user.empty()) - ircdproto->SendQuit(bi, "Quit: Be right back"); - else - { - ircdproto->SendChangeBotNick(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()) - { - ircdproto->SendClientIntroduction(bi, ircd->pseudoclient_mode); - bi->RejoinAll(); - } - - 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.u, this) << "CHANGE " << oldnick << " to " << bi->GetMask() << " " << bi->realname; - - FOREACH_MOD(I_OnBotChange, OnBotChange(bi)); - return; - } - - void DoDel(CommandSource &source, const std::vector ¶ms) - { - const Anope::string &nick = params[1]; - BotInfo *bi; - - if (nick.empty()) - { - this->OnSyntaxError(source, "DEL"); - return; - } - - if (!(bi = findbot(nick))) - { - source.Reply(BOT_DOES_NOT_EXIST, nick.c_str()); - return; - } - - if (nickIsServices(nick, false)) - { - source.Reply(BOT_DOES_NOT_EXIST, nick.c_str()); - return; - } - - FOREACH_MOD(I_OnBotDelete, OnBotDelete(bi)); - - Log(LOG_ADMIN, source.u, 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 ¶ms) - { - const Anope::string &cmd = params[0]; - User *u = source.u; - - if (readonly) - { - source.Reply(_("Sorry, bot modification is temporarily disabled.")); - return; - } - - if (cmd.equals_ci("ADD")) - { - // ADD nick user host real - 5 - if (!u->HasCommand("botserv/bot/add")) - { - source.Reply(ACCESS_DENIED); - return; - } - - if (params.size() < 5) - { - this->OnSyntaxError(source, "ADD"); - return; - } - - std::vector 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 (!u->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 (!u->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) - { - 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" - "\002BOT CHANGE\002 allows to change nickname, username, hostname\n" - "or realname of a bot without actually delete it (and all\n" - "the data associated with it).\n" - "\002BOT DEL\002 removes the given bot from the bot list. \n" - " \n" - "\002Note\002: you cannot create a bot that has 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, CORE), - commandbsbot(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandbsbot); - } -}; - -MODULE_INIT(BSBot) diff --git a/modules/core/bs_botlist.cpp b/modules/core/bs_botlist.cpp deleted file mode 100644 index ee7e4d2d8..000000000 --- a/modules/core/bs_botlist.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/* BotServ core functions - * - * (C) 2003-2011 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")); - this->SetSyntax(""); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - unsigned count = 0; - - for (Anope::insensitive_map::const_iterator it = BotListByNick.begin(), it_end = BotListByNick.end(); it != it_end; ++it) - { - BotInfo *bi = it->second; - - if (!bi->HasFlag(BI_PRIVATE)) - { - if (!count) - source.Reply(_("Bot list:")); - ++count; - source.Reply(" %-15s (%s@%s)", bi->nick.c_str(), bi->GetIdent().c_str(), bi->host.c_str()); - } - } - - if (u->HasCommand("botserv/botserv/botlist") && count < BotListByNick.size()) - { - source.Reply(_("Bots reserved to IRC operators:")); - - for (Anope::insensitive_map::const_iterator it = BotListByNick.begin(), it_end = BotListByNick.end(); it != it_end; ++it) - { - BotInfo *bi = it->second; - - if (bi->HasFlag(BI_PRIVATE)) - { - source.Reply(" %-15s (%s@%s)", bi->nick.c_str(), bi->GetIdent().c_str(), bi->host.c_str()); - ++count; - } - } - } - - if (!count) - source.Reply(_("There are no bots available at this time.\n" - "Ask a Services Operator to create one!")); - else - source.Reply(_("%d bots available."), count); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Lists all available bots on this network.")); - return true; - } -}; - -class BSBotList : public Module -{ - CommandBSBotList commandbsbotlist; - - public: - BSBotList(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandbsbotlist(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandbsbotlist); - } -}; - -MODULE_INIT(BSBotList) diff --git a/modules/core/bs_control.cpp b/modules/core/bs_control.cpp deleted file mode 100644 index 16a5b372a..000000000 --- a/modules/core/bs_control.cpp +++ /dev/null @@ -1,158 +0,0 @@ -/* BotServ core functions - * - * (C) 2003-2011 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 given text on the given channel")); - this->SetSyntax(_("\037channel\037 \037text\037")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - const Anope::string &text = params[1]; - - User *u = source.u; - - ChannelInfo *ci = cs_findchan(params[0]); - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - if (!ci->HasPriv(u, CA_SAY)) - { - 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; - } - - ircdproto->SendPrivmsg(ci->bi, ci->name, "%s", text.c_str()); - ci->bi->lastmsg = Anope::CurTime; - - // XXX need a way to find if someone is overriding this - Log(LOG_COMMAND, u, this, ci) << text; - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Makes the bot say the given text on the given 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 ¶ms) - { - User *u = source.u; - Anope::string message = params[1]; - - ChannelInfo *ci = cs_findchan(params[0]); - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - if (!ci->HasPriv(u, CA_SAY)) - { - 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; - } - - size_t i = 0; - while ((i = message.find(1)) && i != Anope::string::npos) - message.erase(i, 1); - - ircdproto->SendAction(ci->bi, ci->name, "%s", message.c_str()); - ci->bi->lastmsg = Anope::CurTime; - - // XXX Need to be able to find if someone is overriding this. - Log(LOG_COMMAND, u, this, ci) << message; - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Makes the bot do the equivalent of a \"/me\" command\n" - "on the given channel using the given text.")); - return true; - } -}; - -class BSControl : public Module -{ - CommandBSSay commandbssay; - CommandBSAct commandbsact; - - public: - BSControl(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandbssay(this), commandbsact(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandbssay); - ModuleManager::RegisterService(&commandbsact); - } -}; - -MODULE_INIT(BSControl) diff --git a/modules/core/bs_info.cpp b/modules/core/bs_info.cpp deleted file mode 100644 index b48a0b296..000000000 --- a/modules/core/bs_info.cpp +++ /dev/null @@ -1,244 +0,0 @@ -/* BotServ core functions - * - * (C) 2003-2011 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(CommandSource &source, BotInfo *bi) - { - Anope::string buf; - for (registered_channel_map::const_iterator it = RegisteredChannelList.begin(), it_end = RegisteredChannelList.end(); it != it_end; ++it) - { - ChannelInfo *ci = it->second; - - if (ci->bi == bi) - { - if (buf.length() + ci->name.length() > 300) - { - source.Reply("%s", buf.c_str()); - buf.clear(); - } - buf += " " + ci->name + " "; - } - } - - if (!buf.empty()) - source.Reply("%s", buf.c_str()); - return; - } - public: - CommandBSInfo(Module *creator) : Command(creator, "botserv/info", 1, 1) - { - this->SetDesc(_("Allows you to see BotServ information about a channel or a bot")); - this->SetSyntax(_("\002INFO {\037chan\037 | \037nick\037}\002")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - const Anope::string &query = params[0]; - - bool need_comma = false; - char buf[BUFSIZE], *end; - - User *u = source.u; - BotInfo *bi = findbot(query); - ChannelInfo *ci; - if (bi) - { - source.Reply(_("Information for bot \002%s\002:"), bi->nick.c_str()); - source.Reply(_(" Mask : %s@%s"), bi->GetIdent().c_str(), bi->host.c_str()); - source.Reply(_(" Real name : %s"), bi->realname.c_str()); - source.Reply(_(" Created : %s"), do_strftime(bi->created).c_str()); - source.Reply(_(" Options : %s"), bi->HasFlag(BI_PRIVATE) ? _("Private") : _("None")); - source.Reply(_(" Used on : %d channel(s)"), bi->chancount); - - if (u->HasPriv("botserv/administration")) - this->send_bot_channels(source, bi); - } - else if ((ci = cs_findchan(query))) - { - if (!ci->HasPriv(u, CA_FOUNDER) && !u->HasPriv("botserv/administration")) - { - source.Reply(ACCESS_DENIED); - return; - } - - source.Reply(CHAN_INFO_HEADER, ci->name.c_str()); - if (ci->bi) - source.Reply(_(" Bot nick : %s"), ci->bi->nick.c_str()); - else - source.Reply(_(" Bot nick : not assigned yet.")); - - if (ci->botflags.HasFlag(BS_KICK_BADWORDS)) - { - if (ci->ttb[TTB_BADWORDS]) - source.Reply(_(" Bad words kicker : %s (%d kick(s) to ban)"), ENABLED, ci->ttb[TTB_BADWORDS]); - else - source.Reply(_(" Bad words kicker : %s"), ENABLED); - } - else - source.Reply(_(" Bad words kicker : %s"), DISABLED); - if (ci->botflags.HasFlag(BS_KICK_BOLDS)) - { - if (ci->ttb[TTB_BOLDS]) - source.Reply(_(" Bolds kicker : %s (%d kick(s) to ban)"), ENABLED, ci->ttb[TTB_BOLDS]); - else - source.Reply(_(" Bolds kicker : %s"), ENABLED); - } - else - source.Reply(_(" Bolds kicker : %s"), DISABLED); - if (ci->botflags.HasFlag(BS_KICK_CAPS)) - { - if (ci->ttb[TTB_CAPS]) - source.Reply(_(" Caps kicker : %s (%d kick(s) to ban; minimum %d/%d%%)"), ENABLED, ci->ttb[TTB_CAPS], ci->capsmin, ci->capspercent); - else - source.Reply(_(" Caps kicker : %s (minimum %d/%d%%)"), ENABLED, ci->capsmin, ci->capspercent); - } - else - source.Reply(_(" Caps kicker : %s"), DISABLED); - if (ci->botflags.HasFlag(BS_KICK_COLORS)) - { - if (ci->ttb[TTB_COLORS]) - source.Reply(_(" Colors kicker : %s (%d kick(s) to ban)"), ENABLED, ci->ttb[TTB_COLORS]); - else - source.Reply(_(" Colors kicker : %s"), ENABLED); - } - else - source.Reply(_(" Colors kicker : %s"), DISABLED); - if (ci->botflags.HasFlag(BS_KICK_FLOOD)) - { - if (ci->ttb[TTB_FLOOD]) - source.Reply(_(" Flood kicker : %s (%d kick(s) to ban; %d lines in %ds)"), ENABLED, ci->ttb[TTB_FLOOD], ci->floodlines, ci->floodsecs); - else - source.Reply(_(" Flood kicker : %s (%d lines in %ds)"), ENABLED, ci->floodlines, ci->floodsecs); - } - else - source.Reply(_(" Flood kicker : %s"), DISABLED); - if (ci->botflags.HasFlag(BS_KICK_REPEAT)) - { - if (ci->ttb[TTB_REPEAT]) - source.Reply(_(" Repeat kicker : %s (%d kick(s) to ban; %d times)"), ENABLED, ci->ttb[TTB_REPEAT], ci->repeattimes); - else - source.Reply(_(" Repeat kicker : %s (%d times)"), ENABLED, ci->repeattimes); - } - else - source.Reply(_(" Repeat kicker : %s"), DISABLED); - if (ci->botflags.HasFlag(BS_KICK_REVERSES)) - { - if (ci->ttb[TTB_REVERSES]) - source.Reply(_(" Reverses kicker : %s (%d kick(s) to ban)"), ENABLED, ci->ttb[TTB_REVERSES]); - else - source.Reply(_(" Reverses kicker : %s"), ENABLED); - } - else - source.Reply(_(" Reverses kicker : %s"), DISABLED); - if (ci->botflags.HasFlag(BS_KICK_UNDERLINES)) - { - if (ci->ttb[TTB_UNDERLINES]) - source.Reply(_(" Underlines kicker : %s (%d kick(s) to ban)"), ENABLED, ci->ttb[TTB_UNDERLINES]); - else - source.Reply(_(" Underlines kicker : %s"), ENABLED); - } - else - source.Reply(_(" Underlines kicker : %s"), DISABLED); - if (ci->botflags.HasFlag(BS_KICK_ITALICS)) - { - if (ci->ttb[TTB_ITALICS]) - source.Reply(_(" Italics kicker : %s (%d kick(s) to ban)"), ENABLED, ci->ttb[TTB_ITALICS]); - else - source.Reply(_(" Italics kicker : %s"), ENABLED); - } - else - source.Reply(_(" Italics kicker : %s"), DISABLED); - if (ci->botflags.HasFlag(BS_KICK_AMSGS)) - { - if (ci->ttb[TTB_AMSGS]) - source.Reply(_(" AMSG kicker : %s (%d kick(s) to ban)"), ENABLED, ci->ttb[TTB_AMSGS]); - else - source.Reply(_(" AMSG kicker : %s"), ENABLED); - } - else - source.Reply(_(" AMSG kicker : %s"), DISABLED); - - if (ci->botflags.HasFlag(BS_MSG_PRIVMSG)) - source.Reply(_(" Fantasy reply : %s"), "PRIVMSG"); - else if (ci->botflags.HasFlag(BS_MSG_NOTICE)) - source.Reply(_(" Fantasy reply : %s"), "NOTICE"); - else if (ci->botflags.HasFlag(BS_MSG_NOTICEOPS)) - source.Reply(_(" Fantasy reply : %s"), "NOTICEOPS"); - - end = buf; - *end = 0; - if (ci->botflags.HasFlag(BS_DONTKICKOPS)) - { - end += snprintf(end, sizeof(buf) - (end - buf), "%s", _("Ops protection")); - need_comma = true; - } - if (ci->botflags.HasFlag(BS_DONTKICKVOICES)) - { - end += snprintf(end, sizeof(buf) - (end - buf), "%s%s", need_comma ? ", " : "", _("Voices protection")); - need_comma = true; - } - if (ci->botflags.HasFlag(BS_FANTASY)) - { - end += snprintf(end, sizeof(buf) - (end - buf), "%s%s", need_comma ? ", " : "", _("Fantasy")); - need_comma = true; - } - if (ci->botflags.HasFlag(BS_GREET)) - { - end += snprintf(end, sizeof(buf) - (end - buf), "%s%s", need_comma ? ", " : "", _("Greet")); - need_comma = true; - } - if (ci->botflags.HasFlag(BS_NOBOT)) - { - end += snprintf(end, sizeof(buf) - (end - buf), "%s%s", need_comma ? ", " : "", _("No bot")); - need_comma = true; - } - source.Reply(_(" Options : %s"), *buf ? buf : _("None")); - } - else - source.Reply(_("\002%s\002 is not a valid bot or registered channel."), query.c_str()); - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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.owner->nick.c_str()); - return true; - } -}; - -class BSInfo : public Module -{ - CommandBSInfo commandbsinfo; - - public: - BSInfo(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandbsinfo(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandbsinfo); - } -}; - -MODULE_INIT(BSInfo) diff --git a/modules/core/bs_kick.cpp b/modules/core/bs_kick.cpp deleted file mode 100644 index 3f433f773..000000000 --- a/modules/core/bs_kick.cpp +++ /dev/null @@ -1,1011 +0,0 @@ -/* BotServ core functions - * - * (C) 2003-2011 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 CommandBSKick : public Command -{ - public: - CommandBSKick(Module *creator) : Command(creator, "botserv/kick", 3, 6) - { - this->SetDesc(_("Configures kickers")); - this->SetSyntax(_("\037channel\037 \037option\037 {\037ON|\037} [\037settings\037]")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - const Anope::string &chan = params[0]; - const Anope::string &option = params[1]; - const Anope::string &value = params[2]; - const Anope::string &ttb = params.size() > 3 ? params[3] : ""; - - User *u = source.u; - ChannelInfo *ci = cs_findchan(params[0]); - - if (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 (chan.empty() || option.empty() || value.empty()) - this->OnSyntaxError(source, ""); - else if (!value.equals_ci("ON") && !value.equals_ci("OFF")) - this->OnSyntaxError(source, ""); - else if (!ci->HasPriv(u, CA_SET) && !u->HasPriv("botserv/administration")) - source.Reply(ACCESS_DENIED); - else if (!ci->bi) - source.Reply(BOT_NOT_ASSIGNED); - else - { - bool override = !ci->HasPriv(u, CA_SET); - Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << option << " " << value; - - if (option.equals_ci("BADWORDS")) - { - if (value.equals_ci("ON")) - { - if (!ttb.empty()) - { - try - { - ci->ttb[TTB_BADWORDS] = convertTo(ttb); - if (ci->ttb[TTB_BADWORDS] < 0) - throw ConvertException(); - } - catch (const ConvertException &) - { - /* reset the value back to 0 - TSL */ - ci->ttb[TTB_BADWORDS] = 0; - source.Reply(_("\002%s\002 cannot be taken as times to ban."), ttb.c_str()); - return; - } - } - else - ci->ttb[TTB_BADWORDS] = 0; - - ci->botflags.SetFlag(BS_KICK_BADWORDS); - if (ci->ttb[TTB_BADWORDS]) - source.Reply(_("Bot will now kick \002bad words\002, and will place a ban after \n" - "%d kicks for the same user. Use the BADWORDS command\n" - "to add or remove a bad word."), ci->ttb[TTB_BADWORDS]); - else - source.Reply(_("Bot will now kick \002bad words\002. Use the BADWORDS command\n" - "to add or remove a bad word.")); - } - else - { - ci->botflags.UnsetFlag(BS_KICK_BADWORDS); - source.Reply(_("Bot won't kick \002bad words\002 anymore.")); - } - } - else if (option.equals_ci("BOLDS")) - { - if (value.equals_ci("ON")) - { - if (!ttb.empty()) - { - try - { - ci->ttb[TTB_BOLDS] = convertTo(ttb); - if (ci->ttb[TTB_BOLDS] < 0) - throw ConvertException(); - } - catch (const ConvertException &) - { - ci->ttb[TTB_BOLDS] = 0; - source.Reply(_("\002%s\002 cannot be taken as times to ban."), ttb.c_str()); - return; - } - } - else - ci->ttb[TTB_BOLDS] = 0; - ci->botflags.SetFlag(BS_KICK_BOLDS); - if (ci->ttb[TTB_BOLDS]) - source.Reply(_("Bot will now kick \002bolds\002, and will place a ban after\n%d kicks to the same user."), ci->ttb[TTB_BOLDS]); - else - source.Reply(_("Bot will now kick \002bolds\002.")); - } - else - { - ci->botflags.UnsetFlag(BS_KICK_BOLDS); - source.Reply(_("Bot won't kick \002bolds\002 anymore.")); - } - } - else if (option.equals_ci("CAPS")) - { - if (value.equals_ci("ON")) - { - Anope::string min = params.size() > 4 ? params[4] : ""; - Anope::string percent = params.size() > 5 ? params[5] : ""; - - if (!ttb.empty()) - { - try - { - ci->ttb[TTB_CAPS] = convertTo(ttb); - if (ci->ttb[TTB_CAPS] < 0) - throw ConvertException(); - } - catch (const ConvertException &) - { - ci->ttb[TTB_CAPS] = 0; - source.Reply(_("\002%s\002 cannot be taken as times to ban."), ttb.c_str()); - return; - } - } - else - ci->ttb[TTB_CAPS] = 0; - - ci->capsmin = 10; - try - { - ci->capsmin = convertTo(min); - } - catch (const ConvertException &) { } - if (ci->capsmin < 1) - ci->capsmin = 10; - - ci->capspercent = 25; - try - { - ci->capspercent = convertTo(percent); - } - catch (const ConvertException &) { } - if (ci->capspercent < 1 || ci->capspercent > 100) - ci->capspercent = 25; - - ci->botflags.SetFlag(BS_KICK_CAPS); - if (ci->ttb[TTB_CAPS]) - source.Reply(_("Bot will now kick \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."), ci->capsmin, ci->capspercent, ci->ttb[TTB_CAPS]); - else - source.Reply(_("Bot will now kick \002caps\002 (they must constitute at least\n" - "%d characters and %d%% of the entire message)."), ci->capsmin, ci->capspercent); - } - else - { - ci->botflags.UnsetFlag(BS_KICK_CAPS); - source.Reply(_("Bot won't kick \002caps\002 anymore.")); - } - } - else if (option.equals_ci("COLORS")) - { - if (value.equals_ci("ON")) - { - if (!ttb.empty()) - { - try - { - ci->ttb[TTB_COLORS] = convertTo(ttb); - if (ci->ttb[TTB_COLORS] < 1) - throw ConvertException(); - } - catch (const ConvertException &) - { - ci->ttb[TTB_COLORS] = 0; - source.Reply(_("\002%s\002 cannot be taken as times to ban."), ttb.c_str()); - return; - } - } - else - ci->ttb[TTB_COLORS] = 0; - - ci->botflags.SetFlag(BS_KICK_COLORS); - if (ci->ttb[TTB_COLORS]) - source.Reply(_("Bot will now kick \002colors\002, and will place a ban after %d\nkicks for the same user."), ci->ttb[TTB_COLORS]); - else - source.Reply(_("Bot will now kick \002colors\002.")); - } - else - { - ci->botflags.UnsetFlag(BS_KICK_COLORS); - source.Reply(_("Bot won't kick \002colors\002 anymore.")); - } - } - else if (option.equals_ci("FLOOD")) - { - if (value.equals_ci("ON")) - { - Anope::string lines = params.size() > 4 ? params[4] : ""; - Anope::string secs = params.size() > 5 ? params[5] : ""; - - if (!ttb.empty()) - { - try - { - ci->ttb[TTB_FLOOD] = convertTo(ttb); - if (ci->ttb[TTB_FLOOD] < 1) - throw ConvertException(); - } - catch (const ConvertException &) - { - ci->ttb[TTB_FLOOD] = 0; - source.Reply(_("\002%s\002 cannot be taken as times to ban."), ttb.c_str()); - return; - } - } - else - ci->ttb[TTB_FLOOD] = 0; - - ci->floodlines = 6; - try - { - ci->floodlines = convertTo(lines); - } - catch (const ConvertException &) { } - if (ci->floodlines < 2) - ci->floodlines = 6; - - ci->floodsecs = 10; - try - { - ci->floodsecs = convertTo(secs); - } - catch (const ConvertException &) { } - if (ci->floodsecs < 1) - ci->floodsecs = 10; - if (ci->floodsecs > Config->BSKeepData) - ci->floodsecs = Config->BSKeepData; - - ci->botflags.SetFlag(BS_KICK_FLOOD); - if (ci->ttb[TTB_FLOOD]) - source.Reply(_("Bot will now kick \002flood\002 (%d lines in %d seconds and\nwill place a ban after %d kicks for the same user."), ci->floodlines, ci->floodsecs, ci->ttb[TTB_FLOOD]); - else - source.Reply(_("Bot will now kick \002flood\002 (%d lines in %d seconds)."), ci->floodlines, ci->floodsecs); - } - else - { - ci->botflags.UnsetFlag(BS_KICK_FLOOD); - source.Reply(_("Bot won't kick \002flood\002 anymore.")); - } - } - else if (option.equals_ci("REPEAT")) - { - if (value.equals_ci("ON")) - { - Anope::string times = params.size() > 4 ? params[4] : ""; - - if (!ttb.empty()) - { - try - { - ci->ttb[TTB_REPEAT] = convertTo(ttb); - if (ci->ttb[TTB_REPEAT] < 0) - throw ConvertException(); - } - catch (const ConvertException &) - { - ci->ttb[TTB_REPEAT] = 0; - source.Reply(_("\002%s\002 cannot be taken as times to ban."), ttb.c_str()); - return; - } - } - else - ci->ttb[TTB_REPEAT] = 0; - - ci->repeattimes = 3; - try - { - ci->repeattimes = convertTo(times); - } - catch (const ConvertException &) { } - if (ci->repeattimes < 2) - ci->repeattimes = 3; - - ci->botflags.SetFlag(BS_KICK_REPEAT); - if (ci->ttb[TTB_REPEAT]) - source.Reply(_("Bot will now kick \002repeats\002 (users that say %d times\n" - "the same thing), and will place a ban after %d \n" - "kicks for the same user."), ci->repeattimes, ci->ttb[TTB_REPEAT]); - else - source.Reply(_("Bot will now kick \002repeats\002 (users that say %d times\n" - "the same thing)."), ci->repeattimes); - } - else - { - ci->botflags.UnsetFlag(BS_KICK_REPEAT); - source.Reply(_("Bot won't kick \002repeats\002 anymore.")); - } - } - else if (option.equals_ci("REVERSES")) - { - if (value.equals_ci("ON")) - { - if (!ttb.empty()) - { - try - { - ci->ttb[TTB_REVERSES] = convertTo(ttb); - if (ci->ttb[TTB_REVERSES] < 0) - throw ConvertException(); - } - catch (const ConvertException &) - { - ci->ttb[TTB_REVERSES] = 0; - source.Reply(_("\002%s\002 cannot be taken as times to ban."), ttb.c_str()); - return; - } - } - else - ci->ttb[TTB_REVERSES] = 0; - ci->botflags.SetFlag(BS_KICK_REVERSES); - if (ci->ttb[TTB_REVERSES]) - source.Reply(_("Bot will now kick \002reverses\002, and will place a ban after %d\nkicks for the same user."), ci->ttb[TTB_REVERSES]); - else - source.Reply(_("Bot will now kick \002reverses\002.")); - } - else - { - ci->botflags.UnsetFlag(BS_KICK_REVERSES); - source.Reply(_("Bot won't kick \002reverses\002 anymore.")); - } - } - else if (option.equals_ci("UNDERLINES")) - { - if (value.equals_ci("ON")) - { - if (!ttb.empty()) - { - try - { - ci->ttb[TTB_UNDERLINES] = convertTo(ttb); - if (ci->ttb[TTB_REVERSES] < 0) - throw ConvertException(); - } - catch (const ConvertException &) - { - ci->ttb[TTB_UNDERLINES] = 0; - source.Reply(_("\002%s\002 cannot be taken as times to ban."), ttb.c_str()); - return; - } - } - else - ci->ttb[TTB_UNDERLINES] = 0; - - ci->botflags.SetFlag(BS_KICK_UNDERLINES); - if (ci->ttb[TTB_UNDERLINES]) - source.Reply(_("Bot will now kick \002underlines\002, and will place a ban after %d\nkicks for the same user."), ci->ttb[TTB_UNDERLINES]); - else - source.Reply(_("Bot will now kick \002underlines\002.")); - } - else - { - ci->botflags.UnsetFlag(BS_KICK_UNDERLINES); - source.Reply(_("Bot won't kick \002underlines\002 anymore.")); - } - } - else if (option.equals_ci("ITALICS")) - { - if (value.equals_ci("ON")) - { - if (!ttb.empty()) - { - try - { - ci->ttb[TTB_ITALICS] = convertTo(ttb); - if (ci->ttb[TTB_ITALICS] < 0) - throw ConvertException(); - } - catch (const ConvertException &) - { - ci->ttb[TTB_ITALICS] = 0; - source.Reply(_("\002%s\002 cannot be taken as times to ban."), ttb.c_str()); - return; - } - } - else - ci->ttb[TTB_ITALICS] = 0; - - ci->botflags.SetFlag(BS_KICK_ITALICS); - if (ci->ttb[TTB_ITALICS]) - source.Reply(_("Bot will now kick \002italics\002, and will place a ban after\n%d kicks for the same user."), ci->ttb[TTB_ITALICS]); - else - source.Reply(_("Bot will now kick \002italics\002.")); - } - else - { - ci->botflags.UnsetFlag(BS_KICK_ITALICS); - source.Reply(_("Bot won't kick \002italics\002 anymore.")); - } - } - else if (option.equals_ci("AMSGS")) - { - if (value.equals_ci("ON")) - { - if (!ttb.empty()) - { - try - { - ci->ttb[TTB_AMSGS] = convertTo(ttb); - if (ci->ttb[TTB_AMSGS] < 0) - throw ConvertException(); - } - catch (const ConvertException &) - { - ci->ttb[TTB_AMSGS] = 0; - source.Reply(_("\002%s\002 cannot be taken as times to ban."), ttb.c_str()); - return; - } - } - else - ci->ttb[TTB_AMSGS] = 0; - - ci->botflags.SetFlag(BS_KICK_AMSGS); - if (ci->ttb[TTB_AMSGS]) - source.Reply(_("Bot will now kick for \002amsgs\002, and will place a ban after %d\nkicks for the same user."), ci->ttb[TTB_AMSGS]); - else - source.Reply(_("Bot will now kick for \002amsgs\002")); - } - else - { - ci->botflags.UnsetFlag(BS_KICK_AMSGS); - source.Reply(_("Bot won't kick for \002amsgs\002 anymore.")); - } - } - else - source.Reply(UNKNOWN_OPTION, Config->UseStrictPrivMsgString.c_str(), option.c_str(), this->name.c_str()); - } - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - if (subcommand.empty()) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Configures bot kickers. \037option\037 can be one of:\n" - " \n" - " AMSGS Sets if the bot kicks for amsgs\n" - " BOLDS Sets if the bot kicks bolds\n" - " BADWORDS Sets if the bot kicks bad words\n" - " CAPS Sets if the bot kicks caps\n" - " COLORS Sets if the bot kicks colors\n" - " FLOOD Sets if the bot kicks flooding users\n" - " REPEAT Sets if the bot kicks users who repeat\n" - " themselves\n" - " REVERSES Sets if the bot kicks reverses\n" - " UNDERLINES Sets if the bot kicks underlines\n" - " ITALICS Sets if the bot kicks italics\n" - " \n" - "Type \002%s%s HELP KICK \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->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str()); - } - else if (subcommand.equals_ci("BADWORDS")) - source.Reply(_("Syntax: \002\037#channel\037 BADWORDS {\037ON|OFF\037} [\037ttb\037]\002\n" - "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" - "ttb is the number of times a user can be kicked\n" - "before it get banned. Don't give ttb to disable\n" - "the ban system once activated."), Config->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str()); - else if (subcommand.equals_ci("BOLDS")) - source.Reply(_("Syntax: \002\037channel\037 BOLDS {\037ON|OFF\037} [\037ttb\037]\002\n" - "Sets the bolds kicker on or off. When enabled, this\n" - "option tells the bot to kick users who use bolds.\n" - "ttb is the number of times a user can be kicked\n" - "before it get banned. Don't give ttb to disable\n" - "the ban system once activated.")); - else if (subcommand.equals_ci("CAPS")) - source.Reply(_("Syntax: \002\037channel\037 CAPS {\037ON|OFF\037} [\037ttb\037 [\037min\037 [\037percent\037]]]\002\n" - "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" - "ttb is the number of times a user can be kicked\n" - "before it get banned. Don't give ttb to disable\n" - "the ban system once activated.")); - else if (subcommand.equals_ci("COLORS")) - source.Reply(_("Syntax: \002\037channel\037 COLORS {\037ON|OFF\037} [\037ttb\037]\002\n" - "Sets the colors kicker on or off. When enabled, this\n" - "option tells the bot to kick users who use colors.\n" - "ttb is the number of times a user can be kicked\n" - "before it get banned. Don't give ttb to disable\n" - "the ban system once activated.")); - else if (subcommand.equals_ci("FLOOD")) - source.Reply(_("Syntax: \002\037channel\037 FLOOD {\037ON|OFF\037} [\037ttb\037 [\037ln\037 [\037secs\037]]]\002\n" - "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" - "ttb is the number of times a user can be kicked\n" - "before it get banned. Don't give ttb to disable\n" - "the ban system once activated.")); - else if (subcommand.equals_ci("REPEAT")) - source.Reply(_("Syntax: \002\037#channel\037 REPEAT {\037ON|OFF\037} [\037ttb\037 [\037num\037]]\002\n" - "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" - "ttb is the number of times a user can be kicked\n" - "before it get banned. Don't give ttb to disable\n" - "the ban system once activated.")); - else if (subcommand.equals_ci("REVERSES")) - source.Reply(_("Syntax: \002\037channel\037 REVERSES {\037ON|OFF\037} [\037ttb\037]\002\n" - "Sets the reverses kicker on or off. When enabled, this\n" - "option tells the bot to kick users who use reverses.\n" - "ttb is the number of times a user can be kicked\n" - "before it get banned. Don't give ttb to disable\n" - "the ban system once activated.")); - else if (subcommand.equals_ci("UNDERLINES")) - source.Reply(_("Syntax: \002\037channel\037 UNDERLINES {\037ON|OFF\037} [\037ttb\037]\002\n" - "Sets the underlines kicker on or off. When enabled, this\n" - "option tells the bot to kick users who use underlines.\n" - "ttb is the number of times a user can be kicked\n" - "before it get banned. Don't give ttb to disable\n" - "the ban system once activated.")); - else if (subcommand.equals_ci("ITALICS")) - source.Reply(_("Syntax: \002\037channel\037 ITALICS {\037ON|OFF\037} [\037ttb\037]\002\n" - "Sets the italics kicker on or off. When enabled, this\n" - "option tells the bot to kick users who use italics.\n" - "ttb is the number of times a user can be kicked\n" - "before it get banned. Don't give ttb to disable\n" - "the ban system once activated.")); - else if (subcommand.equals_ci("AMSGS")) - source.Reply(_("Syntax: \002\037channel\037 AMSGS {\037ON|OFF\037} [\037ttb\037]\002\n" - "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 BotServ bots are.\n" - "Ttb is the number of times a user can be kicked\n" - "before it get banned. Don't give ttb to disable\n" - "the ban system once activated.")); - else - return false; - - return true; - } -}; - -struct BanData -{ - Anope::string mask; - time_t last_use; - int16 ttb[TTB_SIZE]; - - BanData() - { - this->Clear(); - } - - void Clear() - { - last_use = 0; - for (int i = 0; i < TTB_SIZE; ++i) - this->ttb[i] = 0; - } -}; - -struct UserData -{ - UserData() - { - this->Clear(); - } - - void Clear() - { - last_use = last_start = Anope::CurTime; - lines = times = 0; - lastline.clear(); - } - - /* Data validity */ - time_t last_use; - - /* for flood kicker */ - int16 lines; - time_t last_start; - - /* for repeat kicker */ - Anope::string lastline; - Anope::string lasttarget; - int16 times; -}; - - -class BanDataPurger : public CallBack -{ - public: - BanDataPurger(Module *owner) : CallBack(owner, 300, Anope::CurTime, true) { } - - void Tick(time_t) - { - 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; - - std::map bandata; - if (c->GetExtRegular("bs_main_bandata", bandata)) - { - for (std::map::iterator it2 = bandata.begin(), it2_end = bandata.end(); it2 != it2_end;) - { - const Anope::string &user = it->first; - BanData *bd = &it2->second; - ++it2; - - if (Anope::CurTime - bd->last_use > Config->BSKeepData) - { - bandata.erase(user); - continue; - } - } - - if (bandata.empty()) - c->Shrink("bs_main_bandata"); - } - } - } -}; - -class BSKick : public Module -{ - CommandBSKick commandbskick; - BanDataPurger purger; - - BanData *GetBanData(User *u, Channel *c) - { - std::map bandatamap; - if (!c->GetExtRegular("bs_main_bandata", bandatamap)); - c->Extend("bs_main_bandata", new ExtensibleItemRegular >(bandatamap)); - c->GetExtRegular("bs_main_bandata", bandatamap); - - BanData *bd = &bandatamap[u->GetMask()]; - if (bd->last_use && Anope::CurTime - bd->last_use > Config->BSKeepData) - bd->Clear(); - bd->last_use = Anope::CurTime; - return bd; - } - - UserData *GetUserData(User *u, Channel *c) - { - UserData *ud = NULL; - UserContainer *uc = c->FindUser(u); - if (uc != NULL && !uc->GetExtPointer("bs_main_userdata", ud)) - { - ud = new UserData(); - uc->Extend("bs_main_userdata", new ExtensibleItemPointer(ud)); - } - return ud; - } - - void check_ban(ChannelInfo *ci, User *u, int ttbtype) - { - /* Don't ban ulines */ - if (u->server->IsULined()) - return; - - BanData *bd = this->GetBanData(u, ci->c); - - ++bd->ttb[ttbtype]; - if (ci->ttb[ttbtype] && bd->ttb[ttbtype] >= ci->ttb[ttbtype]) - { - /* Should not use == here because bd->ttb[ttbtype] could possibly be > ci->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 */ - Anope::string mask; - - bd->ttb[ttbtype] = 0; - - get_idealban(ci, u, mask); - - if (ci->c) - ci->c->SetMode(NULL, CMODE_BAN, mask); - FOREACH_MOD(I_OnBotBan, 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->server->IsULined()) - return; - - Anope::string fmt = 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, CORE), - commandbskick(this), purger(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandbskick); - - ModuleManager::Attach(I_OnPrivmsg, this); - } - - ~BSKick() - { - for (channel_map::const_iterator cit = ChannelList.begin(), cit_end = ChannelList.end(); cit != cit_end; ++cit) - { - cit->second->Shrink("bs_main_userdata"); - cit->second->Shrink("bs_main_bandata"); - } - } - - void OnPrivmsg(User *u, Channel *c, Anope::string &msg) - { - /* 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; - - bool Allow = true; - if (ci->HasPriv(u, CA_NOKICK)) - Allow = false; - else if (ci->botflags.HasFlag(BS_DONTKICKOPS) && (c->HasUserStatus(u, CMODE_HALFOP) || c->HasUserStatus(u, CMODE_OP) || c->HasUserStatus(u, CMODE_PROTECT) || c->HasUserStatus(u, CMODE_OWNER))) - Allow = false; - else if (ci->botflags.HasFlag(BS_DONTKICKVOICES) && c->HasUserStatus(u, CMODE_VOICE)) - Allow = false; - - if (Allow) - { - 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 (ci->botflags.HasFlag(BS_KICK_BOLDS) && realbuf.find(2) != Anope::string::npos) - { - check_ban(ci, u, TTB_BOLDS); - bot_kick(ci, u, _("Don't use bolds on this channel!")); - return; - } - - /* Color kicker */ - if (ci->botflags.HasFlag(BS_KICK_COLORS) && realbuf.find(3) != Anope::string::npos) - { - check_ban(ci, u, TTB_COLORS); - bot_kick(ci, u, _("Don't use colors on this channel!")); - return; - } - - /* Reverses kicker */ - if (ci->botflags.HasFlag(BS_KICK_REVERSES) && realbuf.find(22) != Anope::string::npos) - { - check_ban(ci, u, TTB_REVERSES); - bot_kick(ci, u, _("Don't use reverses on this channel!")); - return; - } - - /* Italics kicker */ - if (ci->botflags.HasFlag(BS_KICK_ITALICS) && realbuf.find(29) != Anope::string::npos) - { - check_ban(ci, u, TTB_ITALICS); - bot_kick(ci, u, _("Don't use italics on this channel!")); - return; - } - - /* Underlines kicker */ - if (ci->botflags.HasFlag(BS_KICK_UNDERLINES) && realbuf.find(31) != Anope::string::npos) - { - check_ban(ci, u, TTB_UNDERLINES); - bot_kick(ci, u, _("Don't use underlines on this channel!")); - return; - } - - /* Caps kicker */ - if (ci->botflags.HasFlag(BS_KICK_CAPS) && realbuf.length() >= ci->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 >= ci->capsmin && i * 100 / (i + l) >= ci->capspercent) - { - check_ban(ci, u, TTB_CAPS); - bot_kick(ci, u, _("Turn caps lock OFF!")); - return; - } - } - - /* Bad words kicker */ - if (ci->botflags.HasFlag(BS_KICK_BADWORDS)) - { - bool mustkick = false; - - /* Normalize the buffer */ - Anope::string nbuf = normalizeBuffer(realbuf); - - for (unsigned i = 0, end = ci->GetBadWordCount(); i < end; ++i) - { - BadWord *bw = ci->GetBadWord(i); - - if (bw->type == BW_ANY && ((Config->BSCaseSensitive && nbuf.find(bw->word) != Anope::string::npos) || (!Config->BSCaseSensitive && nbuf.find_ci(bw->word) != Anope::string::npos))) - mustkick = true; - else if (bw->type == BW_SINGLE) - { - size_t len = bw->word.length(); - - if ((Config->BSCaseSensitive && bw->word.equals_cs(nbuf)) || (!Config->BSCaseSensitive && bw->word.equals_ci(nbuf))) - mustkick = true; - else if (nbuf.find(' ') == len && ((Config->BSCaseSensitive && bw->word.equals_cs(nbuf)) || (!Config->BSCaseSensitive && bw->word.equals_ci(nbuf)))) - mustkick = true; - else - { - if (nbuf.rfind(' ') == nbuf.length() - len - 1 && ((Config->BSCaseSensitive && nbuf.find(bw->word) == nbuf.length() - len) || (!Config->BSCaseSensitive && nbuf.find_ci(bw->word) == nbuf.length() - len))) - mustkick = true; - else - { - Anope::string wordbuf = " " + bw->word + " "; - - if ((Config->BSCaseSensitive && nbuf.find(wordbuf) != Anope::string::npos) || (!Config->BSCaseSensitive && nbuf.find_ci(wordbuf) != Anope::string::npos)) - mustkick = true; - } - } - } - else if (bw->type == BW_START) - { - size_t len = bw->word.length(); - - if ((Config->BSCaseSensitive && nbuf.substr(0, len).equals_cs(bw->word)) || (!Config->BSCaseSensitive && nbuf.substr(0, len).equals_ci(bw->word))) - mustkick = true; - else - { - Anope::string wordbuf = " " + bw->word; - - if ((Config->BSCaseSensitive && nbuf.find(wordbuf) != Anope::string::npos) || (!Config->BSCaseSensitive && nbuf.find_ci(wordbuf) != Anope::string::npos)) - mustkick = true; - } - } - else if (bw->type == BW_END) - { - size_t len = bw->word.length(); - - if ((Config->BSCaseSensitive && nbuf.substr(nbuf.length() - len).equals_cs(bw->word)) || (!Config->BSCaseSensitive && nbuf.substr(nbuf.length() - len).equals_ci(bw->word))) - mustkick = true; - else - { - Anope::string wordbuf = bw->word + " "; - - if ((Config->BSCaseSensitive && nbuf.find(wordbuf) != Anope::string::npos) || (!Config->BSCaseSensitive && nbuf.find_ci(wordbuf) != Anope::string::npos)) - mustkick = true; - } - } - - if (mustkick) - { - check_ban(ci, u, TTB_BADWORDS); - if (Config->BSGentleBWReason) - 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; - } - } - - UserData *ud = NULL; - - /* Flood kicker */ - if (ci->botflags.HasFlag(BS_KICK_FLOOD)) - { - ud = GetUserData(u, c); - if (ud) - { - if (Anope::CurTime - ud->last_start > ci->floodsecs) - { - ud->last_start = Anope::CurTime; - ud->lines = 0; - } - - ++ud->lines; - if (ud->lines >= ci->floodlines) - { - check_ban(ci, u, TTB_FLOOD); - bot_kick(ci, u, _("Stop flooding!")); - return; - } - } - } - - /* Repeat kicker */ - if (ci->botflags.HasFlag(BS_KICK_REPEAT)) - { - if (!ud) - ud = GetUserData(u, c); - if (ud) - { - - if (!ud->lastline.empty() && !ud->lastline.equals_ci(realbuf)) - { - ud->lastline = realbuf; - ud->times = 0; - } - else - { - if (ud->lastline.empty()) - ud->lastline = realbuf; - ++ud->times; - } - - if (ud->times >= ci->repeattimes) - { - check_ban(ci, u, TTB_REPEAT); - bot_kick(ci, u, _("Stop repeating yourself!")); - return; - } - } - } - - if (ud && ud->lastline.equals_ci(realbuf) && !ud->lasttarget.empty() && !ud->lasttarget.equals_ci(ci->name)) - { - for (UChannelList::iterator it = u->chans.begin(); it != u->chans.end();) - { - Channel *chan = (*it)->chan; - ++it; - - if (chan->ci != NULL && chan->ci->botflags.HasFlag(BS_KICK_AMSGS) && !chan->ci->HasPriv(u, CA_NOKICK)) - { - check_ban(chan->ci, u, TTB_AMSGS); - bot_kick(chan->ci, u, _("Don't use AMSGs!")); - } - } - } - - if (ud) - ud->lasttarget = ci->name; - } - } - } -}; - -MODULE_INIT(BSKick) diff --git a/modules/core/bs_main.cpp b/modules/core/bs_main.cpp deleted file mode 100644 index 69eab6040..000000000 --- a/modules/core/bs_main.cpp +++ /dev/null @@ -1,204 +0,0 @@ -/* BotServ core functions - * - * (C) 2003-2011 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 BotServCore : public Module -{ - Channel *fantasy_channel; - public: - BotServCore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), fantasy_channel(NULL) - { - this->SetAuthor("Anope"); - - BotInfo *BotServ = findbot(Config->BotServ); - if (BotServ == NULL) - throw ModuleException("No bot named " + Config->BotServ); - - Implementation i[] = { I_OnPrivmsg, I_OnPreCommand, I_OnJoinChannel, I_OnLeaveChannel, - I_OnPreHelp, I_OnPostHelp, I_OnChannelModeSet }; - ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); - - } - - void OnPrivmsg(User *u, Channel *c, Anope::string &msg) - { - if (!u || !c || !c->ci || !c->ci->bi || msg.empty()) - return; - - /* Answer to ping if needed */ - if (msg.substr(0, 6).equals_ci("\1PING ") && msg[msg.length() - 1] == '\1') - { - Anope::string ctcp = msg; - ctcp.erase(ctcp.begin()); - ctcp.erase(ctcp.length() - 1); - ircdproto->SendCTCP(c->ci->bi, u->nick, "%s", ctcp.c_str()); - } - - - Anope::string realbuf = msg; - - bool was_action = false; - if (realbuf.substr(0, 8).equals_ci("\1ACTION ") && realbuf[realbuf.length() - 1] == '\1') - { - realbuf.erase(0, 8); - realbuf.erase(realbuf.length() - 1); - was_action = true; - } - - if (realbuf.empty()) - return; - - /* Fantaisist commands */ - if (c->ci->botflags.HasFlag(BS_FANTASY) && realbuf[0] == Config->BSFantasyCharacter[0] && !was_action) - { - /* Strip off the fantasy character */ - realbuf.erase(realbuf.begin()); - - size_t space = realbuf.find(' '); - Anope::string command, rest; - if (space == Anope::string::npos) - command = realbuf; - else - { - command = realbuf.substr(0, space); - rest = realbuf.substr(space + 1); - } - - BotInfo *bi = findbot(Config->ChanServ); - if (bi == NULL) - bi = findbot(Config->BotServ); - if (bi == NULL || !bi->commands.count(command)) - return; - - if (c->ci->HasPriv(u, CA_FANTASIA)) - { - - this->fantasy_channel = c; - bi->OnMessage(u, realbuf); - this->fantasy_channel = NULL; - - FOREACH_MOD(I_OnBotFantasy, OnBotFantasy(command, u, c->ci, rest)); - } - else - { - FOREACH_MOD(I_OnBotNoFantasyAccess, OnBotNoFantasyAccess(command, u, c->ci, rest)); - } - } - } - - EventReturn OnPreCommand(CommandSource &source, Command *command, std::vector ¶ms) - { - if (this->fantasy_channel != NULL) - { - if (!command->HasFlag(CFLAG_STRIP_CHANNEL)) - params.insert(params.begin(), this->fantasy_channel->name); - source.c = this->fantasy_channel; - } - - return EVENT_CONTINUE; - } - - void OnJoinChannel(User *user, Channel *c) - { - if (c->ci && c->ci->bi) - { - /** - * We let the bot join even if it was an ignored user, as if we don't, - * and the ignored user doesnt just leave, the bot will never - * make it into the channel, leaving the channel botless even for - * legit users - Rob - **/ - if (c->users.size() >= Config->BSMinUsers && !c->FindUser(c->ci->bi)) - c->ci->bi->Join(c, &Config->BotModeList); - /* 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->FindUser(c->ci->bi) && c->ci->botflags.HasFlag(BS_GREET) && user->Account() && !user->Account()->greet.empty() && c->ci->HasPriv(user, CA_GREET) && user->server->IsSynced()) - { - ircdproto->SendPrivmsg(c->ci->bi, c->name, "[%s] %s", user->Account()->display.c_str(), user->Account()->greet.c_str()); - c->ci->bi->lastmsg = Anope::CurTime; - } - } - } - - void OnLeaveChannel(User *u, Channel *c) - { - /* Channel is persistant, it shouldn't be deleted and the service bot should stay */ - if (c->HasFlag(CH_PERSIST) || (c->ci && c->ci->HasFlag(CI_PERSIST))) - return; - - /* Channel is syncing from a netburst, don't destroy it as more users are probably wanting to join immediatly - * We also don't part the bot here either, if necessary we will part it after the sync - */ - if (c->HasFlag(CH_SYNCING)) - return; - - /* Additionally, do not delete this channel if ChanServ/a BotServ bot is inhabiting it */ - if (c->HasFlag(CH_INHABIT)) - return; - - if (c->ci && c->ci->bi && c->users.size() - 1 <= Config->BSMinUsers && c->FindUser(c->ci->bi)) - { - bool persist = c->HasFlag(CH_PERSIST); - c->SetFlag(CH_PERSIST); - c->ci->bi->Part(c->ci->c); - if (!persist) - c->UnsetFlag(CH_PERSIST); - } - } - - void OnPreHelp(CommandSource &source, const std::vector ¶ms) - { - if (!params.empty() || source.owner->nick != Config->BotServ) - return; - source.Reply(_("\002%s\002 allows you to have a bot on your own channel.\n" - "It has been created for users that can't host or\n" - "configure a bot, or for use on networks that don't\n" - "allow user bots. Available commands are listed \n" - "below; to use them, type \002%s%s \037command\037\002. For\n" - "more information on a specific command, type\n" - "\002%s%s %s \037command\037\002.\n "), - Config->BotServ.c_str(), Config->UseStrictPrivMsgString.c_str(), Config->BotServ.c_str(), - Config->UseStrictPrivMsgString.c_str(), Config->BotServ.c_str(), source.command.c_str()); - } - - void OnPostHelp(CommandSource &source, const std::vector ¶ms) - { - if (!params.empty() || source.owner->nick != Config->BotServ) - return; - source.Reply(_(" \n" - "Bot will join a channel whenever there is at least\n" - "\002%d\002 user(s) on it. Additionally, all %s commands\n" - "can be used if fantasy is enabled by prefixing the command\n" - "name with a %c."), Config->BSMinUsers, Config->ChanServ.c_str(), Config->BSFantasyCharacter[0]); - } - - EventReturn OnChannelModeSet(Channel *c, ChannelModeName Name, const Anope::string ¶m) - { - if (Config->BSSmartJoin && Name == CMODE_BAN && c->ci && c->ci->bi && c->FindUser(c->ci->bi)) - { - BotInfo *bi = c->ci->bi; - - Entry ban(CMODE_BAN, param); - if (ban.Matches(bi)) - c->RemoveMode(bi, CMODE_BAN, param); - } - - return EVENT_CONTINUE; - } -}; - -MODULE_INIT(BotServCore) - diff --git a/modules/core/bs_set.cpp b/modules/core/bs_set.cpp deleted file mode 100644 index ccd1bb615..000000000 --- a/modules/core/bs_set.cpp +++ /dev/null @@ -1,307 +0,0 @@ -/* BotServ core functions - * - * (C) 2003-2011 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 CommandBSSet : public Command -{ - public: - CommandBSSet(Module *creator) : Command(creator, "botserv/set", 3, 3) - { - this->SetDesc(_("Configures bot options")); - this->SetSyntax(_("\037(channel | bot)\037 \037option\037 \037settings\037")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - const Anope::string &chan = params[0]; - const Anope::string &option = params[1]; - const Anope::string &value = params[2]; - - User *u = source.u; - ChannelInfo *ci; - - if (readonly) - source.Reply(_("Sorry, bot option setting is temporarily disabled.")); - else if (u->HasCommand("botserv/botserv/set/private") && option.equals_ci("PRIVATE")) - { - BotInfo *bi; - - if (!(bi = findbot(chan))) - { - source.Reply(BOT_DOES_NOT_EXIST, chan.c_str()); - return; - } - - if (value.equals_ci("ON")) - { - bi->SetFlag(BI_PRIVATE); - source.Reply(_("Private mode of bot %s is now \002on\002."), bi->nick.c_str()); - } - else if (value.equals_ci("OFF")) - { - bi->UnsetFlag(BI_PRIVATE); - source.Reply(_("Private mode of bot %s is now \002off\002."), bi->nick.c_str()); - } - else - this->OnSyntaxError(source, "PRIVATE"); - return; - } - else if (!(ci = cs_findchan(chan))) - source.Reply(CHAN_X_NOT_REGISTERED, chan.c_str()); - else if (!u->HasPriv("botserv/administration") && !ci->HasPriv(u, CA_SET)) - source.Reply(ACCESS_DENIED); - else - { - bool override = !ci->HasPriv(u, CA_SET); - Log(override ? LOG_ADMIN : LOG_COMMAND, u, this, ci) << option << " " << value; - - if (option.equals_ci("DONTKICKOPS")) - { - if (value.equals_ci("ON")) - { - ci->botflags.SetFlag(BS_DONTKICKOPS); - source.Reply(_("Bot \002won't kick ops\002 on channel %s."), ci->name.c_str()); - } - else if (value.equals_ci("OFF")) - { - ci->botflags.UnsetFlag(BS_DONTKICKOPS); - source.Reply(_("Bot \002will kick ops\002 on channel %s."), ci->name.c_str()); - } - else - this->OnSyntaxError(source, "DONTKICKOPS"); - } - else if (option.equals_ci("DONTKICKVOICES")) - { - if (value.equals_ci("ON")) - { - ci->botflags.SetFlag(BS_DONTKICKVOICES); - source.Reply(_("Bot \002won't kick voices\002 on channel %s."), ci->name.c_str()); - } - else if (value.equals_ci("OFF")) - { - ci->botflags.UnsetFlag(BS_DONTKICKVOICES); - source.Reply(_("Bot \002will kick voices\002 on channel %s."), ci->name.c_str()); - } - else - this->OnSyntaxError(source, "DONTKICKVOICE"); - } - else if (option.equals_ci("FANTASY")) - { - if (value.equals_ci("ON")) - { - ci->botflags.SetFlag(BS_FANTASY); - source.Reply(_("Fantasy mode is now \002on\002 on channel %s."), ci->name.c_str()); - } - else if (value.equals_ci("OFF")) - { - ci->botflags.UnsetFlag(BS_FANTASY); - source.Reply(_("Fantasy mode is now \002off\002 on channel %s."), ci->name.c_str()); - } - else - this->OnSyntaxError(source, "FANTASY"); - } - else if (option.equals_ci("GREET")) - { - if (value.equals_ci("ON")) - { - ci->botflags.SetFlag(BS_GREET); - source.Reply(_("Greet mode is now \002on\002 on channel %s."), ci->name.c_str()); - } - else if (value.equals_ci("OFF")) - { - ci->botflags.UnsetFlag(BS_GREET); - source.Reply(_("Greet mode is now \002off\002 on channel %s."), ci->name.c_str()); - } - else - this->OnSyntaxError(source, "GREET"); - } - else if (u->HasCommand("botserv/botserv/set/nobot") && option.equals_ci("NOBOT")) - { - if (value.equals_ci("ON")) - { - ci->botflags.SetFlag(BS_NOBOT); - if (ci->bi) - ci->bi->UnAssign(u, ci); - source.Reply(_("No Bot mode is now \002on\002 on channel %s."), ci->name.c_str()); - } - else if (value.equals_ci("OFF")) - { - ci->botflags.UnsetFlag(BS_NOBOT); - source.Reply(_("No Bot mode is now \002off\002 on channel %s."), ci->name.c_str()); - } - else - this->OnSyntaxError(source, "NOBOT"); - } - else if (option.equals_ci("MSG")) - { - if (value.equals_ci("OFF")) - { - ci->botflags.UnsetFlag(BS_MSG_PRIVMSG); - ci->botflags.UnsetFlag(BS_MSG_NOTICE); - ci->botflags.UnsetFlag(BS_MSG_NOTICEOPS); - source.Reply(_("Fantasy replies will no longer be sent to %s."), ci->name.c_str()); - } - else if (value.equals_ci("PRIVMSG")) - { - ci->botflags.SetFlag(BS_MSG_PRIVMSG); - ci->botflags.UnsetFlag(BS_MSG_NOTICE); - ci->botflags.UnsetFlag(BS_MSG_NOTICEOPS); - source.Reply(_("Fantasy replies will be sent via PRIVMSG to %s."), ci->name.c_str()); - } - else if (value.equals_ci("NOTICE")) - { - ci->botflags.UnsetFlag(BS_MSG_PRIVMSG); - ci->botflags.SetFlag(BS_MSG_NOTICE); - ci->botflags.UnsetFlag(BS_MSG_NOTICEOPS); - source.Reply(_("Fantasy replies will be sent via NOTICE to %s."), ci->name.c_str()); - } - else if (value.equals_ci("NOTICEOPS")) - { - ci->botflags.UnsetFlag(BS_MSG_PRIVMSG); - ci->botflags.UnsetFlag(BS_MSG_NOTICE); - ci->botflags.SetFlag(BS_MSG_NOTICEOPS); - source.Reply(_("Fantasy replies will be sent via NOTICE to channel ops on %s."), ci->name.c_str()); - } - else - this->OnSyntaxError(source, "MSG"); - } - else - source.Reply(UNKNOWN_OPTION, option.c_str(), Config->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str(), this->name.c_str()); - } - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - if (subcommand.empty()) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Configures bot options. \037option\037 can be one of:\n" - " \n" - " DONTKICKOPS To protect ops against bot kicks\n" - " DONTKICKVOICES To protect voices against bot kicks\n" - " GREET Enable greet messages\n" - " FANTASY Enable fantaisist commands\n" - " MSG Configure how fantasy commands should be replied to\n" - " \n" - "Type \002%s%s HELP SET \037option\037\002 for more information\n" - "on a specific option.\n" - "Note: access to this command is controlled by the\n" - "level SET."), Config->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str()); - User *u = source.u; - if (u->IsServicesOper()) - source.Reply(_("These options are reserved to Services Operators:\n" - " \n" - " NOBOT Prevent a bot from being assigned to \n" - " a channel\n" - " PRIVATE Prevent a bot from being assigned by\n" - " non IRC operators")); - } - else if (subcommand.equals_ci("DONTKICKOPS")) - source.Reply(_("Syntax: \002SET \037channel\037 DONTKICKOPS {\037ON|OFF\037}\n" - " \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.")); - else if (subcommand.equals_ci("DONTKICKVOICES")) - source.Reply(_("Syntax: \002SET \037channel\037 DONTKICKVOICES {\037ON|OFF\037}\n" - " \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.")); - else if (subcommand.equals_ci("FANTASY")) - source.Reply(_("Syntax: \002SET \037channel\037 FANTASY {\037ON|OFF\037}\n" - "Enables or disables \002fantasy\002 mode on a channel.\n" - "When it is enabled, users will be able to use\n" - "commands !op, !deop, !voice, !devoice,\n" - "!kick, !kb, !unban, !seen on a channel (find how \n" - "to use them; try with or without nick for each, \n" - "and with a reason for some?).\n" - " \n" - "Note that users wanting to use fantaisist\n" - "commands MUST have enough level for both\n" - "the FANTASIA and another level depending\n" - "of the command if required (for example, to use \n" - "!op, user must have enough access for the OPDEOP\n" - "level).")); - else if (subcommand.equals_ci("GREET")) - source.Reply(_("Syntax: \002SET \037channel\037 GREET {\037ON|OFF\037}\n" - " \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.")); - else if (subcommand.equals_ci("NOBOT")) - source.Reply(_("Syntax: \002SET \037channel\037 NOBOT {\037ON|OFF\037}\002\n" - " \n" - "This option makes a channel be unassignable. If a bot \n" - "is already assigned to the channel, it is unassigned\n" - "automatically when you enable the option.")); - else if (subcommand.equals_ci("PRIVATE")) - source.Reply(_("Syntax: \002SET \037bot-nick\037 PRIVATE {\037ON|OFF\037}\002\n" - "This option prevents a bot from being assigned to a\n" - "channel by users that aren't IRC operators.")); - else if (subcommand.equals_ci("MSG")) - source.Reply(_("Syntax: \002SET \037channel\037 MSG {\037OFF|PRIVMSG|NOTICE|NOTICEOPS\037}\002\n" - " \n" - "Configures how fantasy commands should be returned to the channel. Off disables\n" - "fantasy from replying to the channel. Privmsg, notice, and noticeops message the\n" - "channel, notice the channel, and notice the channel ops respectively.\n" - " \n" - "Note that replies over one line will not use this setting to prevent spam, and will\n" - "go directly to the user who executed it.")); - else - return false; - - return true; - } - - void OnSyntaxError(CommandSource &source, const Anope::string &subcommand) - { - if (subcommand.empty()) - Command::OnSyntaxError(source, ""); - else if (subcommand.equals_ci("PRIVATE")) - this->SendSyntax(source, "\037botname\037 PRIVATE {\037ON|OFF\037}"); - else if (subcommand.equals_ci("DONTKICKOPS")) - this->SendSyntax(source, "\037channel\037 DONTKICKOPS {\037ON|OFF\037}"); - else if (subcommand.equals_ci("DONTKICKVOICES")) - this->SendSyntax(source, "\037channel\037 DONTKICKVOICES {\037ON|OFF\037}"); - else if (subcommand.equals_ci("FANTASY")) - this->SendSyntax(source, "\037channel\037 FANTASY {\037ON|OFF\037}"); - else if (subcommand.equals_ci("GREET")) - this->SendSyntax(source, "\037channel\037 GREET {\037ON|OFF\037}"); - else if (subcommand.equals_ci("MSG")) - this->SendSyntax(source, "\037botname\037 NOBOT {\037ON|OFF\037}"); - else if (subcommand.equals_ci("NOBOT")) - this->SendSyntax(source, "\037botname\037 NOBOT {\037ON|OFF\037}"); - } -}; - -class BSSet : public Module -{ - CommandBSSet commandbsset; - - public: - BSSet(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandbsset(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandbsset); - } -}; - -MODULE_INIT(BSSet) diff --git a/modules/core/cs_access.cpp b/modules/core/cs_access.cpp deleted file mode 100644 index 777b0c176..000000000 --- a/modules/core/cs_access.cpp +++ /dev/null @@ -1,915 +0,0 @@ -/* ChanServ core functions - * - * (C) 2003-2011 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" - -enum -{ - ACCESS_INVALID = -10000, - ACCESS_FOUNDER = 10001 -}; - -static struct AccessLevels -{ - ChannelAccess priv; - int default_level; - Anope::string config_name; - Anope::string name; - Anope::string desc; -} defaultLevels[] = { - { CA_ACCESS_CHANGE, 10, "level_change", "ACC-CHANGE", _("Allowed to modify the access list") }, - { CA_ACCESS_LIST, 1, "level_list", "ACC-LIST", _("Allowed to view the access list") }, - { CA_AKICK, 10, "level_akick", "AKICK", _("Allowed to use AKICK command") }, - { CA_ASSIGN, ACCESS_FOUNDER, "level_assign", "ASSIGN", _("Allowed to assign/unassign a bot") }, - { CA_AUTOHALFOP, 4, "level_autohalfop", "AUTOHALFOP", _("Automatic mode +h") }, - { CA_AUTOOP, 5, "level_autoop", "AUTOOP", _("Automatic channel operator status") }, - { CA_AUTOOWNER, 10000, "level_autoowner", "AUTOOWNER", _("Automatic mode +q") }, - { CA_AUTOPROTECT, 10, "level_autoprotect", "AUTOPROTECT", _("Automatic mode +a") }, - { CA_AUTOVOICE, 3, "level_autovoice", "AUTOVOICE", _("Automatic mode +v") }, - { CA_BADWORDS, 10, "level_badwords", "BADWORDS", _("Allowed to modify channel badwords list") }, - { CA_BAN, 4, "level_ban", "BAN", _("Allowed to use ban users") }, - { CA_FANTASIA, 3, "level_fantasia", "FANTASIA", _("Allowed to use fantaisist commands") }, - { CA_FOUNDER, ACCESS_FOUNDER, "level_founder", "FOUNDER", _("Allowed to issue commands restricted to channel founders") }, - { CA_GETKEY, 5, "level_getkey", "GETKEY", _("Allowed to use GETKEY command") }, - { CA_GREET, 5, "level_greet", "GREET", _("Greet message displayed") }, - { CA_HALFOP, 5, "level_halfop", "HALFOP", _("Allowed to (de)halfop users") }, - { CA_HALFOPME, 4, "level_halfopme", "HALFOPME", _("Allowed to (de)halfop him/herself") }, - { CA_INFO, 10000, "level_info", "INFO", _("Allowed to use INFO command with ALL option") }, - { CA_INVITE, 5, "level_invite", "INVITE", _("Allowed to use the INVITE command") }, - { CA_KICK, 4, "level_kick", "KICK", _("Allowed to use the KICK command") }, - { CA_MEMO, 10, "level_memo", "MEMO", _("Allowed to read channel memos") }, - { CA_MODE, 5, "level_mode", "MODE", _("Allowed to change channel modes") }, - { CA_NOKICK, 1, "level_nokick", "NOKICK", _("Never kicked by the bot's kickers") }, - { CA_OPDEOP, 5, "level_opdeop", "OPDEOP", _("Allowed to (de)op users") }, - { CA_OPDEOPME, 5, "level_opdeopme", "OPDEOPME", _("Allowed to (de)op him/herself") }, - { CA_OWNER, ACCESS_FOUNDER, "level_owner", "OWNER", _("Allowed to use (de)owner users") }, - { CA_OWNERME, 10000, "level_ownerme", "OWNERME", _("Allowed to (de)owner him/herself") }, - { CA_PROTECT, 10000, "level_protect", "PROTECT", _("Allowed to (de)protect users") }, - { CA_PROTECTME, 10, "level_protectme", "PROTECTME", _("Allowed to (de)protect him/herself"), }, - { CA_SAY, 5, "level_say", "SAY", _("Allowed to use SAY and ACT commands") }, - { CA_SIGNKICK, ACCESS_FOUNDER, "level_signkick", "SIGNKICK", _("No signed kick when SIGNKICK LEVEL is used") }, - { CA_SET, 10000, "level_set", "SET", _("Allowed to set channel settings") }, - { CA_TOPIC, 5, "level_topic", "TOPIC", _("Allowed to change channel topics") }, - { CA_UNBAN, 4, "level_unban", "UNBAN", _("Allowed to unban users") }, - { CA_VOICE, 4, "level_voice", "VOICE", _("Allowed to (de)voice users") }, - { CA_VOICEME, 3, "level_voiceme", "VOICEME", _("Allowed to (de)voice him/herself") }, - { CA_SIZE, -1, "", "", "" } -}; - -static void reset_levels(ChannelInfo *ci) -{ - for (int i = 0; defaultLevels[i].priv != CA_SIZE; ++i) - ci->levels[defaultLevels[i].priv] = defaultLevels[i].default_level; -} - -class AccessChanAccess : public ChanAccess -{ - public: - int level; - - AccessChanAccess(AccessProvider *p) : ChanAccess(p) - { - } - - bool Matches(User *u, NickCore *nc) - { - if (u && (Anope::Match(u->nick, this->mask) || Anope::Match(u->GetMask(), this->mask))) - return true; - else if (nc && Anope::Match(nc->display, this->mask)) - return true; - return false; - } - - bool HasPriv(ChannelAccess priv) - { - for (int i = 0; defaultLevels[i].priv != CA_SIZE; ++i) - if (defaultLevels[i].priv == priv) - return DetermineLevel(this) >= this->ci->levels[priv]; - return false; - } - - Anope::string Serialize() - { - return stringify(this->level); - } - - void Unserialize(const Anope::string &data) - { - this->level = convertTo(data); - } - - static int DetermineLevel(ChanAccess *access) - { - if (access->provider->name == "access/access") - { - AccessChanAccess *aaccess = debug_cast(access); - return aaccess->level; - } - else - { - int highest = 1; - for (int i = 0; defaultLevels[i].priv != CA_SIZE; ++i) - if (access->ci->levels[defaultLevels[i].priv] > highest && access->HasPriv(defaultLevels[i].priv)) - highest = access->ci->levels[defaultLevels[i].priv]; - return highest; - } - } -}; - -class AccessAccessProvider : public AccessProvider -{ - public: - AccessAccessProvider(Module *o) : AccessProvider(o, "access/access") - { - } - - ChanAccess *Create() - { - return new AccessChanAccess(this); - } -}; - -class AccessListCallback : public NumberList -{ - protected: - CommandSource &source; - ChannelInfo *ci; - bool SentHeader; - public: - AccessListCallback(CommandSource &_source, ChannelInfo *_ci, const Anope::string &numlist) : NumberList(numlist, false), source(_source), ci(_ci), SentHeader(false) - { - } - - ~AccessListCallback() - { - if (SentHeader) - source.Reply(_("End of access list.")); - else - source.Reply(_("No matching entries on %s access list."), ci->name.c_str()); - } - - virtual void HandleNumber(unsigned Number) - { - if (!Number || Number > ci->GetAccessCount()) - return; - - if (!SentHeader) - { - SentHeader = true; - source.Reply(CHAN_ACCESS_LIST_HEADER, ci->name.c_str()); - } - - DoList(source, ci, Number - 1, ci->GetAccess(Number - 1)); - } - - static void DoList(CommandSource &source, ChannelInfo *ci, unsigned Number, ChanAccess *access) - { - source.Reply(_(" %3d %4d %s"), Number + 1, AccessChanAccess::DetermineLevel(access), access->mask.c_str()); - } -}; - -class AccessViewCallback : public AccessListCallback -{ - public: - AccessViewCallback(CommandSource &_source, ChannelInfo *_ci, const Anope::string &numlist) : AccessListCallback(_source, _ci, numlist) - { - } - - void HandleNumber(unsigned Number) - { - if (!Number || Number > ci->GetAccessCount()) - return; - - if (!SentHeader) - { - SentHeader = true; - source.Reply(CHAN_ACCESS_LIST_HEADER, ci->name.c_str()); - } - - DoList(source, ci, Number - 1, ci->GetAccess(Number - 1)); - } - - static void DoList(CommandSource &source, ChannelInfo *ci, unsigned Number, ChanAccess *access) - { - Anope::string timebuf; - if (ci->c) - for (CUserList::const_iterator cit = ci->c->users.begin(), cit_end = ci->c->users.end(); cit != cit_end; ++cit) - if (access->Matches((*cit)->user, (*cit)->user->Account())) - timebuf = "Now"; - if (timebuf.empty()) - { - if (access->last_seen == 0) - timebuf = "Never"; - else - timebuf = do_strftime(access->last_seen); - } - - source.Reply(CHAN_ACCESS_VIEW_AXS_FORMAT, Number + 1, AccessChanAccess::DetermineLevel(access), access->mask.c_str(), access->creator.c_str(), timebuf.c_str()); - } -}; - -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) - { - if (!ci->HasPriv(source.u, CA_ACCESS_CHANGE) && source.u->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.u, c, ci) << "for user" << (Deleted == 1 ? " " : "s ") << 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) - { - if (!Number || Number > ci->GetAccessCount()) - return; - - User *u = source.u; - - ChanAccess *access = ci->GetAccess(Number - 1); - - AccessGroup u_access = ci->AccessFor(u); - ChanAccess *u_highest = u_access.Highest(); - - if (u_highest ? AccessChanAccess::DetermineLevel(u_highest) : 0 <= AccessChanAccess::DetermineLevel(access) && !u->HasPriv("chanserv/access/modify")) - { - Denied = true; - return; - } - - ++Deleted; - if (!Nicks.empty()) - Nicks += ", " + access->mask; - else - Nicks = access->mask; - - FOREACH_MOD(I_OnAccessDel, OnAccessDel(ci, u, access)); - - ci->EraseAccess(Number - 1); - } -}; - -class CommandCSAccess : public Command -{ - void DoAdd(CommandSource &source, ChannelInfo *ci, const std::vector ¶ms) - { - User *u = source.u; - - Anope::string mask = params[2]; - int level = ACCESS_INVALID; - - try - { - level = convertTo(params[3]); - } - catch (const ConvertException &) { } - - if (!level) - { - source.Reply(_("Access level must be non-zero.")); - return; - } - - AccessGroup u_access = ci->AccessFor(u); - ChanAccess *highest = u_access.Highest(); - int u_level = (highest ? AccessChanAccess::DetermineLevel(highest) : 0); - if (!ci->HasPriv(u, CA_FOUNDER) && level >= u_level && !u->HasPriv("chanserv/access/modify")) - { - source.Reply(ACCESS_DENIED); - return; - } - else if (level <= ACCESS_INVALID || level >= ACCESS_FOUNDER) - { - source.Reply(CHAN_ACCESS_LEVEL_RANGE, ACCESS_INVALID + 1, ACCESS_FOUNDER - 1); - return; - } - - bool override = !ci->HasPriv(u, CA_ACCESS_CHANGE) || level >= u_level; - - for (unsigned i = ci->GetAccessCount(); i > 0; --i) - { - ChanAccess *access = ci->GetAccess(i - 1); - if (mask.equals_ci(access->mask)) - { - /* Don't allow lowering from a level >= u_level */ - if (AccessChanAccess::DetermineLevel(access) >= u_level && !u->HasPriv("chanserv/access/modify")) - { - source.Reply(ACCESS_DENIED); - return; - } - ci->EraseAccess(i - 1); - break; - } - } - - if (ci->GetAccessCount() >= Config->CSAccessMax) - { - source.Reply(_("Sorry, you can only have %d access entries on a channel."), Config->CSAccessMax); - return; - } - - if (mask.find_first_of("!*@") == Anope::string::npos && findnick(mask) == NULL) - mask += "!*@*"; - - service_reference provider("access/access"); - if (!provider) - return; - AccessChanAccess *access = debug_cast(provider->Create()); - access->ci = ci; - access->mask = mask; - access->creator = u->nick; - access->level = level; - access->last_seen = 0; - access->created = Anope::CurTime; - ci->AddAccess(access); - - FOREACH_MOD(I_OnAccessAdd, OnAccessAdd(ci, u, access)); - - Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "ADD " << mask << " (level: " << level << ") as level " << u_level; - source.Reply(_("\002%s\002 added to %s access list at level \002%d\002."), access->mask.c_str(), ci->name.c_str(), level); - - return; - } - - void DoDel(CommandSource &source, ChannelInfo *ci, const std::vector ¶ms) - { - User *u = source.u; - - const Anope::string &mask = params[2]; - - 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) - { - AccessDelCallback list(source, ci, this, mask); - list.Process(); - } - else - { - AccessGroup u_access = ci->AccessFor(u); - ChanAccess *highest = u_access.Highest(); - int u_level = (highest ? AccessChanAccess::DetermineLevel(highest) : 0); - - for (unsigned i = ci->GetAccessCount(); i > 0; --i) - { - ChanAccess *access = ci->GetAccess(i - 1); - if (mask.equals_ci(access->mask)) - { - int access_level = AccessChanAccess::DetermineLevel(access); - if (!access->mask.equals_ci(u->Account()->display) && u_level <= access_level && !u->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 = !ci->HasPriv(u, CA_ACCESS_CHANGE) && !access->mask.equals_ci(u->Account()->display); - Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "DEL " << access->mask; - - FOREACH_MOD(I_OnAccessDel, OnAccessDel(ci, u, access)); - ci->EraseAccess(access); - } - return; - } - } - - source.Reply(_("\002%s\002 not found on %s access list."), mask.c_str(), ci->name.c_str()); - } - - return; - } - - void DoList(CommandSource &source, ChannelInfo *ci, const std::vector ¶ms) - { - 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) - { - AccessListCallback list(source, ci, nick); - list.Process(); - } - else - { - bool SentHeader = false; - - for (unsigned i = 0, end = ci->GetAccessCount(); i < end; ++i) - { - ChanAccess *access = ci->GetAccess(i); - - if (!nick.empty() && !Anope::Match(access->mask, nick)) - continue; - - if (!SentHeader) - { - SentHeader = true; - source.Reply(CHAN_ACCESS_LIST_HEADER, ci->name.c_str()); - } - - AccessListCallback::DoList(source, ci, i, access); - } - - if (SentHeader) - source.Reply(_("End of access list.")); - else - source.Reply(_("No matching entries on %s access list."), ci->name.c_str()); - } - - return; - } - - void DoView(CommandSource &source, ChannelInfo *ci, const std::vector ¶ms) - { - 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) - { - AccessViewCallback list(source, ci, nick); - list.Process(); - } - else - { - bool SentHeader = false; - - for (unsigned i = 0, end = ci->GetAccessCount(); i < end; ++i) - { - ChanAccess *access = ci->GetAccess(i); - - if (!nick.empty() && !Anope::Match(access->mask, nick)) - continue; - - if (!SentHeader) - { - SentHeader = true; - source.Reply(CHAN_ACCESS_LIST_HEADER, ci->name.c_str()); - } - - AccessViewCallback::DoList(source, ci, i, access); - } - - if (SentHeader) - source.Reply(_("End of access list.")); - else - source.Reply(_("No matching entries on %s access list."), ci->name.c_str()); - } - - return; - } - - void DoClear(CommandSource &source, ChannelInfo *ci) - { - User *u = source.u; - - if (!IsFounder(u, ci) && !u->HasPriv("chanserv/access/modify")) - source.Reply(ACCESS_DENIED); - else - { - ci->ClearAccess(); - - FOREACH_MOD(I_OnAccessClear, OnAccessClear(ci, u)); - - source.Reply(_("Channel %s access list has been cleared."), ci->name.c_str()); - - bool override = !IsFounder(u, ci); - Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "CLEAR"; - } - - 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\002")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - const Anope::string &cmd = params[1]; - const Anope::string &nick = params.size() > 2 ? params[2] : ""; - const Anope::string &s = params.size() > 3 ? params[3] : ""; - - User *u = source.u; - ChannelInfo *ci = cs_findchan(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 (u->HasPriv("chanserv/access/modify")) - has_access = true; - else if (is_list && ci->HasPriv(u, CA_ACCESS_LIST)) - has_access = true; - else if (ci->HasPriv(u, CA_ACCESS_CHANGE)) - has_access = true; - else if (is_del) - { - NickAlias *na = findnick(nick); - if (na && na->nc == u->Account()) - 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 (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) - { - 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 nick not on the access list has\n" - "a user level of 0."), source.owner->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" - "must be less than that of the user giving the command, and\n" - "if the \037mask\037 is already on the access list, the current\n" - "access level of that nick must be less than the access level\n" - "of the user giving the command. When a user joins the channel\n" - "the access they receive is from the highest level entry in the\n" - "access list.")); - 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(_("\002User access levels\002\n" - " \n" - "By default, the following access levels are defined:\n" - " \n" - " \002Founder\002 Full access to %s functions; automatic\n" - " opping upon entering channel. Note\n" - " that only one person may have founder\n" - " status (it cannot be given using the\n" - " \002ACCESS\002 command).\n" - " \002 10\002 Access to AKICK command; automatic opping.\n" - " \002 5\002 Automatic opping.\n" - " \002 3\002 Automatic voicing.\n" - " \002 0\002 No special privileges; can be opped by other\n" - " ops (unless \002secure-ops\002 is set).\n" - " \002 <0\002 May not be opped.\n" - " \n" - "These levels may be changed, or new ones added, using the\n" - "\002LEVELS\002 command; type \002%s%s HELP LEVELS\002 for\n" - "information."), source.owner->nick.c_str(), Config->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str()); - return true; - } -}; - -class CommandCSLevels : public Command -{ - int levelinfo_maxwidth; - - void DoSet(CommandSource &source, ChannelInfo *ci, const std::vector ¶ms) - { - User *u = source.u; - - 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(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 - { - for (int i = 0; defaultLevels[i].priv != CA_SIZE; ++i) - { - AccessLevels &l = defaultLevels[i]; - - if (what.equals_ci(l.name)) - { - ci->levels[l.priv] = level; - FOREACH_MOD(I_OnLevelChange, OnLevelChange(u, ci, i, level)); - - bool override = !ci->HasPriv(u, CA_FOUNDER); - Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "SET " << l.name << " to " << level; - - if (level == ACCESS_FOUNDER) - source.Reply(_("Level for %s on channel %s changed to founder only."), l.name.c_str(), ci->name.c_str()); - else - source.Reply(_("Level for \002%s\002 on channel %s changed to \002%d\002."), l.name.c_str(), ci->name.c_str(), level); - 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->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str()); - } - - return; - } - - void DoDisable(CommandSource &source, ChannelInfo *ci, const std::vector ¶ms) - { - User *u = source.u; - - const Anope::string &what = params[2]; - - /* Don't allow disabling of the founder level. It would be hard to change it back if you dont have access to use this command */ - if (!what.equals_ci("FOUNDER")) - for (int i = 0; defaultLevels[i].priv != CA_SIZE; ++i) - { - AccessLevels &l = defaultLevels[i]; - - if (what.equals_ci(l.name)) - { - ci->levels[l.priv] = ACCESS_INVALID; - FOREACH_MOD(I_OnLevelChange, OnLevelChange(u, ci, i, l.priv)); - - bool override = !ci->HasPriv(u, CA_FOUNDER); - Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "DISABLE " << l.name; - - source.Reply(_("\002%s\002 disabled on channel %s."), l.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->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str()); - - return; - } - - void DoList(CommandSource &source, ChannelInfo *ci) - { - source.Reply(_("Access level settings for channel %s:"), ci->name.c_str()); - - if (!levelinfo_maxwidth) - for (int i = 0; defaultLevels[i].priv != CA_SIZE; ++i) - { - AccessLevels &l = defaultLevels[i]; - - int len = l.name.length(); - if (len > levelinfo_maxwidth) - levelinfo_maxwidth = len; - } - - for (int i = 0; defaultLevels[i].priv != CA_SIZE; ++i) - { - AccessLevels &l = defaultLevels[i]; - int j = ci->levels[l.priv]; - - if (j == ACCESS_INVALID) - source.Reply(_(" %-*s (disabled)"), levelinfo_maxwidth, l.name.c_str()); - else if (j == ACCESS_FOUNDER) - source.Reply(_(" %-*s (founder only)"), levelinfo_maxwidth, l.name.c_str()); - else - source.Reply(_(" %-*s %d"), levelinfo_maxwidth, l.name.c_str(), j); - } - - return; - } - - void DoReset(CommandSource &source, ChannelInfo *ci) - { - User *u = source.u; - - reset_levels(ci); - FOREACH_MOD(I_OnLevelChange, OnLevelChange(u, ci, -1, 0)); - - bool override = !ci->HasPriv(u, CA_FOUNDER); - Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "RESET"; - - 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), levelinfo_maxwidth(0) - { - 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 ¶ms) - { - const Anope::string &cmd = params[1]; - const Anope::string &what = params.size() > 2 ? params[2] : ""; - const Anope::string &s = params.size() > 3 ? params[3] : ""; - - User *u = source.u; - - ChannelInfo *ci = cs_findchan(params[0]); - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - /* 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 (!ci->HasPriv(u, CA_FOUNDER) && !u->HasPriv("chanserv/access/modify")) - source.Reply(ACCESS_DENIED); - 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) - { - if (subcommand.equals_ci("DESC")) - { - source.Reply(_("The following feature/function names are understood.")); - if (!levelinfo_maxwidth) - for (int i = 0; defaultLevels[i].priv != CA_SIZE; ++i) - { - AccessLevels &l = defaultLevels[i]; - - int len = l.name.length(); - if (len > levelinfo_maxwidth) - levelinfo_maxwidth = len; - } - for (int i = 0; defaultLevels[i].priv != CA_SIZE; ++i) - { - AccessLevels &l = defaultLevels[i]; - source.Reply(_(" %-*s %s"), levelinfo_maxwidth, l.name.c_str(), translate(source.u, l.desc.c_str())); - } - } - 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).\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 (see\n" - "\002HELP ACCESS LEVELS\002).\n" - " \n" - "For a list of the features and functions whose levels can be\n" - "set, see \002HELP LEVELS DESC\002."), source.owner->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, CORE), - accessprovider(this), commandcsaccess(this), commandcslevels(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&accessprovider); - ModuleManager::RegisterService(&commandcsaccess); - ModuleManager::RegisterService(&commandcslevels); - - Implementation i[] = { I_OnReload, I_OnChanRegistered }; - ModuleManager::Attach(i, this, 2); - - this->OnReload(); - } - - void OnReload() - { - ConfigReader config; - - for (int i = 0; defaultLevels[i].priv != CA_SIZE; ++i) - { - AccessLevels &l = defaultLevels[i]; - - const Anope::string &value = config.ReadValue("chanserv", l.config_name, "", 0); - if (value.equals_ci("founder")) - l.default_level = ACCESS_FOUNDER; - else - l.default_level = config.ReadInteger("chanserv", l.config_name, 0, false); - } - } - - void OnChanRegistered(ChannelInfo *ci) - { - reset_levels(ci); - } -}; - -MODULE_INIT(CSAccess) diff --git a/modules/core/cs_akick.cpp b/modules/core/cs_akick.cpp deleted file mode 100644 index 437bf5572..000000000 --- a/modules/core/cs_akick.cpp +++ /dev/null @@ -1,564 +0,0 @@ -/* ChanServ core functions - * - * (C) 2003-2011 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 void split_usermask(const Anope::string &mask, Anope::string &nick, Anope::string &user, Anope::string &host) -{ - size_t ex = mask.find('!'), at = mask.find('@', ex == Anope::string::npos ? 0 : ex + 1); - if (ex == Anope::string::npos) - { - if (at == Anope::string::npos) - { - nick = mask; - user = host = "*"; - } - else - { - nick = "*"; - user = mask.substr(0, at); - host = mask.substr(at + 1); - } - } - else - { - nick = mask.substr(0, ex); - if (at == Anope::string::npos) - { - user = mask.substr(ex + 1); - host = "*"; - } - else - { - user = mask.substr(ex + 1, at - ex - 1); - host = mask.substr(at + 1); - } - } -} - -class AkickListCallback : public NumberList -{ - protected: - CommandSource &source; - ChannelInfo *ci; - bool SentHeader; - public: - AkickListCallback(CommandSource &_source, ChannelInfo *_ci, const Anope::string &numlist) : NumberList(numlist, false), source(_source), ci(_ci), SentHeader(false) - { - } - - ~AkickListCallback() - { - if (!SentHeader) - source.Reply(_("No matching entries on %s autokick list."), ci->name.c_str()); - } - - virtual void HandleNumber(unsigned Number) - { - if (!Number || Number > ci->GetAkickCount()) - return; - - if (!SentHeader) - { - SentHeader = true; - source.Reply(_("Autokick list for %s:"), ci->name.c_str()); - } - - DoList(source, ci, Number - 1, ci->GetAkick(Number - 1)); - } - - static void DoList(CommandSource &source, ChannelInfo *ci, unsigned index, AutoKick *akick) - { - source.Reply(_(" %3d %s (%s)"), index + 1, akick->HasFlag(AK_ISNICK) ? akick->nc->display.c_str() : akick->mask.c_str(), !akick->reason.empty() ? akick->reason.c_str() : NO_REASON); - } -}; - -class AkickViewCallback : public AkickListCallback -{ - public: - AkickViewCallback(CommandSource &_source, ChannelInfo *_ci, const Anope::string &numlist) : AkickListCallback(_source, _ci, numlist) - { - } - - void HandleNumber(unsigned Number) - { - if (!Number || Number > ci->GetAkickCount()) - return; - - if (!SentHeader) - { - SentHeader = true; - source.Reply(_("Autokick list for %s:"), ci->name.c_str()); - } - - DoList(source, ci, Number - 1, ci->GetAkick(Number - 1)); - } - - static void DoList(CommandSource &source, ChannelInfo *ci, unsigned index, AutoKick *akick) - { - Anope::string timebuf; - - if (akick->addtime) - timebuf = do_strftime(akick->addtime); - else - timebuf = UNKNOWN; - - source.Reply(CHAN_AKICK_VIEW_FORMAT, index + 1, akick->HasFlag(AK_ISNICK) ? akick->nc->display.c_str() : akick->mask.c_str(), !akick->creator.empty() ? akick->creator.c_str() : UNKNOWN, timebuf.c_str(), !akick->reason.empty() ? akick->reason.c_str() : _(NO_REASON)); - - if (akick->last_used) - source.Reply(_(" Last used %s"), do_strftime(akick->last_used).c_str()); - } -}; - -class AkickDelCallback : public NumberList -{ - CommandSource &source; - ChannelInfo *ci; - Command *c; - unsigned Deleted; - public: - AkickDelCallback(CommandSource &_source, ChannelInfo *_ci, Command *_c, const Anope::string &list) : NumberList(list, true), source(_source), ci(_ci), c(_c), Deleted(0) - { - } - - ~AkickDelCallback() - { - User *u = source.u; - bool override = !ci->HasPriv(u, CA_AKICK); - Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, c, ci) << "DEL on " << Deleted << " users"; - - 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) - { - if (!Number || Number > ci->GetAkickCount()) - return; - - ++Deleted; - ci->EraseAkick(Number - 1); - } -}; - -class CommandCSAKick : public Command -{ - void DoAdd(CommandSource &source, ChannelInfo *ci, const std::vector ¶ms) - { - User *u = source.u; - - Anope::string mask = params[2]; - Anope::string reason = params.size() > 3 ? params[3] : ""; - NickAlias *na = findnick(mask); - NickCore *nc = NULL; - AutoKick *akick; - - if (!na) - { - Anope::string nick, user, host; - - split_usermask(mask, nick, user, host); - mask = nick + "!" + user + "@" + host; - } - else - nc = na->nc; - - /* Check excepts BEFORE we get this far */ - if (ci->c) - { - std::pair modes = ci->c->GetModeList(CMODE_EXCEPT); - for (; modes.first != modes.second; ++modes.first) - { - if (Anope::Match(modes.first->second, mask)) - { - source.Reply(CHAN_EXCEPTED, mask.c_str(), ci->name.c_str()); - return; - } - } - } - - /* Check whether target nick has equal/higher access - * or whether the mask matches a user with higher/equal access - Viper */ - if (ci->HasFlag(CI_PEACE) && nc) - { - AccessGroup nc_access = ci->AccessFor(nc), u_access = ci->AccessFor(u); - if (nc == ci->GetFounder() || nc_access >= u_access) - { - source.Reply(ACCESS_DENIED); - return; - } - } - else if (ci->HasFlag(CI_PEACE)) - { - /* Match against all currently online users with equal or - * higher access. - Viper */ - for (Anope::insensitive_map::iterator it = UserListByNick.begin(), it_end = UserListByNick.end(); it != it_end; ++it) - { - User *u2 = it->second; - - AccessGroup nc_access = ci->AccessFor(nc), u_access = ci->AccessFor(u); - Entry entry_mask(CMODE_BEGIN, mask); - - if ((ci->HasPriv(u2, CA_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 = ci->AccessFor(u); - 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->HasFlag(AK_ISNICK) ? akick->nc == nc : mask.equals_ci(akick->mask)) - { - source.Reply(_("\002%s\002 already exists on %s autokick list."), akick->HasFlag(AK_ISNICK) ? akick->nc->display.c_str() : akick->mask.c_str(), ci->name.c_str()); - return; - } - } - - if (ci->GetAkickCount() >= Config->CSAutokickMax) - { - source.Reply(_("Sorry, you can only have %d autokick masks on a channel."), Config->CSAutokickMax); - return; - } - - if (nc) - akick = ci->AddAkick(u->nick, nc, reason); - else - akick = ci->AddAkick(u->nick, mask, reason); - - bool override = !ci->HasPriv(u, CA_AKICK); - Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "ADD " << mask << ": " << reason; - - FOREACH_MOD(I_OnAkickAdd, OnAkickAdd(u, 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 ¶ms) - { - User *u = source.u; - - const Anope::string &mask = params[2]; - AutoKick *akick; - 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) - { - AkickDelCallback list(source, ci, this, mask); - list.Process(); - } - else - { - NickAlias *na = findnick(mask); - NickCore *nc = na ? na->nc : NULL; - - for (i = 0, end = ci->GetAkickCount(); i < end; ++i) - { - akick = ci->GetAkick(i); - - if ((akick->HasFlag(AK_ISNICK) && akick->nc == nc) || (!akick->HasFlag(AK_ISNICK) && 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 = !ci->HasPriv(u, CA_AKICK); - Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "DEL " << mask; - - ci->EraseAkick(i); - - source.Reply(_("\002%s\002 deleted from %s autokick list."), mask.c_str(), ci->name.c_str()); - } - } - - void DoList(CommandSource &source, ChannelInfo *ci, const std::vector ¶ms) - { - User *u = source.u; - - const Anope::string &mask = params.size() > 2 ? params[2] : ""; - - bool override = !ci->HasPriv(u, CA_AKICK); - Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "LIST"; - - if (!ci->GetAkickCount()) - { - source.Reply(_("%s autokick list is empty."), ci->name.c_str()); - return; - } - - if (!mask.empty() && isdigit(mask[0]) && mask.find_first_not_of("1234567890,-") == Anope::string::npos) - { - AkickListCallback list(source, ci, mask); - list.Process(); - } - else - { - bool SentHeader = false; - - for (unsigned i = 0, end = ci->GetAkickCount(); i < end; ++i) - { - AutoKick *akick = ci->GetAkick(i); - - if (!mask.empty()) - { - if (!akick->HasFlag(AK_ISNICK) && !Anope::Match(akick->mask, mask)) - continue; - if (akick->HasFlag(AK_ISNICK) && !Anope::Match(akick->nc->display, mask)) - continue; - } - - if (!SentHeader) - { - SentHeader = true; - source.Reply(_("Autokick list for %s:"), ci->name.c_str()); - } - - AkickListCallback::DoList(source, ci, i, akick); - } - - if (!SentHeader) - source.Reply(_("No matching entries on %s autokick list."), ci->name.c_str()); - } - } - - void DoView(CommandSource &source, ChannelInfo *ci, const std::vector ¶ms) - { - User *u = source.u; - - const Anope::string &mask = params.size() > 2 ? params[2] : ""; - - bool override = !ci->HasPriv(u, CA_AKICK); - Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "VIEW"; - - if (!ci->GetAkickCount()) - { - source.Reply(_("%s autokick list is empty."), ci->name.c_str()); - return; - } - - if (!mask.empty() && isdigit(mask[0]) && mask.find_first_not_of("1234567890,-") == Anope::string::npos) - { - AkickViewCallback list(source, ci, mask); - list.Process(); - } - else - { - bool SentHeader = false; - - for (unsigned i = 0, end = ci->GetAkickCount(); i < end; ++i) - { - AutoKick *akick = ci->GetAkick(i); - - if (!mask.empty()) - { - if (!akick->HasFlag(AK_ISNICK) && !Anope::Match(akick->mask, mask)) - continue; - if (akick->HasFlag(AK_ISNICK) && !Anope::Match(akick->nc->display, mask)) - continue; - } - - if (!SentHeader) - { - SentHeader = true; - source.Reply(_("Autokick list for %s:"), ci->name.c_str()); - } - - AkickViewCallback::DoList(source, ci, i, akick); - } - - if (!SentHeader) - source.Reply(_("No matching entries on %s autokick list."), ci->name.c_str()); - } - } - - void DoEnforce(CommandSource &source, ChannelInfo *ci) - { - User *u = source.u; - Channel *c = ci->c; - int count = 0; - - if (!c) - { - source.Reply(CHAN_X_NOT_IN_USE, ci->name.c_str()); - return; - } - - for (CUserList::iterator it = c->users.begin(), it_end = c->users.end(); it != it_end; ) - { - UserContainer *uc = *it++; - - if (ci->CheckKick(uc->user)) - ++count; - } - - bool override = !ci->HasPriv(u, CA_AKICK); - Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, 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) - { - User *u = source.u; - bool override = !ci->HasPriv(u, CA_AKICK); - Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "CLEAR"; - - 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(_("\002AKICK \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 ¶ms) - { - Anope::string chan = params[0]; - Anope::string cmd = params[1]; - Anope::string mask = params.size() > 2 ? params[2] : ""; - - User *u = source.u; - - ChannelInfo *ci = cs_findchan(params[0]); - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - if (mask.empty() && (cmd.equals_ci("ADD") || cmd.equals_ci("DEL"))) - this->OnSyntaxError(source, cmd); - else if (!ci->HasPriv(u, CA_AKICK) && !u->HasPriv("chanserv/access/modify")) - source.Reply(ACCESS_DENIED); - else if (!cmd.equals_ci("LIST") && !cmd.equals_ci("VIEW") && !cmd.equals_ci("ENFORCE") && 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) - { - 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 \"You have been\n" - "banned from the channel\".\n" - "When akicking a \037registered nick\037 the nickserv 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.owner->nick.c_str()); - 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\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.owner->nick.c_str()); - return true; - } -}; - -class CSAKick : public Module -{ - CommandCSAKick commandcsakick; - - public: - CSAKick(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandcsakick(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandcsakick); - } -}; - -MODULE_INIT(CSAKick) diff --git a/modules/core/cs_ban.cpp b/modules/core/cs_ban.cpp deleted file mode 100644 index 759649662..000000000 --- a/modules/core/cs_ban.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* ChanServ core functions - * - * (C) 2003-2011 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 CommandCSBan : public Command -{ - public: - CommandCSBan(Module *creator) : Command(creator, "chanserv/ban", 2, 3) - { - this->SetDesc(_("Bans a selected nick on a channel")); - this->SetSyntax(_("\037#channel\037 \037nick\037 [\037reason\037]")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - const Anope::string &chan = params[0]; - const Anope::string &target = params[1]; - const Anope::string &reason = params.size() > 2 ? params[2] : "Requested"; - - ChannelInfo *ci = cs_findchan(params[0]); - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - User *u = source.u; - Channel *c = ci->c; - bool is_same = target.equals_ci(u->nick); - User *u2 = is_same ? u : finduser(target); - - AccessGroup u_access = ci->AccessFor(u), u2_access = ci->AccessFor(u2); - - if (!c) - source.Reply(CHAN_X_NOT_IN_USE, chan.c_str()); - else if (!u2) - source.Reply(NICK_X_NOT_IN_USE, target.c_str()); - else if (!ci->HasPriv(u, CA_BAN)) - source.Reply(ACCESS_DENIED); - else if (!is_same && ci->HasFlag(CI_PEACE) && u2_access >= u_access) - source.Reply(ACCESS_DENIED); - /* - * Dont ban/kick the user on channels where he is excepted - * to prevent services <-> server wars. - */ - else if (matches_list(ci->c, u2, CMODE_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; - get_idealban(ci, u2, mask); - - // XXX need a way to detect if someone is overriding - Log(LOG_COMMAND, u, this, ci) << "for " << mask; - - c->SetMode(NULL, CMODE_BAN, mask); - - /* We still allow host banning while not allowing to kick */ - if (!c->FindUser(u2)) - return; - - if (ci->HasFlag(CI_SIGNKICK) || (ci->HasFlag(CI_SIGNKICK_LEVEL) && !ci->HasPriv(u, CA_SIGNKICK))) - c->Kick(ci->WhoSends(), u2, "%s (%s)", reason.c_str(), u->nick.c_str()); - else - c->Kick(ci->WhoSends(), u2, "%s", reason.c_str()); - } - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Bans a selected nick on a channel.\n" - " \n" - "By default, limited to AOPs or those with level 5 access \n" - "and above on the channel.")); - return true; - } -}; - -class CSBan : public Module -{ - CommandCSBan commandcsban; - - public: - CSBan(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), commandcsban(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandcsban); - } -}; - -MODULE_INIT(CSBan) diff --git a/modules/core/cs_clearusers.cpp b/modules/core/cs_clearusers.cpp deleted file mode 100644 index 18dc75479..000000000 --- a/modules/core/cs_clearusers.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/* ChanServ core functions - * - * (C) 2003-2011 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 CommandCSClearUsers : public Command -{ - public: - CommandCSClearUsers(Module *creator) : Command(creator, "chanserv/clearusers", 1, 1) - { - this->SetDesc(_("Kicks all users on a channel")); - this->SetSyntax(_("\037channel\037")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - const Anope::string &chan = params[0]; - - User *u = source.u; - Channel *c = findchan(chan); - Anope::string modebuf; - - if (!c) - source.Reply(CHAN_X_NOT_IN_USE, chan.c_str()); - else if (!c->ci) - source.Reply(CHAN_X_NOT_REGISTERED, c->name.c_str()); - else if (!c->ci->HasPriv(u, CA_FOUNDER)) - { - source.Reply(ACCESS_DENIED); - return; - } - - Anope::string buf = "CLEARUSERS command from " + u->nick + " (" + u->Account()->display + ")"; - - for (CUserList::iterator it = c->users.begin(), it_end = c->users.end(); it != it_end; ) - { - UserContainer *uc = *it++; - - c->Kick(NULL, uc->user, "%s", buf.c_str()); - } - - source.Reply(_("All users have been kicked from \2%s\2."), chan.c_str()); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Tells %s to clear (kick) all users certain settings on a channel." - " \n" - "By default, limited to those with founder access on the\n" - "channel."), source.owner->nick.c_str()); - return true; - } -}; - -class CSClearUsers : public Module -{ - CommandCSClearUsers commandcsclearusers; - - public: - CSClearUsers(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandcsclearusers(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandcsclearusers); - } -}; - -MODULE_INIT(CSClearUsers) diff --git a/modules/core/cs_clone.cpp b/modules/core/cs_clone.cpp deleted file mode 100644 index 5ede6a9e4..000000000 --- a/modules/core/cs_clone.cpp +++ /dev/null @@ -1,186 +0,0 @@ -/* ChanServ core functions - * - * (C) 2003-2011 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 CommandCSClone : public Command -{ -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")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - const Anope::string &channel = params[0]; - const Anope::string &target = params[1]; - Anope::string what = params.size() > 2 ? params[2] : ""; - - User *u = source.u; - ChannelInfo *ci = cs_findchan(params[0]); - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - if (!ci->HasPriv(u, CA_SET)) - { - source.Reply(ACCESS_DENIED); - return; - } - ChannelInfo *target_ci = cs_findchan(target); - if (!target_ci) - { - source.Reply(CHAN_X_NOT_REGISTERED, target.c_str()); - return; - } - if (!IsFounder(u, ci) || !IsFounder(u, target_ci)) - { - source.Reply(ACCESS_DENIED); - return; - } - - if (Config->CSMaxReg && u->Account()->channelcount >= Config->CSMaxReg && !u->HasPriv("chanserv/no-register-limit")) - { - source.Reply(u->Account()->channelcount > Config->CSMaxReg ? CHAN_EXCEEDED_CHANNEL_LIMIT : _(CHAN_REACHED_CHANNEL_LIMIT), Config->CSMaxReg); - return; - } - - if (what.equals_ci("ALL")) - what.clear(); - - if (what.empty()) - { - delete target_ci; - target_ci = new ChannelInfo(*ci); - target_ci->name = target; - RegisteredChannelList[target_ci->name] = target_ci; - target_ci->c = findchan(target_ci->name); - if (target_ci->c) - { - target_ci->c->ci = target_ci; - - check_modes(target_ci->c); - - ChannelMode *cm; - if (u->FindChannel(target_ci->c) != NULL) - { - /* On most ircds you do not receive the admin/owner mode till its registered */ - if ((cm = ModeManager::FindChannelModeByName(CMODE_OWNER))) - target_ci->c->SetMode(NULL, cm, u->nick); - else if ((cm = ModeManager::FindChannelModeByName(CMODE_PROTECT))) - target_ci->c->RemoveMode(NULL, cm, u->nick); - } - - /* Mark the channel as persistant */ - if (target_ci->c->HasMode(CMODE_PERM)) - target_ci->SetFlag(CI_PERSIST); - /* Persist may be in def cflags, set it here */ - else if (target_ci->HasFlag(CI_PERSIST) && (cm = ModeManager::FindChannelModeByName(CMODE_PERM))) - target_ci->c->SetMode(NULL, CMODE_PERM); - - if (target_ci->bi && target_ci->c->FindUser(target_ci->bi) == NULL) - target_ci->bi->Join(target_ci->c); - } - - 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.owner->nick; - - FOREACH_MOD(I_OnChanRegistered, OnChanRegistered(target_ci)); - - source.Reply(_("All settings from \002%s\002 have been transferred to \002%s\002"), channel.c_str(), target.c_str()); - } - else if (what.equals_ci("ACCESS")) - { - /*target_ci->ClearAccess(); - for (unsigned i = 0; i < ci->GetAccessCount(); ++i) - { - ChanAccess *access = ci->GetAccess(i); - target_ci->AddAccess(access->GetMask(), access->level, access->creator, access->last_seen); - } - - source.Reply(_("All access entries from \002%s\002 have been transferred to \002%s\002"), channel.c_str(), target.c_str()); - XXX - */ - } - else if (what.equals_ci("AKICK")) - { - target_ci->ClearAkick(); - for (unsigned i = 0; i < ci->GetAkickCount(); ++i) - { - AutoKick *akick = ci->GetAkick(i); - if (akick->HasFlag(AK_ISNICK)) - 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 transferred to \002%s\002"), channel.c_str(), target.c_str()); - } - else if (what.equals_ci("BADWORDS")) - { - target_ci->ClearBadWords(); - for (unsigned i = 0; i < ci->GetBadWordCount(); ++i) - { - BadWord *bw = ci->GetBadWord(i); - target_ci->AddBadWord(bw->word, bw->type); - } - - source.Reply(_("All badword entries from \002%s\002 have been transferred to \002%s\002"), channel.c_str(), target.c_str()); - } - else - { - this->OnSyntaxError(source, ""); - return; - } - - Log(LOG_COMMAND, u, this, ci) << "to clone it to " << target_ci->name; - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Copies all settings, access, akicks, etc from channel to the\n" - "target channel. If access, akick, or badwords is specified then only\n" - "the respective settings are transferred. You must have founder level\n" - "access to \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, CORE), commandcsclone(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandcsclone); - } -}; - -MODULE_INIT(CSClone) diff --git a/modules/core/cs_drop.cpp b/modules/core/cs_drop.cpp deleted file mode 100644 index eb01875db..000000000 --- a/modules/core/cs_drop.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/* ChanServ core functions - * - * (C) 2003-2011 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, 1) - { - this->SetDesc(_("Cancel the registration of a channel")); - this->SetSyntax(_("\037channel\037")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - const Anope::string &chan = params[0]; - - User *u = source.u; - - if (readonly) - { - source.Reply(_("Sorry, channel de-registration is temporarily disabled.")); // XXX: READ_ONLY_MODE? - return; - } - - ChannelInfo *ci = cs_findchan(params[0]); - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - ci = cs_findchan(chan); - - if (ci->HasFlag(CI_SUSPENDED) && !u->HasCommand("chanserv/chanserv/drop")) - { - source.Reply(CHAN_X_SUSPENDED, chan.c_str()); - return; - } - - if ((ci->HasFlag(CI_SECUREFOUNDER) ? !IsFounder(u, ci) : !ci->HasPriv(u, CA_FOUNDER)) && !u->HasCommand("chanserv/chanserv/drop")) - { - source.Reply(ACCESS_DENIED); - return; - } - - if (ci->c && ModeManager::FindChannelModeByName(CMODE_REGISTERED)) - ci->c->RemoveMode(NULL, CMODE_REGISTERED, "", false); - - bool override = (ci->HasFlag(CI_SECUREFOUNDER) ? !IsFounder(u, ci) : !ci->HasPriv(u, CA_FOUNDER)); - Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "founder: " << (ci->GetFounder() ? ci->GetFounder()->display : "none"); - - delete ci; - - source.Reply(_("Channel \002%s\002 has been dropped."), chan.c_str()); - - FOREACH_MOD(I_OnChanDrop, OnChanDrop(chan)); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - User *u = source.u; - this->SendSyntax(source); - source.Reply(" "); - if (u->IsServicesOper()) - source.Reply(_("Unregisters the named channel. Only \002Services Operators\002\n" - "can drop a channel for which they have not identified.")); - else - source.Reply(_("Unregisters the named channel. Can only be used by\n" - "\002channel founder\002.")); - - return true; - } -}; - -class CSDrop : public Module -{ - CommandCSDrop commandcsdrop; - - public: - CSDrop(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), commandcsdrop(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandcsdrop); - } -}; - -MODULE_INIT(CSDrop) diff --git a/modules/core/cs_flags.cpp b/modules/core/cs_flags.cpp deleted file mode 100644 index 48f1a22f1..000000000 --- a/modules/core/cs_flags.cpp +++ /dev/null @@ -1,455 +0,0 @@ -/* ChanServ core functions - * - * (C) 2003-2011 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 struct FlagLevels -{ - ChannelAccess priv; - char default_char; - Anope::string config_name; - Anope::string desc; -} flagLevels[] = { - { CA_ACCESS_CHANGE, 'f', "flag_change", _("Allowed to modify the access list") }, - { CA_ACCESS_LIST, 'l', "flag_list", _("Allowed to view the access list") }, - { CA_AKICK, 'K', "flag_akick", _("Allowed to use AKICK command") }, - { CA_ASSIGN, 's', "flag_assign", _("Allowed to assign/unassign a bot") }, - { CA_AUTOHALFOP, 'H', "flag_autohalfop", _("Automatic mode +h") }, - { CA_AUTOOP, 'O', "flag_autoop", _("Automatic channel operator status") }, - { CA_AUTOOWNER, 'Q', "flag_autoowner", _("Automatic mode +q") }, - { CA_AUTOPROTECT, 'A', "flag_autoprotect", _("Automatic mode +a") }, - { CA_AUTOVOICE, 'V', "flag_autovoice", _("Automatic mode +v") }, - { CA_BADWORDS, 'k', "flag_badwords", _("Allowed to modify channel badwords list") }, - { CA_BAN, 'b', "flag_ban", _("Allowed to use ban users") }, - { CA_FANTASIA, 'c', "flag_fantasia", _("Allowed to use fantasy commands") }, - { CA_FOUNDER, 'F', "flag_founder", _("Allowed to issue commands restricted to channel founders") }, - { CA_GETKEY, 'G', "flag_getkey", _("Allowed to use GETKEY command") }, - { CA_GREET, 'g', "flag_greet", _("Greet message displayed") }, - { CA_HALFOP, 'h', "flag_halfop", _("Allowed to (de)halfop users") }, - { CA_HALFOPME, 'h', "flag_halfopme", _("Allowed to (de)halfop him/herself") }, - { CA_INFO, 'I', "flag_info", _("Allowed to use INFO command with ALL option") }, - { CA_INVITE, 'i', "flag_invite", _("Allowed to use the INVITE command") }, - { CA_KICK, 'k', "flag_kick", _("Allowed to use the KICK command") }, - { CA_MEMO, 'm', "flag_memo", _("Allowed to read channel memos") }, - { CA_MODE, 's', "flag_mode", _("Allowed to change channel modes") }, - { CA_NOKICK, 'N', "flag_nokick", _("Prevents users being kicked by Services") }, - { CA_OPDEOP, 'o', "flag_opdeop", _("Allowed to (de)op users") }, - { CA_OPDEOPME, 'o', "flag_opdeopme", _("Allowed to (de)op him/herself") }, - { CA_OWNER, 'q', "flag_owner", _("Allowed to use (de)owner users") }, - { CA_OWNERME, 'q', "flag_ownerme", _("Allowed to (de)owner him/herself") }, - { CA_PROTECT, 'a', "flag_protect", _("Allowed to (de)protect users") }, - { CA_PROTECTME, 'a', "flag_protectme", _("Allowed to (de)protect him/herself"), }, - { CA_SAY, 'B', "flag_say", _("Allowed to use SAY and ACT commands") }, - { CA_SET, 's', "flag_set", _("Allowed to set channel settings") }, - { CA_SIGNKICK, 'K', "flag_signkick", _("Prevents kicks from being signed") }, - { CA_TOPIC, 't', "flag_topic", _("Allowed to change channel topics") }, - { CA_UNBAN, 'u', "flag_unban", _("Allowed to unban users") }, - { CA_VOICE, 'v', "flag_voice", _("Allowed to (de)voice users") }, - { CA_VOICEME, 'v', "flag_voiceme", _("Allowed to (de)voice him/herself") }, - { CA_SIZE, -1, "", "" } -}; - -class FlagsChanAccess : public ChanAccess -{ - public: - std::set flags; - - FlagsChanAccess(AccessProvider *p) : ChanAccess(p) - { - } - - bool Matches(User *u, NickCore *nc) - { - if (u && (Anope::Match(u->nick, this->mask) || Anope::Match(u->GetMask(), this->mask))) - return true; - else if (nc && Anope::Match(nc->display, this->mask)) - return true; - return false; - } - - bool HasPriv(ChannelAccess priv) - { - for (int i = 0; flagLevels[i].priv != CA_SIZE; ++i) - if (flagLevels[i].priv == priv) - return this->flags.count(flagLevels[i].default_char); - - return false; - } - - Anope::string Serialize() - { - return Anope::string(this->flags.begin(), this->flags.end()); - } - - void Unserialize(const Anope::string &data) - { - for (unsigned i = data.length(); i > 0; --i) - this->flags.insert(data[i - 1]); - } - - static Anope::string DetermineFlags(ChanAccess *access) - { - if (access->provider->name == "access/flags") - return access->Serialize(); - - std::set buffer; - - for (int i = 0; flagLevels[i].priv != CA_SIZE; ++i) - { - FlagLevels &l = flagLevels[i]; - - if (access->HasPriv(l.priv)) - buffer.insert(l.default_char); - } - - return Anope::string(buffer.begin(), buffer.end()); - } -}; - -class FlagsAccessProvider : public AccessProvider -{ - public: - FlagsAccessProvider(Module *o) : AccessProvider(o, "access/flags") - { - } - - ChanAccess *Create() - { - return new FlagsChanAccess(this); - } -}; - -class CommandCSFlags : public Command -{ - void DoModify(CommandSource &source, ChannelInfo *ci, const std::vector ¶ms) - { - User *u = source.u; - - Anope::string mask = params.size() > 2 ? params[2] : ""; - Anope::string flags = params.size() > 3 ? params[3] : ""; - - if (flags.empty()) - { - this->OnSyntaxError(source, ""); - return; - } - - AccessGroup u_access = ci->AccessFor(u); - - if (mask.find_first_of("!*@") == Anope::string::npos && findnick(mask) == NULL) - mask += "!*@*"; - - ChanAccess *current = NULL; - std::set current_flags; - for (unsigned i = ci->GetAccessCount(); i > 0; --i) - { - ChanAccess *access = ci->GetAccess(i - 1); - if (mask.equals_ci(access->mask)) - { - 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; - } - } - - if (ci->GetAccessCount() >= Config->CSAccessMax) - { - source.Reply(_("Sorry, you can only have %d access entries on a channel."), Config->CSAccessMax); - return; - } - - bool override = false; - int add = -1; - for (size_t i = 0; i < flags.length(); ++i) - { - char f = flags[i]; - switch (f) - { - case '+': - add = 1; - break; - case '-': - add = 0; - break; - case '*': - if (add == -1) - break; - for (int j = 0; flagLevels[j].priv != CA_SIZE; ++j) - { - FlagLevels &l = flagLevels[j]; - if (!u_access.HasPriv(l.priv)) - { - if (u->HasPriv("chanserv/access/modify")) - override = true; - else - continue; - } - if (add == 1) - current_flags.insert(f); - else if (add == 0) - current_flags.erase(f); - } - break; - default: - if (add == -1) - break; - for (int j = 0; flagLevels[j].priv != CA_SIZE; ++j) - { - FlagLevels &l = flagLevels[j]; - if (f != l.default_char) - continue; - else if (!u_access.HasPriv(l.priv)) - { - if (u->HasPriv("chanserv/access/modify")) - override = true; - else - { - source.Reply(_("You can not set the \002%c\002 flag."), f); - continue; - } - } - if (add == 1) - current_flags.insert(f); - else if (add == 0) - current_flags.erase(f); - break; - } - } - } - if (current_flags.empty()) - { - if (current != NULL) - { - FOREACH_MOD(I_OnAccessDel, OnAccessDel(ci, u, current)); - ci->EraseAccess(current); - source.Reply(_("\002%s\002 removed from the %s access list."), mask.c_str(), ci->name.c_str()); - } - else - { - source.Reply(_("Insufficient flags given")); - } - return; - } - - service_reference provider("access/flags"); - if (!provider) - return; - FlagsChanAccess *access = debug_cast(provider->Create()); - access->ci = ci; - access->mask = mask; - access->creator = u->nick; - access->last_seen = current ? current->last_seen : 0; - access->created = Anope::CurTime; - access->flags = current_flags; - - if (current != NULL) - ci->EraseAccess(current); - - ci->AddAccess(access); - - FOREACH_MOD(I_OnAccessAdd, OnAccessAdd(ci, u, access)); - - Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "MODIFY " << mask << " with flags " << access->Serialize(); - source.Reply(_("Access for \002%s\002 on %s set to +\002%s\002"), access->mask.c_str(), ci->name.c_str(), access->Serialize().c_str()); - - return; - } - - void DoList(CommandSource &source, ChannelInfo *ci, const std::vector ¶ms) - { - const Anope::string &arg = params.size() > 2 ? params[2] : ""; - - if (!ci->GetAccessCount()) - source.Reply(_("%s access list is empty."), ci->name.c_str()); - else - { - unsigned total = 0; - - for (unsigned i = 0, end = ci->GetAccessCount(); i < end; ++i) - { - 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; - } - - if (++total == 1) - { - source.Reply(_("Flags list for %s"), ci->name.c_str()); - } - - source.Reply(_(" %3d %-10s +%-10s [last modified on %s by %s]"), i + 1, access->mask.c_str(), FlagsChanAccess::DetermineFlags(access).c_str(), do_strftime(access->created, source.u->Account(), true).c_str(), access->creator.c_str()); - } - - if (total == 0) - source.Reply(_("No matching entries on %s access list."), ci->name.c_str()); - else if (total == ci->GetAccessCount()) - source.Reply(_("End of access list.")); - else - source.Reply(_("End of access list - %d/%d entries shown."), total, ci->GetAccessCount()); - } - } - - void DoClear(CommandSource &source, ChannelInfo *ci) - { - User *u = source.u; - - if (!IsFounder(u, ci) && !u->HasPriv("chanserv/access/modify")) - source.Reply(ACCESS_DENIED); - else - { - ci->ClearAccess(); - - FOREACH_MOD(I_OnAccessClear, OnAccessClear(ci, u)); - - source.Reply(_("Channel %s access list has been cleared."), ci->name.c_str()); - - bool override = !IsFounder(u, ci); - Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "CLEAR"; - } - - return; - } - - public: - CommandCSFlags(Module *creator) : Command(creator, "chanserv/flags", 2, 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\002")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - const Anope::string &chan = params[0]; - const Anope::string &cmd = params[1]; - - User *u = source.u; - ChannelInfo *ci = cs_findchan(chan); - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, chan.c_str()); - return; - } - - bool is_list = cmd.equals_ci("LIST"); - bool has_access = false; - if (u->HasPriv("chanserv/access/modify")) - has_access = true; - else if (is_list && ci->HasPriv(u, CA_ACCESS_LIST)) - has_access = true; - else if (ci->HasPriv(u, CA_ACCESS_CHANGE)) - has_access = true; - - if (!has_access) - source.Reply(ACCESS_DENIED); - else if (readonly && !is_list) - source.Reply(_("Sorry, channel access list modification is temporarily disabled.")); - else if (cmd.equals_ci("MODIFY")) - this->DoModify(source, ci, params); - else if (cmd.equals_ci("LIST")) - this->DoList(source, ci, params); - else if (cmd.equals_ci("CLEAR")) - this->DoClear(source, ci); - else - this->OnSyntaxError(source, cmd); - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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 MODIFY command allows you to modify the access list. If mask is\n" - "not already on the access list is it 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 up what you already have.")); - source.Reply(" "); - source.Reply(_("The LIST 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 CLEAR command clears the channel access list, which requires channel founder.")); - source.Reply(" "); - source.Reply(_("The available flags are:")); - - std::multimap > levels; - for (int i = 0; flagLevels[i].priv != CA_SIZE; ++i) - levels.insert(std::make_pair(flagLevels[i].default_char, &flagLevels[i])); - for (std::multimap >::iterator it = levels.begin(), it_end = levels.end(); it != it_end; ++it) - { - FlagLevels *l = it->second; - source.Reply(" %c - %s", l->default_char, translate(source.u->Account(), l->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, CORE), - accessprovider(this), commandcsflags(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&accessprovider); - ModuleManager::RegisterService(&commandcsflags); - - Implementation i[] = { I_OnReload }; - ModuleManager::Attach(i, this, 1); - - this->OnReload(); - } - - void OnReload() - { - ConfigReader config; - - for (int i = 0; flagLevels[i].priv != CA_SIZE; ++i) - { - FlagLevels &l = flagLevels[i]; - - const Anope::string &value = config.ReadValue("chanserv", l.config_name, "", 0); - if (value.empty()) - continue; - l.default_char = value[0]; - } - } -}; - -MODULE_INIT(CSFlags) diff --git a/modules/core/cs_getkey.cpp b/modules/core/cs_getkey.cpp deleted file mode 100644 index 4a6d62442..000000000 --- a/modules/core/cs_getkey.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/* ChanServ core functions - * - * (C) 2003-2011 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 ¶ms) - { - const Anope::string &chan = params[0]; - - User *u = source.u; - ChannelInfo *ci = cs_findchan(params[0]); - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - - if (!ci->HasPriv(u, CA_GETKEY) && !u->HasCommand("chanserv/chanserv/getkey")) - { - source.Reply(ACCESS_DENIED); - return; - } - - Anope::string key; - if (!ci->c || !ci->c->GetParam(CMODE_KEY, key)) - { - source.Reply(_("The channel \002%s\002 has no key."), chan.c_str()); - return; - } - - bool override = !ci->HasPriv(u, CA_GETKEY); - Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci); - - source.Reply(_("Key for channel \002%s\002 is \002%s\002."), chan.c_str(), key.c_str()); - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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, CORE), commandcsgetkey(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandcsgetkey); - } -}; - -MODULE_INIT(CSGetKey) diff --git a/modules/core/cs_info.cpp b/modules/core/cs_info.cpp deleted file mode 100644 index ef026563e..000000000 --- a/modules/core/cs_info.cpp +++ /dev/null @@ -1,142 +0,0 @@ -/* ChanServ core functions - * - * (C) 2003-2011 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 -{ - void CheckOptStr(Anope::string &buf, ChannelInfoFlag opt, const Anope::string &str, ChannelInfo *ci, const NickCore *nc) - { - if (ci->HasFlag(opt)) - { - if (!buf.empty()) - buf += ", "; - - buf += str; - } - } - - public: - CommandCSInfo(Module *creator) : Command(creator, "chanserv/info", 1, 2) - { - this->SetFlag(CFLAG_ALLOW_UNREGISTERED); - this->SetDesc(_("Lists information about the named registered channel")); - this->SetSyntax(_("\037channel\037")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - const Anope::string &chan = params[0]; - - User *u = source.u; - ChannelInfo *ci = cs_findchan(params[0]); - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - bool has_auspex = u->IsIdentified() && u->HasPriv("chanserv/auspex"); - bool show_all = false; - - /* Should we show all fields? Only for sadmins and identified users */ - if (has_auspex || ci->HasPriv(u, CA_INFO)) - show_all = true; - - source.Reply(CHAN_INFO_HEADER, chan.c_str()); - if (ci->GetFounder()) - source.Reply(_(" Founder: %s"), ci->GetFounder()->display.c_str()); - - if (show_all && ci->successor) - source.Reply(_(" Successor: %s"), ci->successor->display.c_str()); - - if (!ci->desc.empty()) - source.Reply(_(" Description: %s"), ci->desc.c_str()); - source.Reply(_(" Registered: %s"), do_strftime(ci->time_registered).c_str()); - source.Reply(_(" Last used: %s"), do_strftime(ci->last_used).c_str()); - - ModeLock *secret = ci->GetMLock(CMODE_SECRET); - if (!ci->last_topic.empty() && (show_all || ((!secret || secret->set == false) && (!ci->c || !ci->c->HasMode(CMODE_SECRET))))) - { - source.Reply(_(" Last topic: %s"), ci->last_topic.c_str()); - source.Reply(_(" Topic set by: %s"), ci->last_topic_setter.c_str()); - } - - if (show_all) - { - source.Reply(_(" Ban type: %d"), ci->bantype); - Anope::string optbuf; - - CheckOptStr(optbuf, CI_KEEPTOPIC, _("Topic Retention"), ci, u->Account()); - CheckOptStr(optbuf, CI_OPNOTICE, _("OP Notice"), ci, u->Account()); - CheckOptStr(optbuf, CI_PEACE, _("Peace"), ci, u->Account()); - CheckOptStr(optbuf, CI_PRIVATE, _("Private"), ci, u->Account()); - CheckOptStr(optbuf, CI_RESTRICTED, _("Restricted Access"), ci, u->Account()); - CheckOptStr(optbuf, CI_SECURE, _("Secure"), ci, u->Account()); - CheckOptStr(optbuf, CI_SECUREFOUNDER, _("Secure Founder"), ci, u->Account()); - CheckOptStr(optbuf, CI_SECUREOPS, _("Secure Ops"), ci, u->Account()); - if (ci->HasFlag(CI_SIGNKICK)) - CheckOptStr(optbuf, CI_SIGNKICK, _("Signed kicks"), ci, u->Account()); - else - CheckOptStr(optbuf, CI_SIGNKICK_LEVEL, _("Signed kicks"), ci, u->Account()); - CheckOptStr(optbuf, CI_TOPICLOCK, _("Topic Lock"), ci, u->Account()); - CheckOptStr(optbuf, CI_PERSIST, _("Persistant"), ci, u->Account()); - CheckOptStr(optbuf, CI_NO_EXPIRE, _("No expire"), ci, u->Account()); - - source.Reply(NICK_INFO_OPTIONS, optbuf.empty() ? _("None") : optbuf.c_str()); - source.Reply(_(" Mode lock: %s"), ci->GetMLockAsString(true).c_str()); - - if (!ci->HasFlag(CI_NO_EXPIRE)) - source.Reply(_(" Expires on: %s"), do_strftime(ci->last_used + Config->CSExpire).c_str()); - } - if (ci->HasFlag(CI_SUSPENDED)) - { - Anope::string by, reason; - ci->GetExtRegular("suspend_by", by); - ci->GetExtRegular("suspend_reason", reason); - source.Reply(_(" Suspended: [%s] %s"), by.c_str(), !reason.empty() ? reason.c_str() : NO_REASON); - } - - FOREACH_MOD(I_OnChanInfo, OnChanInfo(source, ci, show_all)); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Lists information about the named registered channel,\n" - "including its founder, time of registration, last time\n" - "used, description, and mode lock, if any. If \002ALL\002 is \n" - "specified, the entry message and successor will also \n" - "be displayed.")); - return true; - } -}; - -class CSInfo : public Module -{ - CommandCSInfo commandcsinfo; - - public: - CSInfo(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandcsinfo(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandcsinfo); - } -}; - -MODULE_INIT(CSInfo) diff --git a/modules/core/cs_invite.cpp b/modules/core/cs_invite.cpp deleted file mode 100644 index 27b4b03a3..000000000 --- a/modules/core/cs_invite.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/* ChanServ core functions - * - * (C) 2003-2011 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 into a channel")); - this->SetSyntax(_("\037channel\037")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - const Anope::string &chan = params[0]; - - User *u = source.u; - Channel *c = findchan(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 (!ci->HasPriv(u, CA_INVITE)) - { - source.Reply(ACCESS_DENIED); - return; - } - - User *u2; - if (params.size() == 1) - u2 = u; - else - { - if (!(u2 = finduser(params[1]))) - { - source.Reply(NICK_X_NOT_IN_USE, params[1].c_str()); - return; - } - } - - // XXX need a check for override... - Log(LOG_COMMAND, u, this, ci) << "for " << u2->nick; - - if (c->FindUser(u2)) - source.Reply(_("You are already in \002%s\002! "), c->name.c_str()); - else - { - ircdproto->SendInvite(ci->WhoSends(), chan, u2->nick); - 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."), c->name.c_str()); - } - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Tells %s to invite you into the given channel.\n" - " \n" - "By default, limited to AOPs or those with level 5 and above\n" - "on the channel."), source.owner->nick.c_str()); - return true; - } -}; - -class CSInvite : public Module -{ - CommandCSInvite commandcsinvite; - - public: - CSInvite(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), commandcsinvite(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandcsinvite); - } -}; - -MODULE_INIT(CSInvite) diff --git a/modules/core/cs_kick.cpp b/modules/core/cs_kick.cpp deleted file mode 100644 index b98d0953d..000000000 --- a/modules/core/cs_kick.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/* ChanServ core functions - * - * (C) 2003-2011 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 selected nick from a channel")); - this->SetSyntax(_("\037channel\037 \037nick\037 [\037reason\037]")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - const Anope::string &chan = params[0]; - const Anope::string &target = params[1]; - const Anope::string &reason = params.size() > 2 ? params[2] : "Requested"; - - User *u = source.u; - ChannelInfo *ci = cs_findchan(params[0]); - Channel *c = findchan(params[0]); - bool is_same = target.equals_ci(u->nick); - User *u2 = is_same ? u : finduser(target); - - AccessGroup u_access = ci->AccessFor(u), u2_access = ci->AccessFor(u2); - - if (!c) - source.Reply(CHAN_X_NOT_IN_USE, chan.c_str()); - else if (!ci) - source.Reply(CHAN_X_NOT_REGISTERED, chan.c_str()); - else if (!u2) - source.Reply(NICK_X_NOT_IN_USE, target.c_str()); - else if (!ci->HasPriv(u, CA_KICK)) - source.Reply(ACCESS_DENIED); - else if (!is_same && (ci->HasFlag(CI_PEACE)) && u2_access >= u_access) - 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 - { - // XXX - Log(LOG_COMMAND, u, this, ci) << "for " << u2->nick; - - if (ci->HasFlag(CI_SIGNKICK) || (ci->HasFlag(CI_SIGNKICK_LEVEL) && !ci->HasPriv(u, CA_SIGNKICK))) - ci->c->Kick(ci->WhoSends(), u2, "%s (%s)", reason.c_str(), u->nick.c_str()); - else - ci->c->Kick(ci->WhoSends(), u2, "%s", reason.c_str()); - } - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Kicks a selected nick on a channel.\n" - " \n" - "By default, limited to AOPs or those with level 5 access \n" - "and above on the channel.")); - return true; - } -}; - -class CSKick : public Module -{ - CommandCSKick commandcskick; - - public: - CSKick(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), commandcskick(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandcskick); - } -}; - -MODULE_INIT(CSKick) diff --git a/modules/core/cs_list.cpp b/modules/core/cs_list.cpp deleted file mode 100644 index a79c047c4..000000000 --- a/modules/core/cs_list.cpp +++ /dev/null @@ -1,136 +0,0 @@ -/* ChanServ core functions - * - * (C) 2003-2011 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 CommandCSList : public Command -{ - public: - CommandCSList(Module *creator) : Command(creator, "chanserv/list", 1, 2) - { - this->SetFlag(CFLAG_STRIP_CHANNEL); - this->SetDesc(_("Lists all registered channels matching the given pattern")); - this->SetSyntax(_("\037pattern\037")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - - Anope::string pattern = params[0]; - unsigned nchans; - bool is_servadmin = u->HasCommand("chanserv/chanserv/list"); - int count = 0, from = 0, to = 0; - bool suspended = false, channoexpire = false; - - if (pattern[0] == '#') - { - Anope::string n1 = myStrGetToken(pattern.substr(1), '-', 0), /* Read FROM out */ - n2 = myStrGetTokenRemainder(pattern, '-', 1); - - try - { - from = convertTo(n1); - to = convertTo(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; - - source.Reply(LIST_HEADER, pattern.c_str()); - - for (registered_channel_map::const_iterator it = RegisteredChannelList.begin(), it_end = RegisteredChannelList.end(); it != it_end; ++it) - { - ChannelInfo *ci = it->second; - - if (!is_servadmin && (ci->HasFlag(CI_PRIVATE) || ci->HasFlag(CI_SUSPENDED))) - continue; - else if (suspended && !ci->HasFlag(CI_SUSPENDED)) - continue; - else if (channoexpire && !ci->HasFlag(CI_NO_EXPIRE)) - continue; - - if (pattern.equals_ci(ci->name) || ci->name.equals_ci(spattern) || Anope::Match(ci->name, pattern) || Anope::Match(ci->name, spattern)) - { - if (((count + 1 >= from && count + 1 <= to) || (!from && !to)) && ++nchans <= Config->CSListMax) - { - char noexpire_char = ' '; - if (is_servadmin && (ci->HasFlag(CI_NO_EXPIRE))) - noexpire_char = '!'; - - Anope::string buf; - if (ci->HasFlag(CI_SUSPENDED)) - buf = Anope::printf("%-20s [Suspended]", ci->name.c_str()); - else - buf = Anope::printf("%-20s %s", ci->name.c_str(), !ci->desc.empty() ? ci->desc.c_str() : ""); - - source.Reply(" %c%s", noexpire_char, buf.c_str()); - } - ++count; - } - } - - source.Reply(_("End of list - %d/%d matches shown."), nchans > Config->CSListMax ? Config->CSListMax : nchans, nchans); - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Lists all registered channels matching the given pattern.\n" - "(Channels with the \002PRIVATE\002 option set are not listed.)\n" - "Note that a preceding '#' specifies a range, channel names\n" - "are to be written without '#'.")); - return true; - } -}; - -class CSList : public Module -{ - CommandCSList commandcslist; - - public: - CSList(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), commandcslist(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandcslist); - } -}; - -MODULE_INIT(CSList) diff --git a/modules/core/cs_main.cpp b/modules/core/cs_main.cpp deleted file mode 100644 index 5c490e4ff..000000000 --- a/modules/core/cs_main.cpp +++ /dev/null @@ -1,143 +0,0 @@ -/* ChanServ core functions - * - * (C) 2003-2011 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 ChanServCore : public Module -{ - public: - ChanServCore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE) - { - this->SetAuthor("Anope"); - - BotInfo *ChanServ = findbot(Config->ChanServ); - if (ChanServ == NULL) - throw ModuleException("No bot named " + Config->ChanServ); - - Implementation i[] = { I_OnBotPrivmsg, I_OnDelCore, I_OnPreHelp, I_OnPostHelp }; - ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); - } - - EventReturn OnBotPrivmsg(User *u, BotInfo *bi, Anope::string &message) - { - if (Config->CSOpersOnly && !u->HasMode(UMODE_OPER) && bi->nick == Config->ChanServ) - { - u->SendMessage(bi, ACCESS_DENIED); - return EVENT_STOP; - } - - return EVENT_CONTINUE; - } - - void OnDelCore(NickCore *nc) - { - // XXX this is slightly inefficient - for (registered_channel_map::const_iterator it = RegisteredChannelList.begin(), it_end = RegisteredChannelList.end(); it != it_end;) - { - ChannelInfo *ci = it->second; - ++it; - - if (ci->GetFounder() == nc) - { - NickCore *newowner = NULL; - if (ci->successor && (ci->successor->IsServicesOper() || !Config->CSMaxReg || ci->successor->channelcount < Config->CSMaxReg)) - newowner = ci->successor; - else - { - ChanAccess *highest = NULL; - for (unsigned j = 0; j < ci->GetAccessCount(); ++j) - { - ChanAccess *ca = ci->GetAccess(j); - NickCore *anc = findcore(ca->mask); - - if (!anc || (!anc->IsServicesOper() && Config->CSMaxReg && anc->channelcount >= Config->CSMaxReg) || (anc == nc)) - continue; - if (!highest || *ca > *highest) - highest = ca; - } - if (highest) - newowner = findcore(highest->mask); - } - - if (newowner) - { - Log(LOG_NORMAL, "chanserv/expire") << "Transferring foundership of " << ci->name << " from deleted nick " << nc->display << " to " << newowner->display; - ci->SetFounder(newowner); - ci->successor = NULL; - } - else - { - Log(LOG_NORMAL, "chanserv/expire") << "Deleting channel " << ci->name << " owned by deleted nick " << nc->display; - - delete ci; - continue; - } - } - - if (ci->successor == nc) - ci->successor = NULL; - - for (unsigned j = 0; j < ci->GetAccessCount(); ++j) - { - ChanAccess *ca = ci->GetAccess(j); - NickCore *anc = findcore(ca->mask); - - if (anc && anc == nc) - { - ci->EraseAccess(j); - break; - } - } - - for (unsigned j = ci->GetAkickCount(); j > 0; --j) - { - AutoKick *akick = ci->GetAkick(j - 1); - if (akick->HasFlag(AK_ISNICK) && akick->nc == nc) - ci->EraseAkick(j - 1); - } - } - } - - void OnPreHelp(CommandSource &source, const std::vector ¶ms) - { - if (!params.empty() || source.owner->nick != Config->ChanServ) - return; - 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 "), - Config->ChanServ.c_str(), Config->ChanServ.c_str(), Config->UseStrictPrivMsgString.c_str(), Config->ChanServ.c_str(), Config->UseStrictPrivMsgString.c_str(), Config->ChanServ.c_str(), Config->ChanServ.c_str(), source.command.c_str()); - } - - void OnPostHelp(CommandSource &source, const std::vector ¶ms) - { - if (!params.empty() || source.owner->nick != Config->ChanServ) - return; - if (Config->CSExpire >= 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."), Config->CSExpire / 86400); - if (source.u->IsServicesOper()) - source.Reply(_(" \n" - "Services Operators can also drop any channel without needing\n" - "to identify via password, and may view the access, akick,\n" - "and level setting lists for any channel.")); - } -}; - -MODULE_INIT(ChanServCore) - diff --git a/modules/core/cs_mode.cpp b/modules/core/cs_mode.cpp deleted file mode 100644 index e67abd85a..000000000 --- a/modules/core/cs_mode.cpp +++ /dev/null @@ -1,364 +0,0 @@ -/* ChanServ core functions - * - * (C) 2003-2011 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 CommandCSMode : public Command -{ - bool CanSet(User *u, ChannelInfo *ci, ChannelModeName mode) - { - switch (mode) - { - case CMODE_OWNER: - return ci->HasPriv(u, CA_OWNER); - case CMODE_PROTECT: - return ci->HasPriv(u, CA_PROTECT); - case CMODE_OP: - return ci->HasPriv(u, CA_OPDEOP); - case CMODE_HALFOP: - return ci->HasPriv(u, CA_HALFOP); - case CMODE_VOICE: - return ci->HasPriv(u, CA_VOICE); - default: - break; - } - - return false; - } - - void DoLock(CommandSource &source, ChannelInfo *ci, const std::vector ¶ms) - { - User *u = source.u; - const Anope::string &subcommand = params[2]; - const Anope::string ¶m = params.size() > 3 ? params[3] : ""; - - if (subcommand.equals_ci("ADD") && !param.empty()) - { - spacesepstream sep(param); - Anope::string modes; - - sep.GetToken(modes); - - int adding = -1; - for (size_t i = 0; i < modes.length(); ++i) - { - switch (modes[i]) - { - case '+': - adding = 1; - break; - case '-': - adding = 0; - break; - default: - if (adding == -1) - break; - ChannelMode *cm = ModeManager::FindChannelModeByChar(modes[i]); - if (!cm || !cm->CanSet(u)) - { - source.Reply(_("Unknown mode character %c ignored."), 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->ModeChar); - else - { - ci->SetMLock(cm, adding, mode_param, u->nick); - if (!mode_param.empty()) - mode_param = " " + mode_param; - source.Reply(_("%c%c%s locked on %s"), adding ? '+' : '-', cm->ModeChar, mode_param.c_str(), ci->name.c_str()); - } - } - } - - if (ci->c) - check_modes(ci->c); - } - else if (subcommand.equals_ci("DEL") && !param.empty()) - { - spacesepstream sep(param); - Anope::string modes; - - sep.GetToken(modes); - - int adding = -1; - for (size_t i = 0; i < modes.length(); ++i) - { - switch (modes[i]) - { - case '+': - adding = 1; - break; - case '-': - adding = 0; - break; - default: - if (adding == -1) - break; - ChannelMode *cm = ModeManager::FindChannelModeByChar(modes[i]); - if (!cm || !cm->CanSet(u)) - { - source.Reply(_("Unknown mode character %c ignored."), modes[i]); - break; - } - Anope::string mode_param; - if (!cm->Type == MODE_REGULAR && !sep.GetToken(mode_param)) - source.Reply(_("Missing parameter for mode %c."), cm->ModeChar); - else - { - if (ci->RemoveMLock(cm, mode_param)) - { - if (!mode_param.empty()) - mode_param = " " + mode_param; - source.Reply(_("%c%c%s has been unlocked from %s."), adding == 1 ? '+' : '-', cm->ModeChar, mode_param.c_str(), ci->name.c_str()); - } - else - source.Reply(_("%c is not locked on %s."), cm->ModeChar, ci->name.c_str()); - } - } - } - } - else if (subcommand.equals_ci("LIST")) - { - const std::multimap &mlocks = ci->GetMLock(); - if (mlocks.empty()) - { - source.Reply(_("Channel %s has no mode locks."), ci->name.c_str()); - } - else - { - source.Reply(_("Mode locks for %s:"), ci->name.c_str()); - for (std::multimap::const_iterator it = mlocks.begin(), it_end = mlocks.end(); it != it_end; ++it) - { - const ModeLock &ml = it->second; - ChannelMode *cm = ModeManager::FindChannelModeByName(ml.name); - if (!cm) - continue; - - Anope::string modeparam = ml.param; - if (!modeparam.empty()) - modeparam = " " + modeparam; - source.Reply(_("%c%c%s, by %s on %s"), ml.set ? '+' : '-', cm->ModeChar, modeparam.c_str(), ml.setter.c_str(), do_strftime(ml.created).c_str()); - } - } - } - else - this->OnSyntaxError(source, subcommand); - } - - void DoSet(CommandSource &source, ChannelInfo *ci, const std::vector ¶ms) - { - User *u = source.u; - - spacesepstream sep(params.size() > 3 ? params[3] : ""); - Anope::string modes = params[2], param; - - Log(LOG_COMMAND, u, this, ci) << "to set " << params[2]; - - 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) - break; - for (unsigned j = 0; j < ModeManager::ChannelModes.size(); ++j) - { - ChannelMode *cm = ModeManager::ChannelModes[j]; - if (cm->CanSet(u)) - { - 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 || !cm->CanSet(u)) - continue; - switch (cm->Type) - { - case MODE_REGULAR: - if (adding) - ci->c->SetMode(NULL, cm); - else - ci->c->RemoveMode(NULL, cm); - break; - case MODE_PARAM: - 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)) - break; - - if (!this->CanSet(u, ci, cm->Name)) - { - source.Reply(_("You do not have access to set mode %c."), cm->ModeChar); - break; - } - - AccessGroup u_access = ci->AccessFor(u); - - if (str_is_wildcard(param)) - { - for (CUserList::const_iterator it = ci->c->users.begin(), it_end = ci->c->users.end(); it != it_end; ++it) - { - UserContainer *uc = *it; - - AccessGroup targ_access = ci->AccessFor(uc->user); - - if (targ_access > u_access) - { - 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->nick); - else - ci->c->RemoveMode(NULL, cm, uc->user->nick); - } - } - } - else - { - User *target = finduser(param); - if (target != NULL) - { - AccessGroup targ_access = ci->AccessFor(target); - if (targ_access > u_access) - { - source.Reply(_("You do not have the access to change %s's modes."), target->nick.c_str()); - break; - } - } - if (adding) - ci->c->SetMode(NULL, cm, param); - else - ci->c->RemoveMode(NULL, cm, param); - } - break; - } - case MODE_LIST: - if (!sep.GetToken(param)) - break; - if (adding) - ci->c->SetMode(NULL, cm, param); - else - { - std::pair its = ci->c->GetModeList(cm->Name); - for (; its.first != its.second;) - { - const Anope::string &mask = its.first->second; - ++its.first; - - if (Anope::Match(mask, param)) - ci->c->RemoveMode(NULL, cm, mask); - } - } - } - } - } - } - - public: - CommandCSMode(Module *creator) : Command(creator, "chanserv/mode", 3, 4) - { - this->SetDesc(_("Control modes and mode locks on a channel")); - this->SetSyntax(_("\037channel\037 LOCK {ADD|DEL|LIST} [\037what\037]")); - this->SetSyntax(_("\037channel\037 SET \037modes\037")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - const Anope::string &subcommand = params[1]; - - User *u = source.u; - ChannelInfo *ci = cs_findchan(params[0]); - - if (!ci || !ci->c) - source.Reply(CHAN_X_NOT_IN_USE, ci->name.c_str()); - else if (!ci->HasPriv(u, CA_MODE) && !u->HasCommand("chanserv/chanserv/mode")) - source.Reply(ACCESS_DENIED); - else if (subcommand.equals_ci("LOCK")) - this->DoLock(source, ci, params); - else if (subcommand.equals_ci("SET")) - this->DoSet(source, ci, params); - else - this->OnSyntaxError(source, ""); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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 \002MODE 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.\n" - "Example:\n" - " \002MODE #channel LOCK ADD +bmnt *!*@*aol*\002\n" - " \n" - "The \002MODE 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:")); - return true; - } -}; - -class CSMode : public Module -{ - CommandCSMode commandcsmode; - - public: - CSMode(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandcsmode(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandcsmode); - } -}; - -MODULE_INIT(CSMode) diff --git a/modules/core/cs_modes.cpp b/modules/core/cs_modes.cpp deleted file mode 100644 index cee2f9460..000000000 --- a/modules/core/cs_modes.cpp +++ /dev/null @@ -1,431 +0,0 @@ -/* ChanServ core functions - * - * (C) 2003-2011 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 CommandModeBase : public Command -{ - void do_mode(CommandSource &source, Command *com, ChannelMode *cm, const Anope::string &chan, const Anope::string &nick, bool set, ChannelAccess level, ChannelAccess levelself, ChannelInfoFlag notice) - { - User *u = source.u; - User *u2 = finduser(nick); - Channel *c = findchan(chan); - ChannelInfo *ci = c ? c->ci : NULL; - - bool is_same = u == u2; - - AccessGroup u_access = ci ? ci->AccessFor(u) : AccessGroup(), u2_access = ci && u2 ? ci->AccessFor(u2) : AccessGroup(); - - if (!c) - source.Reply(CHAN_X_NOT_IN_USE, chan.c_str()); - else if (!ci) - source.Reply(CHAN_X_NOT_REGISTERED, chan.c_str()); - else if (!u2) - source.Reply(NICK_X_NOT_IN_USE, nick.c_str()); - else if (is_same ? !ci->HasPriv(u, levelself) : !ci->HasPriv(u, level)) - source.Reply(ACCESS_DENIED); - else if (!set && !is_same && ci->HasFlag(CI_PEACE) && u2_access >= u_access) - source.Reply(ACCESS_DENIED); - else if (!set && u2->IsProtected() && !is_same) - 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 - { - if (set) - c->SetMode(NULL, cm, u2->nick); - else - c->RemoveMode(NULL, cm, u2->nick); - - Log(LOG_COMMAND, u, com, ci) << "for " << u2->nick; - if (notice && ci->HasFlag(notice)) - ircdproto->SendMessage(ci->WhoSends(), c->name, "%s command used for %s by %s", com->name.c_str(), u2->nick.c_str(), u->nick.c_str()); - } - } - - protected: - /** do_util: not a command, but does the job of others - * @param source The source of the command - * @param com The command calling this function - * @param cm A channel mode class - * @param chan The channel its being set on - * @param nick The nick the modes being set on - * @param set Is the mode being set or removed - * @param level The acecss level required to set this mode on someone else - * @param levelself The access level required to set this mode on yourself - * @param notice Flag required on a channel to send a notice - */ - void do_util(CommandSource &source, Command *com, ChannelMode *cm, const Anope::string &chan, const Anope::string &nick, bool set, ChannelAccess level, ChannelAccess levelself, ChannelInfoFlag notice) - { - User *u = source.u; - - if (chan.empty()) - for (UChannelList::iterator it = u->chans.begin(); it != u->chans.end(); ++it) - do_mode(source, com, cm, (*it)->chan->name, u->nick, set, level, levelself, notice); - else - do_mode(source, com, cm, chan, !nick.empty() ? nick : u->nick, set, level, levelself, notice); - - return; - } - - public: - CommandModeBase(Module *creator, const Anope::string &cname) : Command(creator, cname, 0, 2) - { - this->SetSyntax(_("[\037#channel\037] [\037nick\037]")); - } -}; - -class CommandCSOp : public CommandModeBase -{ - public: - CommandCSOp(Module *creator) : CommandModeBase(creator, "chanserv/op") - { - this->SetDesc(_("Gives Op status to a selected nick on a channel")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - ChannelMode *cm = ModeManager::FindChannelModeByName(CMODE_OP); - - return do_util(source, this, cm, !params.empty() ? params[0] : "", params.size() > 1 ? params[1] : "", true, CA_OPDEOP, CA_OPDEOPME, CI_OPNOTICE); - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Ops a selected nick on a channel. If nick is not given,\n" - "it will op you. If channel is not given, it will op you\n" - "on every channel.\n" - " \n" - "By default, limited to AOPs or those with level 5 access \n" - "and above on the channel.")); - return true; - } -}; - -class CommandCSDeOp : public CommandModeBase -{ - public: - CommandCSDeOp(Module *creator) : CommandModeBase(creator, "chanserv/deop") - { - this->SetDesc(_("Deops a selected nick on a channel")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - ChannelMode *cm = ModeManager::FindChannelModeByName(CMODE_OP); - - return do_util(source, this, cm, !params.empty() ? params[0] : "", params.size() > 1 ? params[1] : "", false, CA_OPDEOP, CA_OPDEOPME, CI_OPNOTICE); - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply("Deops a selected nick on a channel. If nick is not given,\n" - "it will deop you. If channel is not given, it will deop\n" - "you on every channel.\n" - " \n" - "By default, limited to AOPs or those with level 5 access \n" - "and above on the channel."); - return true; - } -}; - -class CommandCSVoice : public CommandModeBase -{ - public: - CommandCSVoice(Module *creator) : CommandModeBase(creator, "chanserv/voice") - { - this->SetDesc(_("Voices a selected nick on a channel")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - ChannelMode *cm = ModeManager::FindChannelModeByName(CMODE_VOICE); - - return do_util(source, this, cm, !params.empty() ? params[0] : "", params.size() > 1 ? params[1] : "", true, CA_VOICE, CA_VOICEME, CI_BEGIN); - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Voices a selected nick on a channel. If nick is not given,\n" - "it will voice you. If channel is not given, it will voice you\n" - "on every channel.\n" - " \n" - "By default, limited to AOPs or those with level 5 access \n" - "and above on the channel, or to VOPs or those with level 3 \n" - "and above for self voicing.")); - return true; - } -}; - -class CommandCSDeVoice : public CommandModeBase -{ - public: - CommandCSDeVoice(Module *creator) : CommandModeBase(creator, "chanserv/devoice") - { - this->SetDesc(_("Devoices a selected nick on a channel")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - ChannelMode *cm = ModeManager::FindChannelModeByName(CMODE_VOICE); - - return do_util(source, this, cm, !params.empty() ? params[0] : "", params.size() > 1 ? params[1] : "", false, CA_VOICE, CA_VOICEME, CI_BEGIN); - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Devoices a selected nick on a channel. If nick is not given,\n" - "it will devoice you. If channel is not given, it will devoice\n" - "you on every channel.\n" - " \n" - "By default, limited to AOPs or those with level 5 access \n" - "and above on the channel, or to VOPs or those with level 3 \n" - "and above for self devoicing.")); - return true; - } -}; - -class CommandCSHalfOp : public CommandModeBase -{ - public: - CommandCSHalfOp(Module *creator) : CommandModeBase(creator, "chanserv/halfop") - { - this->SetDesc(_("Halfops a selected nick on a channel")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - ChannelMode *cm = ModeManager::FindChannelModeByName(CMODE_HALFOP); - - if (!cm) - return; - - return do_util(source, this, cm, !params.empty() ? params[0] : "", params.size() > 1 ? params[1] : "", true, CA_HALFOP, CA_HALFOPME, CI_BEGIN); - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Halfops a selected nick on a channel. If nick is not given,\n" - "it will halfop you. If channel is not given, it will halfop\n" - "you on every channel.\n" - " \n" - "By default, limited to AOPs and those with level 5 access \n" - "and above on the channel, or to HOPs or those with level 4 \n")); - return true; - } -}; - -class CommandCSDeHalfOp : public CommandModeBase -{ - public: - CommandCSDeHalfOp(Module *creator) : CommandModeBase(creator, "chanserv/dehalfop") - { - this->SetDesc(_("Dehalfops a selected nick on a channel")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - ChannelMode *cm = ModeManager::FindChannelModeByName(CMODE_HALFOP); - - if (!cm) - return; - - return do_util(source, this, cm, !params.empty() ? params[0] : "", params.size() > 1 ? params[1] : "", false, CA_HALFOP, CA_HALFOPME, CI_BEGIN); - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Dehalfops a selected nick on a channel. If nick is not given,\n" - "it will dehalfop you. If channel is not given, it will dehalfop\n" - "you on every channel.\n" - " \n" - "By default, limited to AOPs and those with level 5 access \n" - "and above on the channel, or to HOPs or those with level 4 \n" - "and above for self dehalfopping.")); - return true; - } -}; - -class CommandCSProtect : public CommandModeBase -{ - public: - CommandCSProtect(Module *creator) : CommandModeBase(creator, "chanserv/protect") - { - this->SetDesc(_("Protects a selected nick on a channel")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - ChannelMode *cm = ModeManager::FindChannelModeByName(CMODE_PROTECT); - - if (!cm) - return; - - return do_util(source, this, cm, !params.empty() ? params[0] : "", params.size() > 1 ? params[1] : "", true, CA_PROTECT, CA_PROTECTME, CI_BEGIN); - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Protects a selected nick on a channel. If nick is not given,\n" - "it will protect you. If channel is not given, it will protect\n" - "you on every channel.\n" - " \n" - "By default, limited to the founder, or to SOPs or those with \n" - "level 10 and above on the channel for self protecting.")); - return true; - } -}; - -class CommandCSDeProtect : public CommandModeBase -{ - public: - CommandCSDeProtect(Module *creator) : CommandModeBase(creator, "chanserv/deprotect") - { - this->SetDesc(_("Deprotects a selected nick on a channel")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - ChannelMode *cm = ModeManager::FindChannelModeByName(CMODE_PROTECT); - - if (!cm) - return; - - return do_util(source, this, cm, !params.empty() ? params[0] : "", params.size() > 1 ? params[1] : "", false, CA_PROTECT, CA_PROTECTME, CI_BEGIN); - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Deprotects a selected nick on a channel. If nick is not given,\n" - "it will deprotect you. If channel is not given, it will deprotect\n" - "you on every channel.\n" - " \n" - "By default, limited to the founder, or to SOPs or those with \n")); - return true; - } -}; - -class CommandCSOwner : public CommandModeBase -{ - public: - CommandCSOwner(Module *creator) : CommandModeBase(module, "chanserv/owner") - { - this->SetDesc(_("Gives you owner status on channel")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - ChannelMode *cm = ModeManager::FindChannelModeByName(CMODE_OWNER); - - if (!cm) - return; - - return do_util(source, this, cm, !params.empty() ? params[0] : "", params.size() > 1 ? params[1] : "", true, CA_OWNER, CA_OWNERME, CI_BEGIN); - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Gives the selected nick owner status on \002channel\002. If nick is not\n" - "given, it will give you owner. If channel is not given, it will\n" - "give you owner on every channel.\n" - " \n" - "Limited to those with founder access on the channel.")); - return true; - } -}; - -class CommandCSDeOwner : public CommandModeBase -{ - public: - CommandCSDeOwner(Module *creator) : CommandModeBase(creator, "chanserv/deowner") - { - this->SetDesc(_("Removes your owner status on a channel")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - ChannelMode *cm = ModeManager::FindChannelModeByName(CMODE_OWNER); - - if (!cm) - return; - - return do_util(source, this, cm, !params.empty() ? params[0] : "", params.size() > 1 ? params[1] : "", false, CA_OWNER, CA_OWNERME, CI_BEGIN); - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Removes owner status from the selected nick on \002channel\002. If nick\n" - "is not given, it will deowner you. If channel is not given, it will\n" - "deowner you on every channel.\n" - " \n" - "Limited to those with founder access on the channel.")); - return true; - } -}; - -class CSModes : public Module -{ - CommandCSOwner commandcsowner; - CommandCSDeOwner commandcsdeowner; - CommandCSProtect commandcsprotect; - CommandCSDeProtect commandcsdeprotect; - CommandCSOp commandcsop; - CommandCSDeOp commandcsdeop; - CommandCSHalfOp commandcshalfop; - CommandCSDeHalfOp commandcsdehalfop; - CommandCSVoice commandcsvoice; - CommandCSDeVoice commandcsdevoice; - - public: - CSModes(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandcsowner(this), commandcsdeowner(this), commandcsprotect(this), commandcsdeprotect(this), - commandcsop(this), commandcsdeop(this), commandcshalfop(this), commandcsdehalfop(this), - commandcsvoice(this), commandcsdevoice(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandcsop); - ModuleManager::RegisterService(&commandcsdeop); - ModuleManager::RegisterService(&commandcsvoice); - ModuleManager::RegisterService(&commandcsdevoice); - - ModuleManager::RegisterService(&commandcsowner); - ModuleManager::RegisterService(&commandcsdeowner); - ModuleManager::RegisterService(&commandcsprotect); - ModuleManager::RegisterService(&commandcsdeprotect); - ModuleManager::RegisterService(&commandcshalfop); - ModuleManager::RegisterService(&commandcsdehalfop); - } -}; - -MODULE_INIT(CSModes) diff --git a/modules/core/cs_register.cpp b/modules/core/cs_register.cpp deleted file mode 100644 index 117212519..000000000 --- a/modules/core/cs_register.cpp +++ /dev/null @@ -1,193 +0,0 @@ -/* ChanServ core functions - * - * (C) 2003-2011 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 ¶ms) - { - const Anope::string &chan = params[0]; - const Anope::string &chdesc = params.size() > 1 ? params[1] : ""; - - User *u = source.u; - Channel *c = findchan(params[0]); - ChannelInfo *ci = cs_findchan(params[0]); - - if (readonly) - source.Reply(_("Sorry, channel registration is temporarily disabled.")); - else if (u->Account()->HasFlag(NI_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 (!ircdproto->IsChannelValid(chan)) - source.Reply(CHAN_X_INVALID, chan.c_str()); - else if (ci) - source.Reply(_("Channel \002%s\002 is already registered!"), chan.c_str()); - else if (c && !c->HasUserStatus(u, CMODE_OP)) - source.Reply(_("You must be a channel operator to register the channel.")); - else if (Config->CSMaxReg && u->Account()->channelcount >= Config->CSMaxReg && !u->HasPriv("chanserv/no-register-limit")) - source.Reply(u->Account()->channelcount > Config->CSMaxReg ? CHAN_EXCEEDED_CHANNEL_LIMIT : _(CHAN_REACHED_CHANNEL_LIMIT), Config->CSMaxReg); - else - { - ci = new ChannelInfo(chan); - ci->SetFounder(u->Account()); - if (!chdesc.empty()) - 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.owner->nick; - - ci->bi = NULL; - Log(LOG_COMMAND, u, this, ci); - source.Reply(_("Channel \002%s\002 registered under your nickname: %s"), chan.c_str(), u->nick.c_str()); - - /* Implement new mode lock */ - if (c) - { - check_modes(c); - - ChannelMode *cm; - if (u->FindChannel(c) != NULL) - { - /* On most ircds you do not receive the admin/owner mode till its registered */ - if ((cm = ModeManager::FindChannelModeByName(CMODE_OWNER))) - c->SetMode(NULL, cm, u->nick); - else if ((cm = ModeManager::FindChannelModeByName(CMODE_PROTECT))) - c->RemoveMode(NULL, cm, u->nick); - } - - /* Mark the channel as persistant */ - if (c->HasMode(CMODE_PERM)) - ci->SetFlag(CI_PERSIST); - /* Persist may be in def cflags, set it here */ - else if (ci->HasFlag(CI_PERSIST) && (cm = ModeManager::FindChannelModeByName(CMODE_PERM))) - c->SetMode(NULL, CMODE_PERM); - } - - FOREACH_MOD(I_OnChanRegistered, OnChanRegistered(ci)); - } - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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.\n" - "See the \002ACCESS\002 command (\002%s%s HELP ACCESS\002) for\n" - "information on giving a subset of these privileges to\n" - "other channel users.\n" - " \n" - "NOTICE: In order to register a channel, you must have\n" - "first registered your nickname. If you haven't,\n" - "\002%s%s HELP\002 for information on how to do so."), - source.owner->nick.c_str(), source.owner->nick.c_str(), Config->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str(), Config->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str()); - return true; - } -}; - -class ExpireCallback : public CallBack -{ - public: - ExpireCallback(Module *owner) : CallBack(owner, Config->ExpireTimeout, Anope::CurTime, true) { } - - void Tick(time_t) - { - if (!Config->CSExpire || noexpire || 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 (ci->HasFlag(CI_SUSPENDED)) - { - if (Config->CSSuspendExpire && Anope::CurTime - ci->last_used >= Config->CSSuspendExpire) - expire = true; - } - else if (!ci->c && Config->CSExpire && Anope::CurTime - ci->last_used >= Config->CSExpire) - expire = true; - - if (ci->HasFlag(CI_NO_EXPIRE)) - expire = false; - - if (expire) - { - EventReturn MOD_RESULT; - FOREACH_RESULT(I_OnPreChanExpire, OnPreChanExpire(ci)); - if (MOD_RESULT == EVENT_STOP) - continue; - - Anope::string extra; - if (ci->HasFlag(CI_SUSPENDED)) - extra = "suspended "; - - Log(LOG_NORMAL, "chanserv/expire") << "Expiring " << extra << "channel " << ci->name << " (founder: " << (ci->GetFounder() ? ci->GetFounder()->display : "(none)") << ")"; - FOREACH_MOD(I_OnChanExpire, OnChanExpire(ci)); - delete ci; - } - } - } -}; - -class CSRegister : public Module -{ - CommandCSRegister commandcsregister; - ExpireCallback ecb; - - public: - CSRegister(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandcsregister(this), ecb(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandcsregister); - ModuleManager::Attach(I_OnDelChan, this); - } - - void OnDelChan(ChannelInfo *ci) - { - if (ci->c && ci->c->HasMode(CMODE_REGISTERED)) - ci->c->RemoveMode(NULL, CMODE_REGISTERED, "", false); - } -}; - -MODULE_INIT(CSRegister) diff --git a/modules/core/cs_saset.cpp b/modules/core/cs_saset.cpp deleted file mode 100644 index 699c5bc2c..000000000 --- a/modules/core/cs_saset.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/* ChanServ core functions - * - * (C) 2003-2011 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 CommandCSSASet : public Command -{ - public: - CommandCSSASet(Module *creator) : Command(creator, "chanserv/saset", 2, 3) - { - this->SetDesc(_("Forcefully set channel options and information")); - this->SetSyntax(_("\037option\037 \037channel\037 \037parameters\037")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - this->OnSyntaxError(source, ""); - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Allows Services Operators to forcefully change settings\n" - "on channels.\n" - " \n" - "Available options:")); - Anope::string this_name = source.command; - for (BotInfo::command_map::iterator it = source.owner->commands.begin(), it_end = source.owner->commands.end(); it != it_end; ++it) - { - const Anope::string &c_name = it->first; - CommandInfo &info = it->second; - if (c_name.find_ci(this_name + " ") == 0) - { - service_reference command(info.name); - if (command) - { - source.command = it->first; - command->OnServHelp(source); - } - } - } - source.Reply(_("Type \002%s%s HELP SASET \037option\037\002 for more information on a\n" - "particular option."), Config->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str()); - return true; - } -}; - -class CSSASet : public Module -{ - CommandCSSASet commandcssaset; - - public: - CSSASet(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandcssaset(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandcssaset); - } -}; - -MODULE_INIT(CSSASet) diff --git a/modules/core/cs_saset_noexpire.cpp b/modules/core/cs_saset_noexpire.cpp deleted file mode 100644 index 1e085496f..000000000 --- a/modules/core/cs_saset_noexpire.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/* ChanServ core functions - * - * (C) 2003-2011 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 CommandCSSASetNoexpire : public Command -{ - public: - CommandCSSASetNoexpire(Module *creator) : Command(creator, "chanserv/saset/noexpire", 2, 2) - { - this->SetDesc(_("Prevent the channel from expiring")); - this->SetDesc(_("\037channel\037 {ON | OFF}")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - ChannelInfo *ci = cs_findchan(params[0]); - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - if (source.permission.empty() && !ci->HasPriv(u, CA_SET)) - { - source.Reply(ACCESS_DENIED); - return; - } - - if (params[1].equals_ci("ON")) - { - ci->SetFlag(CI_NO_EXPIRE); - source.Reply(_("Channel %s \002will not\002 expire."), ci->name.c_str()); - } - else if (params[1].equals_ci("OFF")) - { - ci->UnsetFlag(CI_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 &) - { - 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 CSSetNoexpire : public Module -{ - CommandCSSASetNoexpire commandcssasetnoexpire; - - public: - CSSetNoexpire(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandcssasetnoexpire(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandcssasetnoexpire); - } -}; - -MODULE_INIT(CSSetNoexpire) diff --git a/modules/core/cs_set.cpp b/modules/core/cs_set.cpp deleted file mode 100644 index 65719ce91..000000000 --- a/modules/core/cs_set.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/* ChanServ core functions - * - * (C) 2003-2011 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 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 ¶ms) - { - this->OnSyntaxError(source, ""); - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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; - for (BotInfo::command_map::iterator it = source.owner->commands.begin(), it_end = source.owner->commands.end(); it != it_end; ++it) - { - const Anope::string &c_name = it->first; - CommandInfo &info = it->second; - if (c_name.find_ci(this_name + " ") == 0) - { - service_reference command(info.name); - if (command) - { - source.command = it->first; - command->OnServHelp(source); - } - } - } - source.Reply(_("Type \002%s%s HELP SET \037option\037\002 for more information on a\n" - "particular option."), Config->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str()); - return true; - } -}; - -class CSSet : public Module -{ - CommandCSSet commandcsset; - - public: - CSSet(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandcsset(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandcsset); - } -}; - -MODULE_INIT(CSSet) diff --git a/modules/core/cs_set_bantype.cpp b/modules/core/cs_set_bantype.cpp deleted file mode 100644 index 94d2aa151..000000000 --- a/modules/core/cs_set_bantype.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/* ChanServ core functions - * - * (C) 2003-2011 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 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 ¶ms) - { - User *u = source.u; - ChannelInfo *ci = cs_findchan(params[0]); - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - if (source.permission.empty() && !ci->HasPriv(u, CA_SET)) - { - source.Reply(ACCESS_DENIED); - return; - } - - try - { - int16 new_type = convertTo(params[1]); - if (new_type < 0 || new_type > 3) - throw ConvertException("Invalid range"); - 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()); - } - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &) - { - 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 CommandCSSASetBanType : public CommandCSSetBanType -{ - public: - CommandCSSASetBanType(Module *creator) : CommandCSSetBanType(creator, "chanserv/saset/bantype") - { - } -}; - -class CSSetBanType : public Module -{ - CommandCSSetBanType commandcssetbantype; - CommandCSSASetBanType commandcssasetbantype; - - public: - CSSetBanType(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandcssetbantype(this), commandcssasetbantype(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandcssetbantype); - ModuleManager::RegisterService(&commandcssasetbantype); - } -}; - -MODULE_INIT(CSSetBanType) diff --git a/modules/core/cs_set_description.cpp b/modules/core/cs_set_description.cpp deleted file mode 100644 index 5c2cd447d..000000000 --- a/modules/core/cs_set_description.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/* ChanServ core functions - * - * (C) 2003-2011 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 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 ¶ms) - { - User *u = source.u; - ChannelInfo *ci = cs_findchan(params[0]); - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - if (source.permission.empty() && !ci->HasPriv(u, CA_SET)) - { - source.Reply(ACCESS_DENIED); - return; - } - - if (params.size() > 1) - { - ci->desc = params[1]; - source.Reply(_("Description of %s changed to \002%s\002."), ci->name.c_str(), ci->desc.c_str()); - } - else - { - ci->desc.clear(); - source.Reply(_("Description of %s unset."), ci->name.c_str()); - } - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &) - { - 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 CommandCSSASetDescription : public CommandCSSetDescription -{ - public: - CommandCSSASetDescription(Module *creator) : CommandCSSetDescription(creator, "chanserv/saset/description") - { - } -}; - -class CSSetDescription : public Module -{ - CommandCSSetDescription commandcssetdescription; - CommandCSSASetDescription commandcssasetdescription; - - public: - CSSetDescription(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandcssetdescription(this), commandcssasetdescription(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandcssetdescription); - ModuleManager::RegisterService(&commandcssasetdescription); - } -}; - -MODULE_INIT(CSSetDescription) diff --git a/modules/core/cs_set_founder.cpp b/modules/core/cs_set_founder.cpp deleted file mode 100644 index 2169af972..000000000 --- a/modules/core/cs_set_founder.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/* ChanServ core functions - * - * (C) 2003-2011 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 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 ¶ms) - { - User *u = source.u; - ChannelInfo *ci = cs_findchan(params[0]); - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - if (source.permission.empty() && !ci->HasPriv(u, CA_SET)) - { - source.Reply(ACCESS_DENIED); - return; - } - - if (source.permission.empty() && (ci->HasFlag(CI_SECUREFOUNDER) ? !IsFounder(u, ci) : !ci->HasPriv(u, CA_FOUNDER))) - { - source.Reply(ACCESS_DENIED); - return; - } - - NickAlias *na = findnick(params[1]); - - if (!na) - { - source.Reply(NICK_X_NOT_REGISTERED, params[1].c_str()); - return; - } - - NickCore *nc = na->nc; - if (Config->CSMaxReg && nc->channelcount >= Config->CSMaxReg && !u->HasPriv("chanserv/no-register-limit")) - { - source.Reply(_("\002%s\002 has too many channels registered."), na->nick.c_str()); - return; - } - - Log(!source.permission.empty() ? LOG_ADMIN : LOG_COMMAND, u, this, ci) << "to change the founder to " << nc->display; - - ci->SetFounder(nc); - - source.Reply(_("Founder of %s changed to \002%s\002."), ci->name.c_str(), na->nick.c_str()); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &) - { - 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 CommandCSSASetFounder : public CommandCSSetFounder -{ - public: - CommandCSSASetFounder(Module *creator) : CommandCSSetFounder(creator, "chanserv/saset/founder") - { - } -}; - -class CSSetFounder : public Module -{ - CommandCSSetFounder commandcssetfounder; - CommandCSSASetFounder commandcssasetfounder; - - public: - CSSetFounder(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandcssetfounder(this), commandcssasetfounder(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandcssetfounder); - ModuleManager::RegisterService(&commandcssasetfounder); - } -}; - -MODULE_INIT(CSSetFounder) diff --git a/modules/core/cs_set_keeptopic.cpp b/modules/core/cs_set_keeptopic.cpp deleted file mode 100644 index 05578a2e5..000000000 --- a/modules/core/cs_set_keeptopic.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/* ChanServ core functions - * - * (C) 2003-2011 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 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 ¶ms) - { - User *u = source.u; - ChannelInfo *ci = cs_findchan(params[0]); - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - if (source.permission.empty() && !ci->HasPriv(u, CA_SET)) - { - source.Reply(ACCESS_DENIED); - return; - } - - if (params[1].equals_ci("ON")) - { - ci->SetFlag(CI_KEEPTOPIC); - source.Reply(_("Topic retention option for %s is now \002on\002."), ci->name.c_str()); - } - else if (params[1].equals_ci("OFF")) - { - ci->UnsetFlag(CI_KEEPTOPIC); - source.Reply(_("Topic retention option for %s is now \002off\002."), ci->name.c_str()); - } - else - this->OnSyntaxError(source, "KEEPTOPIC"); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Enables or disables the \002topic retention\002 option for a \n" - "channel. When \002topic retention\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."), this->name.c_str(), source.owner->nick.c_str()); - return true; - } -}; - -class CommandCSSASetKeepTopic : public CommandCSSetKeepTopic -{ - public: - CommandCSSASetKeepTopic(Module *creator) : CommandCSSetKeepTopic(creator, "chanserv/saset/keeptopic") - { - } -}; - -class CSSetKeepTopic : public Module -{ - CommandCSSetKeepTopic commandcssetkeeptopic; - CommandCSSASetKeepTopic commandcssasetkeeptopic; - - public: - CSSetKeepTopic(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandcssetkeeptopic(this), commandcssasetkeeptopic(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandcssetkeeptopic); - ModuleManager::RegisterService(&commandcssasetkeeptopic); - } -}; - -MODULE_INIT(CSSetKeepTopic) diff --git a/modules/core/cs_set_opnotice.cpp b/modules/core/cs_set_opnotice.cpp deleted file mode 100644 index 018b89437..000000000 --- a/modules/core/cs_set_opnotice.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/* ChanServ core functions - * - * (C) 2003-2011 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 CommandCSSetOpNotice : public Command -{ - public: - CommandCSSetOpNotice(Module *creator, const Anope::string &cname = "chanserv/set/notice") : Command(creator, cname, 2, 2) - { - this->SetDesc(_("Send a notice when OP/DEOP commands are used")); - this->SetSyntax(_("\037channel\037 {ON | OFF}")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - ChannelInfo *ci = cs_findchan(params[0]); - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - if (source.permission.empty() && !ci->HasPriv(u, CA_SET)) - { - source.Reply(ACCESS_DENIED); - return; - } - - if (params[1].equals_ci("ON")) - { - ci->SetFlag(CI_OPNOTICE); - source.Reply(_("Op-notice option for %s is now \002on\002."), ci->name.c_str()); - } - else if (params[1].equals_ci("OFF")) - { - ci->UnsetFlag(CI_OPNOTICE); - source.Reply(_("Op-notice option for %s is now \002off\002."), ci->name.c_str()); - } - else - this->OnSyntaxError(source, "OPNOTICE"); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Enables or disables the \002op-notice\002 option for a channel.\n" - "When \002op-notice\002 is set, %s will send a notice to the\n" - "channel whenever the \002OP\002 or \002DEOP\002 commands are used for a user\n" - "in the channel."), this->name.c_str(), source.owner->nick.c_str()); - return true; - } -}; - -class CommandCSSASetOpNotice : public CommandCSSetOpNotice -{ - public: - CommandCSSASetOpNotice(Module *creator) : CommandCSSetOpNotice(creator, "chanserv/saset/opnotice") - { - } -}; - -class CSSetOpNotice : public Module -{ - CommandCSSetOpNotice commandcssetopnotice; - CommandCSSASetOpNotice commandcssasetopnotice; - - public: - CSSetOpNotice(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandcssetopnotice(this), commandcssasetopnotice(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandcssetopnotice); - ModuleManager::RegisterService(&commandcssasetopnotice); - } -}; - -MODULE_INIT(CSSetOpNotice) diff --git a/modules/core/cs_set_peace.cpp b/modules/core/cs_set_peace.cpp deleted file mode 100644 index 3a045fc6d..000000000 --- a/modules/core/cs_set_peace.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/* ChanServ core functions - * - * (C) 2003-2011 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 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 ¶ms) - { - User *u = source.u; - ChannelInfo *ci = cs_findchan(params[0]); - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - if (source.permission.empty() && !ci->HasPriv(u, CA_SET)) - { - source.Reply(ACCESS_DENIED); - return; - } - - if (params[1].equals_ci("ON")) - { - ci->SetFlag(CI_PEACE); - source.Reply(_("Peace option for %s is now \002on\002."), ci->name.c_str()); - } - else if (params[1].equals_ci("OFF")) - { - ci->UnsetFlag(CI_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 &) - { - 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.owner->nick.c_str()); - return true; - } -}; - -class CommandCSSASetPeace : public CommandCSSetPeace -{ - public: - CommandCSSASetPeace(Module *creator) : CommandCSSetPeace(creator, "chanserv/saset/peace") - { - } -}; - -class CSSetPeace : public Module -{ - CommandCSSetPeace commandcssetpeace; - CommandCSSASetPeace commandcssasetpeace; - - public: - CSSetPeace(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandcssetpeace(this), commandcssasetpeace(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandcssetpeace); - ModuleManager::RegisterService(&commandcssasetpeace); - } -}; - -MODULE_INIT(CSSetPeace) diff --git a/modules/core/cs_set_persist.cpp b/modules/core/cs_set_persist.cpp deleted file mode 100644 index 3cc9564c4..000000000 --- a/modules/core/cs_set_persist.cpp +++ /dev/null @@ -1,180 +0,0 @@ -/* ChanServ core functions - * - * (C) 2003-2011 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 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 ¶ms) - { - User *u = source.u; - ChannelInfo *ci = cs_findchan(params[0]); - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - if (source.permission.empty() && !ci->HasPriv(u, CA_SET)) - { - source.Reply(ACCESS_DENIED); - return; - } - - ChannelMode *cm = ModeManager::FindChannelModeByName(CMODE_PERM); - - if (params[1].equals_ci("ON")) - { - if (!ci->HasFlag(CI_PERSIST)) - { - ci->SetFlag(CI_PERSIST); - if (ci->c) - ci->c->SetFlag(CH_PERSIST); - - /* Channel doesn't exist, create it */ - if (!ci->c) - { - Channel *c = new Channel(ci->name); - if (ci->bi) - ci->bi->Join(c); - } - - /* No botserv bot, no channel mode */ - /* Give them ChanServ - * Yes, this works fine with no Config->s_BotServ - */ - if (!ci->bi && !cm) - { - BotInfo *bi = findbot(Config->ChanServ); - if (!bi) - { - source.Reply(_("ChanServ is required to enable persist on this network.")); - return; - } - bi->Assign(NULL, ci); - if (!ci->c->FindUser(bi)) - bi->Join(ci->c); - } - - /* Set the perm mode */ - if (cm) - { - if (ci->c && !ci->c->HasMode(CMODE_PERM)) - ci->c->SetMode(NULL, cm); - /* Add it to the channels mlock */ - ci->SetMLock(cm, true); - } - } - - source.Reply(_("Channel \002%s\002 is now persistant."), ci->name.c_str()); - } - else if (params[1].equals_ci("OFF")) - { - if (ci->HasFlag(CI_PERSIST)) - { - ci->UnsetFlag(CI_PERSIST); - if (ci->c) - ci->c->UnsetFlag(CH_PERSIST); - - /* Unset perm mode */ - if (cm) - { - if (ci->c && ci->c->HasMode(CMODE_PERM)) - ci->c->RemoveMode(NULL, cm); - /* Remove from mlock */ - ci->RemoveMLock(cm); - } - - /* No channel mode, no BotServ, but using ChanServ as the botserv bot - * which was assigned when persist was set on - */ - if (!cm && Config->BotServ.empty() && ci->bi) - { - BotInfo *bi = findbot(Config->ChanServ); - if (!bi) - { - source.Reply(_("ChanServ is required to enable persist on this network.")); - return; - } - /* Unassign bot */ - bi->UnAssign(NULL, ci); - } - } - - source.Reply(_("Channel \002%s\002 is no longer persistant."), ci->name.c_str()); - } - else - this->OnSyntaxError(source, "PERSIST"); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Enables or disables the persistant channel setting.\n" - "When persistant is set, the service bot will remain\n" - "in the channel when it has emptied of users.\n" - " \n" - "If your IRCd does not a permanent (persistant) 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 BotServ enabled and does\n" - "not have a permanent channel mode, ChanServ 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 (persistant) channel mode\n" - "and is is set or unset (for any reason, including MLOCK),\n" - "persist is automatically set and unset for the channel aswell.\n" - "Additionally, services will set or unset this mode when you\n" - "set persist on or off.")); - return true; - } -}; - -class CommandCSSASetPersist : public CommandCSSetPersist -{ - public: - CommandCSSASetPersist(Module *creator) : CommandCSSetPersist(creator, "chanserv/saset/persist") - { - } -}; - -class CSSetPersist : public Module -{ - CommandCSSetPersist commandcssetpeace; - CommandCSSASetPersist commandcssasetpeace; - - public: - CSSetPersist(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandcssetpeace(this), commandcssasetpeace(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandcssetpeace); - ModuleManager::RegisterService(&commandcssasetpeace); - } -}; - -MODULE_INIT(CSSetPersist) diff --git a/modules/core/cs_set_private.cpp b/modules/core/cs_set_private.cpp deleted file mode 100644 index 108b11569..000000000 --- a/modules/core/cs_set_private.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/* ChanServ core functions - * - * (C) 2003-2011 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 CommandCSSetPrivate : public Command -{ - public: - CommandCSSetPrivate(Module *creator, const Anope::string &cname = "chanserv/set/private") : Command(creator, cname, 2, 2) - { - this->SetDesc(_("Hide channel from LIST command")); - this->SetSyntax(_("\037channel\037 {ON | OFF}")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - ChannelInfo *ci = cs_findchan(params[0]); - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - if (source.permission.empty() && !ci->HasPriv(u, CA_SET)) - { - source.Reply(ACCESS_DENIED); - return; - } - - if (params[1].equals_ci("ON")) - { - ci->SetFlag(CI_PRIVATE); - source.Reply(_("Private option for %s is now \002on\002."), ci->name.c_str()); - } - else if (params[1].equals_ci("OFF")) - { - ci->UnsetFlag(CI_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 &) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Enables or disables the \002private\002 option for a channel.\n" - "When \002private\002 is set, a \002%s%s LIST\002 will not\n" - "include the channel in any lists."), - Config->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str()); - return true; - } -}; - -class CommandCSSASetPrivate : public CommandCSSetPrivate -{ - public: - CommandCSSASetPrivate(Module *creator) : CommandCSSetPrivate(creator, "chanserv/saset/private") - { - } -}; - -class CSSetPrivate : public Module -{ - CommandCSSetPrivate commandcssetprivate; - CommandCSSASetPrivate commandcssasetprivate; - - public: - CSSetPrivate(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandcssetprivate(this), commandcssasetprivate(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandcssetprivate); - ModuleManager::RegisterService(&commandcssasetprivate); - } -}; - -MODULE_INIT(CSSetPrivate) diff --git a/modules/core/cs_set_restricted.cpp b/modules/core/cs_set_restricted.cpp deleted file mode 100644 index 5d675d2e7..000000000 --- a/modules/core/cs_set_restricted.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/* ChanServ core functions - * - * (C) 2003-2011 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 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 ¶ms) - { - User *u = source.u; - ChannelInfo *ci = cs_findchan(params[0]); - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - if (source.permission.empty() && !ci->HasPriv(u, CA_SET)) - { - source.Reply(ACCESS_DENIED); - return; - } - - if (params[1].equals_ci("ON")) - { - ci->SetFlag(CI_RESTRICTED); - source.Reply(_("Restricted access option for %s is now \002on\002."), ci->name.c_str()); - } - else if (params[1].equals_ci("OFF")) - { - ci->UnsetFlag(CI_RESTRICTED); - source.Reply(_("Restricted access option for %s is now \002off\002."), ci->name.c_str()); - } - else - this->OnSyntaxError(source, "RESTRICTED"); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &) - { - 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 CommandCSSASetRestricted : public CommandCSSetRestricted -{ - public: - CommandCSSASetRestricted(Module *creator) : CommandCSSetRestricted(creator, "chanserv/saset/restricted") - { - } -}; - -class CSSetRestricted : public Module -{ - CommandCSSetRestricted commandcssetrestricted; - CommandCSSASetRestricted commandcssasetrestricted; - - public: - CSSetRestricted(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandcssetrestricted(this), commandcssasetrestricted(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandcssetrestricted); - ModuleManager::RegisterService(&commandcssasetrestricted); - } -}; - -MODULE_INIT(CSSetRestricted) diff --git a/modules/core/cs_set_secure.cpp b/modules/core/cs_set_secure.cpp deleted file mode 100644 index 26e8c5768..000000000 --- a/modules/core/cs_set_secure.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/* ChanServ core functions - * - * (C) 2003-2011 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 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 ¶ms) - { - User *u = source.u; - ChannelInfo *ci = cs_findchan(params[0]); - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - if (source.permission.empty() && !ci->HasPriv(u, CA_SET)) - { - source.Reply(ACCESS_DENIED); - return; - } - - if (params[1].equals_ci("ON")) - { - ci->SetFlag(CI_SECURE); - source.Reply(_("Secure option for %s is now \002on\002."), ci->name.c_str()); - } - else if (params[1].equals_ci("OFF")) - { - ci->UnsetFlag(CI_SECURE); - source.Reply(_("Secure option for %s is now \002off\002."), ci->name.c_str()); - } - else - this->OnSyntaxError(source, "SECURE"); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Enables or disables security features for a\n" - "channel. When \002%s\002 is set, only users who have\n" - "registered their nicknames and IDENTIFY'd\n" - "with their password will be given access to the channel\n" - "as controlled by the access list."), this->name.c_str()); - return true; - } -}; - -class CommandCSSASetSecure : public CommandCSSetSecure -{ - public: - CommandCSSASetSecure(Module *creator) : CommandCSSetSecure(creator, "chanserv/saset/secure") - { - } -}; - -class CSSetSecure : public Module -{ - CommandCSSetSecure commandcssetsecure; - CommandCSSASetSecure commandcssasetsecure; - - public: - CSSetSecure(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandcssetsecure(this), commandcssasetsecure(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandcssetsecure); - ModuleManager::RegisterService(&commandcssasetsecure); - } -}; - -MODULE_INIT(CSSetSecure) diff --git a/modules/core/cs_set_securefounder.cpp b/modules/core/cs_set_securefounder.cpp deleted file mode 100644 index 7afc2e32d..000000000 --- a/modules/core/cs_set_securefounder.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/* ChanServ core functions - * - * (C) 2003-2011 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 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 ¶ms) - { - User *u = source.u; - ChannelInfo *ci = cs_findchan(params[0]); - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - - if (source.permission.empty() && ci->HasFlag(CI_SECUREFOUNDER) ? !IsFounder(u, ci) : !ci->HasPriv(u, CA_FOUNDER)) - { - source.Reply(ACCESS_DENIED); - return; - } - - if (params[1].equals_ci("ON")) - { - ci->SetFlag(CI_SECUREFOUNDER); - source.Reply(_("Secure founder option for %s is now \002on\002."), ci->name.c_str()); - } - else if (params[1].equals_ci("OFF")) - { - ci->UnsetFlag(CI_SECUREFOUNDER); - source.Reply(_("Secure founder option for %s is now \002off\002."), ci->name.c_str()); - } - else - this->OnSyntaxError(source, "SECUREFOUNDER"); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &) - { - 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 password, its founder and its\n" - "successor, and not those who have founder level access through\n" - "the access/qop command.")); - return true; - } -}; - -class CommandCSSASetSecureFounder : public CommandCSSetSecureFounder -{ - public: - CommandCSSASetSecureFounder(Module *creator) : CommandCSSetSecureFounder(creator, "chanserv/saset/securefounder") - { - } -}; - -class CSSetSecureFounder : public Module -{ - CommandCSSetSecureFounder commandcssetsecurefounder; - CommandCSSASetSecureFounder commandcssasetsecurefounder; - - public: - CSSetSecureFounder(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandcssetsecurefounder(this), commandcssasetsecurefounder(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandcssetsecurefounder); - ModuleManager::RegisterService(&commandcssasetsecurefounder); - } -}; - -MODULE_INIT(CSSetSecureFounder) diff --git a/modules/core/cs_set_secureops.cpp b/modules/core/cs_set_secureops.cpp deleted file mode 100644 index 4c3c54b05..000000000 --- a/modules/core/cs_set_secureops.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/* ChanServ core functions - * - * (C) 2003-2011 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 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 ¶ms) - { - User *u = source.u; - ChannelInfo *ci = cs_findchan(params[0]); - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - if (source.permission.empty() && !ci->HasPriv(u, CA_SET)) - { - source.Reply(ACCESS_DENIED); - return; - } - - if (params[1].equals_ci("ON")) - { - ci->SetFlag(CI_SECUREOPS); - source.Reply(_("Secure ops option for %s is now \002on\002."), ci->name.c_str()); - } - else if (params[1].equals_ci("OFF")) - { - ci->UnsetFlag(CI_SECUREOPS); - source.Reply(_("Secure ops option for %s is now \002off\002."), ci->name.c_str()); - } - else - this->OnSyntaxError(source, "SECUREOPS"); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &) - { - 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 userlist\n" - "will not be allowed chanop status.")); - return true; - } -}; - -class CommandCSSASetSecureOps : public CommandCSSetSecureOps -{ - public: - CommandCSSASetSecureOps(Module *creator) : CommandCSSetSecureOps(creator, "chanserv/saset/secureops") - { - } -}; - -class CSSetSecureOps : public Module -{ - CommandCSSetSecureOps commandcssetsecureops; - CommandCSSASetSecureOps commandcssasetsecureops; - - public: - CSSetSecureOps(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandcssetsecureops(this), commandcssasetsecureops(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandcssetsecureops); - ModuleManager::RegisterService(&commandcssasetsecureops); - } -}; - -MODULE_INIT(CSSetSecureOps) diff --git a/modules/core/cs_set_signkick.cpp b/modules/core/cs_set_signkick.cpp deleted file mode 100644 index 0d421576d..000000000 --- a/modules/core/cs_set_signkick.cpp +++ /dev/null @@ -1,104 +0,0 @@ -/* ChanServ core functions - * - * (C) 2003-2011 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 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 KICK command")); - this->SetSyntax(_("\037channel\037 SIGNKICK {ON | LEVEL | OFF}")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - ChannelInfo *ci = cs_findchan(params[0]); - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - if (source.permission.empty() && !ci->HasPriv(u, CA_SET)) - { - source.Reply(ACCESS_DENIED); - return; - } - - if (params[1].equals_ci("ON")) - { - ci->SetFlag(CI_SIGNKICK); - ci->UnsetFlag(CI_SIGNKICK_LEVEL); - source.Reply(_("Signed kick option for %s is now \002on\002."), ci->name.c_str()); - } - else if (params[1].equals_ci("LEVEL")) - { - ci->SetFlag(CI_SIGNKICK_LEVEL); - ci->UnsetFlag(CI_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()); - } - else if (params[1].equals_ci("OFF")) - { - ci->UnsetFlag(CI_SIGNKICK); - ci->UnsetFlag(CI_SIGNKICK_LEVEL); - source.Reply(_("Signed kick option for %s is now \002off\002."), ci->name.c_str()); - } - else - this->OnSyntaxError(source, "SIGNKICK"); - } - - bool OnHelp(CommandSource &source, const Anope::string &) - { - 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" - "KICK 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 CommandCSSASetSignKick : public CommandCSSetSignKick -{ - public: - CommandCSSASetSignKick(Module *creator) : CommandCSSetSignKick(creator, "chanserv/saset/signkick") - { - } -}; - -class CSSetSignKick : public Module -{ - CommandCSSetSignKick commandcssetsignkick; - CommandCSSASetSignKick commandcssasetsignkick; - - public: - CSSetSignKick(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandcssetsignkick(this), commandcssasetsignkick(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandcssetsignkick); - ModuleManager::RegisterService(&commandcssasetsignkick); - } -}; - -MODULE_INIT(CSSetSignKick) diff --git a/modules/core/cs_set_successor.cpp b/modules/core/cs_set_successor.cpp deleted file mode 100644 index f7ed8dba9..000000000 --- a/modules/core/cs_set_successor.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/* ChanServ core functions - * - * (C) 2003-2011 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 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 ¶ms) - { - User *u = source.u; - ChannelInfo *ci = cs_findchan(params[0]); - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - if (source.permission.empty() && !ci->HasPriv(u, CA_SET)) - { - source.Reply(ACCESS_DENIED); - return; - } - - if (source.permission.empty() && ci->HasFlag(CI_SECUREFOUNDER) ? !IsFounder(u, ci) : !ci->HasPriv(u, CA_FOUNDER)) - { - source.Reply(ACCESS_DENIED); - return; - } - - NickCore *nc; - - if (params.size() > 1) - { - NickAlias *na = findnick(params[1]); - - if (!na) - { - source.Reply(NICK_X_NOT_REGISTERED, params[1].c_str()); - return; - } - if (na->nc == ci->GetFounder()) - { - source.Reply(_("%s cannot be the successor on channel %s they are the founder."), na->nick.c_str(), ci->name.c_str()); - return; - } - nc = na->nc; - } - else - nc = NULL; - - Log(!source.permission.empty() ? LOG_ADMIN : LOG_COMMAND, u, this, ci) << "to change the successor from " << (ci->successor ? ci->successor->display : "none") << " to " << (nc ? nc->display : "none"); - - ci->successor = nc; - - if (nc) - source.Reply(_("Successor for %s 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 &) - { - 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. 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. The new\n" - "nickname must be a registered one."), Config->CSMaxReg); - return true; - } -}; - -class CommandCSSASetSuccessor : public CommandCSSetSuccessor -{ - public: - CommandCSSASetSuccessor(Module *creator) : CommandCSSetSuccessor(creator, "chanserv/saset/successor") - { - } -}; - -class CSSetSuccessor : public Module -{ - CommandCSSetSuccessor commandcssetsuccessor; - CommandCSSASetSuccessor commandcssasetsuccessor; - - public: - CSSetSuccessor(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandcssetsuccessor(this), commandcssasetsuccessor(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandcssetsuccessor); - ModuleManager::RegisterService(&commandcssasetsuccessor); - } -}; - -MODULE_INIT(CSSetSuccessor) diff --git a/modules/core/cs_set_topiclock.cpp b/modules/core/cs_set_topiclock.cpp deleted file mode 100644 index e670385ba..000000000 --- a/modules/core/cs_set_topiclock.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/* ChanServ core functions - * - * (C) 2003-2011 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 CommandCSSetTopicLock : public Command -{ - public: - CommandCSSetTopicLock(Module *creator, const Anope::string &cname = "chanserv/set/topiclock") : Command(creator, cname, 2, 2) - { - this->SetDesc(_("Topic can only be changed with TOPIC")); - this->SetSyntax(_("\037channel\037 {ON | OFF}")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - ChannelInfo *ci = cs_findchan(params[0]); - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - if (source.permission.empty() && !ci->HasPriv(u, CA_SET)) - { - source.Reply(ACCESS_DENIED); - return; - } - - if (params[1].equals_ci("ON")) - { - ci->SetFlag(CI_TOPICLOCK); - source.Reply(_("Topic lock option for %s is now \002on\002."), ci->name.c_str()); - } - else if (params[1].equals_ci("OFF")) - { - ci->UnsetFlag(CI_TOPICLOCK); - source.Reply(_("Topic lock option for %s is now \002off\002."), ci->name.c_str()); - } - else - this->OnSyntaxError(source, "TOPICLOCK"); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Enables or disables the \002topic lock\002 option for a channel.\n" - "When \002topic lock\002 is set, the channel topic will be unchangable\n" - " except via the \002TOPIC\002 command.")); - return true; - } -}; - -class CommandCSSASetTopicLock : public CommandCSSetTopicLock -{ - public: - CommandCSSASetTopicLock(Module *creator) : CommandCSSetTopicLock(creator, "chanserv/saset/topiclock") - { - } -}; - -class CSSetTopicLock : public Module -{ - CommandCSSetTopicLock commandcssettopiclock; - CommandCSSASetTopicLock commandcssasettopiclock; - - public: - CSSetTopicLock(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandcssettopiclock(this), commandcssasettopiclock(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandcssettopiclock); - ModuleManager::RegisterService(&commandcssasettopiclock); - } -}; - -MODULE_INIT(CSSetTopicLock) diff --git a/modules/core/cs_suspend.cpp b/modules/core/cs_suspend.cpp deleted file mode 100644 index d59ffb44b..000000000 --- a/modules/core/cs_suspend.cpp +++ /dev/null @@ -1,158 +0,0 @@ -/* ChanServ core functions - * - * (C) 2003-2011 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 CommandCSSuspend : public Command -{ - public: - CommandCSSuspend(Module *creator) : Command(creator, "chanserv/suspend", 1, 2) - { - this->SetDesc(_("Prevent a channel from being used preserving channel data and settings")); - this->SetSyntax(_("\037channel\037 [\037reason\037]")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - const Anope::string &reason = params.size() > 1 ? params[1] : ""; - - User *u = source.u; - - if (Config->ForceForbidReason && reason.empty()) - { - this->OnSyntaxError(source, ""); - return; - } - - if (readonly) - source.Reply(READ_ONLY_MODE); - - ChannelInfo *ci = cs_findchan(params[0]); - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - ci->SetFlag(CI_SUSPENDED); - ci->Extend("suspend_by", new ExtensibleItemRegular(u->nick)); - if (!reason.empty()) - ci->Extend("suspend_reason", new ExtensibleItemRegular(u->nick)); - - if (ci->c) - { - for (CUserList::iterator it = ci->c->users.begin(), it_end = ci->c->users.end(); it != it_end; ) - { - UserContainer *uc = *it++; - - if (uc->user->HasMode(UMODE_OPER)) - continue; - - ci->c->Kick(NULL, uc->user, "%s", !reason.empty() ? reason.c_str() : translate(uc->user, _("This channel has been suspended."))); - } - } - - Log(LOG_ADMIN, u, this, ci) << (!reason.empty() ? reason : "No reason"); - source.Reply(_("Channel \002%s\002 is now suspended."), ci->name.c_str()); - - FOREACH_MOD(I_OnChanSuspend, OnChanSuspend(ci)); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Disallows anyone from using the given channel.\n" - "May be cancelled by using the UNSUSPEND\n" - "command to preserve all previous channel data/settings.\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 ¶ms) - { - User *u = source.u; - - if (readonly) - source.Reply(READ_ONLY_MODE); - - ChannelInfo *ci = cs_findchan(params[0]); - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - /* Only UNSUSPEND already suspended channels */ - if (!ci->HasFlag(CI_SUSPENDED)) - { - source.Reply(_("Couldn't release channel \002%s\002!"), ci->name.c_str()); - return; - } - - Anope::string by, reason; - ci->GetExtRegular("suspend_by", by); - ci->GetExtRegular("suspend_reason", reason); - Log(LOG_ADMIN, u, this, ci) << " which was suspended by " << by << " for: " << (!reason.empty() ? reason : "No reason"); - - ci->UnsetFlag(CI_SUSPENDED); - ci->Shrink("suspend_by"); - ci->Shrink("suspend_reason"); - - source.Reply(_("Channel \002%s\002 is now released."), ci->name.c_str()); - - FOREACH_MOD(I_OnChanUnsuspend, OnChanUnsuspend(ci)); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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; - - public: - CSSuspend(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandcssuspend(this), commandcsunsuspend(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandcssuspend); - ModuleManager::RegisterService(&commandcsunsuspend); - } -}; - -MODULE_INIT(CSSuspend) diff --git a/modules/core/cs_topic.cpp b/modules/core/cs_topic.cpp deleted file mode 100644 index 1b5de11c3..000000000 --- a/modules/core/cs_topic.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/* ChanServ core functions - * - * (C) 2003-2011 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 CommandCSTopic : public Command -{ - public: - CommandCSTopic(Module *creator) : Command(creator, "chanserv/topic", 1, 2) - { - this->SetDesc(_("Manipulate the topic of the specified channel")); - this->SetSyntax(_("\037channel\037 [\037topic\037]")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - const Anope::string &topic = params.size() > 1 ? params[1] : ""; - - User *u = source.u; - - ChannelInfo *ci = cs_findchan(params[0]); - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - if (!ci->c) - source.Reply(CHAN_X_NOT_IN_USE, ci->name.c_str()); - else if (!ci->HasPriv(u, CA_TOPIC) && !u->HasCommand("chanserv/chanserv/topic")) - source.Reply(ACCESS_DENIED); - else - { - bool has_topiclock = ci->HasFlag(CI_TOPICLOCK); - ci->UnsetFlag(CI_TOPICLOCK); - ci->c->ChangeTopic(u->nick, topic, Anope::CurTime); - if (has_topiclock) - ci->SetFlag(CI_TOPICLOCK); - - bool override = !ci->HasPriv(u, CA_TOPIC); - Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "to change the topic to " << (!topic.empty() ? topic : "No topic"); - } - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Causes %s to set the channel topic to the one\n" - "specified. If \002topic\002 is not given, then an empty topic\n" - "is set. This command is most useful in conjunction\n" - "with topic lock.\n" - "By default, limited to those with founder access on the\n" - "channel."), source.owner->nick.c_str()); - return true; - } -}; - -class CSTopic : public Module -{ - CommandCSTopic commandcstopic; - - public: - CSTopic(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandcstopic(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandcstopic); - } -}; - -MODULE_INIT(CSTopic) diff --git a/modules/core/cs_unban.cpp b/modules/core/cs_unban.cpp deleted file mode 100644 index cc3c68e91..000000000 --- a/modules/core/cs_unban.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/* ChanServ core functions - * - * (C) 2003-2011 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 CommandCSUnban : public Command -{ - public: - CommandCSUnban(Module *creator) : Command(creator, "chanserv/unban", 1, 2) - { - this->SetDesc(_("Remove all bans preventing a user from entering a channel")); - this->SetSyntax(_("\037channel\037 [\037nick\037]")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - - ChannelInfo *ci = cs_findchan(params[0]); - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - if (ci->c == NULL) - { - source.Reply(CHAN_X_NOT_IN_USE, ci->name.c_str()); - return; - } - - if (!ci->HasPriv(u, CA_UNBAN)) - { - source.Reply(ACCESS_DENIED); - return; - } - - User *u2 = u; - if (params.size() > 1) - u2 = finduser(params[1]); - - if (!u2) - { - source.Reply(NICK_X_NOT_IN_USE, params[1].c_str()); - return; - } - - common_unban(ci, u2, u == u2); - if (u2 == u) - source.Reply(_("You have been unbanned from \002%s\002."), ci->c->name.c_str()); - else - source.Reply(_("\002%s\002 has been unbanned from \002%s\002."), u2->nick.c_str(), ci->c->name.c_str()); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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. \n" - " \n" - "By default, limited to AOPs or those with level 5 and above\n" - "on the channel."), source.owner->nick.c_str()); - return true; - } -}; - -class CSUnban : public Module -{ - CommandCSUnban commandcsunban; - - public: - CSUnban(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandcsunban(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandcsunban); - } -}; - -MODULE_INIT(CSUnban) diff --git a/modules/core/cs_xop.cpp b/modules/core/cs_xop.cpp deleted file mode 100644 index 1b9348026..000000000 --- a/modules/core/cs_xop.cpp +++ /dev/null @@ -1,880 +0,0 @@ -/* ChanServ core functions - * - * (C) 2003-2011 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" - -enum XOPType -{ - XOP_QOP, - XOP_SOP, - XOP_AOP, - XOP_HOP, - XOP_VOP, - XOP_UNKNOWN -}; - -static struct XOPAccess -{ - XOPType type; - Anope::string name; - ChannelAccess access[CA_SIZE]; -} xopAccess[] = { - { XOP_QOP, "QOP", - { - CA_SIGNKICK, - CA_SET, - CA_AUTOOWNER, - CA_OWNERME, - CA_PROTECT, - CA_INFO, - CA_SIZE - } - }, - { XOP_SOP, "SOP", - { - CA_AUTOPROTECT, - CA_AKICK, - CA_BADWORDS, - CA_ASSIGN, - CA_MEMO, - CA_ACCESS_CHANGE, - CA_PROTECTME, - CA_OPDEOP, - CA_SIZE - } - }, - { XOP_AOP, "AOP", - { - CA_TOPIC, - CA_MODE, - CA_GETKEY, - CA_INVITE, - CA_UNBAN, - CA_AUTOOP, - CA_OPDEOPME, - CA_HALFOP, - CA_SAY, - CA_NOKICK, - CA_SIZE - } - }, - { XOP_HOP, "HOP", - { - CA_AUTOHALFOP, - CA_HALFOPME, - CA_KICK, - CA_BAN, - CA_FANTASIA, - CA_SIZE - } - }, - { XOP_VOP, "VOP", - { - CA_AUTOVOICE, - CA_VOICEME, - CA_ACCESS_LIST, - CA_SIZE - } - }, - { XOP_UNKNOWN, "", { } - } -}; - -class XOPChanAccess : public ChanAccess -{ - public: - XOPType type; - - XOPChanAccess(AccessProvider *p) : ChanAccess(p) - { - } - - bool Matches(User *u, NickCore *nc) - { - if (u && (Anope::Match(u->nick, this->mask) || Anope::Match(u->GetMask(), this->mask))) - return true; - else if (nc && Anope::Match(nc->display, this->mask)) - return true; - return false; - } - - bool HasPriv(ChannelAccess priv) - { - for (int i = 0; xopAccess[i].type != XOP_UNKNOWN; ++i) - { - XOPAccess &x = xopAccess[i]; - - if (this->type > x.type) - continue; - - for (int j = 0; x.access[j] != CA_SIZE; ++j) - if (x.access[j] == priv) - return true; - } - - return false; - } - - Anope::string Serialize() - { - for (int i = 0; xopAccess[i].type != XOP_UNKNOWN; ++i) - { - XOPAccess &x = xopAccess[i]; - - if (this->type == x.type) - return x.name; - } - - return ""; - } - - void Unserialize(const Anope::string &data) - { - for (int i = 0; xopAccess[i].type != XOP_UNKNOWN; ++i) - { - XOPAccess &x = xopAccess[i]; - - if (data == x.name) - { - this->type = x.type; - return; - } - } - - this->type = XOP_UNKNOWN; - } - - static XOPType DetermineLevel(ChanAccess *access) - { - if (access->provider->name == "access/xop") - { - XOPChanAccess *xaccess = debug_cast(access); - return xaccess->type; - } - else - { - int count[XOP_UNKNOWN]; - for (int i = 0; i < XOP_UNKNOWN; ++i) - count[i] = 0; - - for (int i = 0; xopAccess[i].type != XOP_UNKNOWN; ++i) - { - XOPAccess &x = xopAccess[i]; - - for (int j = 0; x.access[j] != CA_SIZE; ++j) - if (access->HasPriv(x.access[j])) - ++count[x.type]; - } - - XOPType max = XOP_UNKNOWN; - int maxn = 0; - for (int i = 0; i < XOP_UNKNOWN; ++i) - if (count[i] > maxn) - { - max = static_cast(i); - maxn = count[i]; - } - - return max; - } - } -}; - -class XOPAccessProvider : public AccessProvider -{ - public: - XOPAccessProvider(Module *o) : AccessProvider(o, "access/xop") - { - } - - ChanAccess *Create() - { - return new XOPChanAccess(this); - } -}; - -class XOPListCallback : public NumberList -{ - CommandSource &source; - ChannelInfo *ci; - XOPType type; - Command *c; - bool SentHeader; - public: - XOPListCallback(CommandSource &_source, ChannelInfo *_ci, const Anope::string &numlist, XOPType _type, Command *_c) : NumberList(numlist, false), source(_source), ci(_ci), type(_type), c(_c), SentHeader(false) - { - } - - void HandleNumber(unsigned Number) - { - if (!Number || Number > ci->GetAccessCount()) - return; - - ChanAccess *access = ci->GetAccess(Number - 1); - - if (this->type != XOPChanAccess::DetermineLevel(access)) - return; - - if (!SentHeader) - { - SentHeader = true; - source.Reply(_("%s list for %s:\n Num Nick"), source.command.c_str(), ci->name.c_str()); - } - - DoList(source, access, Number - 1, this->type); - } - - static void DoList(CommandSource &source, ChanAccess *access, unsigned index, XOPType type) - { - source.Reply(_(" %3d %s"), index, access->mask.c_str()); - } -}; - -class XOPDelCallback : public NumberList -{ - CommandSource &source; - ChannelInfo *ci; - Command *c; - unsigned Deleted; - Anope::string Nicks; - bool override; - XOPType type; - public: - XOPDelCallback(CommandSource &_source, ChannelInfo *_ci, Command *_c, bool _override, XOPType _type, const Anope::string &numlist) : NumberList(numlist, true), source(_source), ci(_ci), c(_c), Deleted(0), override(_override), type(_type) - { - } - - ~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.u, c, ci) << "deleted access of users " << 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) - { - if (!Number || Number > ci->GetAccessCount()) - return; - - ChanAccess *access = ci->GetAccess(Number - 1); - - if (this->type != XOPChanAccess::DetermineLevel(access)) - return; - - ++Deleted; - if (!Nicks.empty()) - Nicks += ", " + access->mask; - else - Nicks = access->mask; - - FOREACH_MOD(I_OnAccessDel, OnAccessDel(ci, source.u, access)); - - ci->EraseAccess(Number - 1); - } -}; - -class XOPBase : public Command -{ - private: - void DoAdd(CommandSource &source, ChannelInfo *ci, const std::vector ¶ms, XOPType level) - { - User *u = source.u; - - Anope::string mask = params.size() > 2 ? params[2] : ""; - - if (mask.empty()) - { - this->OnSyntaxError(source, "ADD"); - return; - } - - if (readonly) - { - source.Reply(_("Sorry, channel %s list modification is temporarily disabled."), source.command.c_str()); - return; - } - - AccessGroup access = ci->AccessFor(u); - ChanAccess *highest = access.Highest(); - int u_level = (highest ? XOPChanAccess::DetermineLevel(highest) : 0); - - if (((access.HasPriv(CA_FOUNDER) || level >= u_level) || !access.HasPriv(CA_ACCESS_CHANGE)) && !u->HasPriv("chanserv/access/modify")) - { - source.Reply(ACCESS_DENIED); - return; - } - - for (unsigned i = 0; i < ci->GetAccessCount(); ++i) - { - ChanAccess *a = ci->GetAccess(i); - - if (a->mask.equals_ci(mask)) - { - if (XOPChanAccess::DetermineLevel(a) >= u_level && !u->HasPriv("chanserv/access/modify")) - { - source.Reply(ACCESS_DENIED); - return; - } - - ci->EraseAccess(i); - break; - } - } - - if (ci->GetAccessCount() >= Config->CSAccessMax) - { - source.Reply(_("Sorry, you can only have %d %s entries on a channel."), Config->CSAccessMax, source.command.c_str()); - return; - } - - if (mask.find_first_of("!*@") == Anope::string::npos && findnick(mask) == NULL) - mask += "!*@*"; - - service_reference provider("access/xop"); - if (!provider) - return; - XOPChanAccess *acc = debug_cast(provider->Create()); - acc->ci = ci; - acc->mask = mask; - acc->creator = u->nick; - acc->type = level; - acc->last_seen = 0; - acc->created = Anope::CurTime; - ci->AddAccess(acc); - - bool override = level >= u_level || !access.HasPriv(CA_ACCESS_CHANGE); - Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "ADD " << mask << " as level " << level; - - FOREACH_MOD(I_OnAccessAdd, OnAccessAdd(ci, u, 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 ¶ms, XOPType level) - { - User *u = source.u; - - const Anope::string &mask = params.size() > 2 ? params[2] : ""; - - if (mask.empty()) - { - this->OnSyntaxError(source, "DEL"); - return; - } - - if (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; - } - - AccessGroup access = ci->AccessFor(u); - ChanAccess *highest = access.Highest(); - bool override = false; - if (!mask.equals_ci(u->Account()->display) && !access.HasPriv(CA_ACCESS_CHANGE) && (!highest || level >= XOPChanAccess::DetermineLevel(highest))) - { - if (u->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) - { - XOPDelCallback list(source, ci, this, override, level, mask); - list.Process(); - } - else - { - for (unsigned i = 0; i < ci->GetAccessCount(); ++i) - { - ChanAccess *a = ci->GetAccess(i); - - if (a->mask.equals_ci(mask) && XOPChanAccess::DetermineLevel(a) == level) - { - Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "DEL " << a->mask; - - source.Reply(_("\002%s\002 deleted from %s %s list."), a->mask.c_str(), ci->name.c_str(), source.command.c_str()); - - FOREACH_MOD(I_OnAccessDel, OnAccessDel(ci, u, a)); - ci->EraseAccess(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 ¶ms, XOPType level) - { - User *u = source.u; - - const Anope::string &nick = params.size() > 2 ? params[2] : ""; - - AccessGroup access = ci->AccessFor(u); - bool override = false; - - if (!access.HasPriv(CA_ACCESS_LIST)) - { - if (u->HasCommand("chanserv/access/list")) - override = true; - else - { - source.Reply(ACCESS_DENIED); - return; - } - } - - Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci); - - if (!ci->GetAccessCount()) - { - source.Reply(_("%s %s list is empty."), ci->name.c_str(), source.command.c_str()); - return; - } - - if (!nick.empty() && nick.find_first_not_of("1234567890,-") == Anope::string::npos) - { - XOPListCallback list(source, ci, nick, level, this); - list.Process(); - } - else - { - bool SentHeader = false; - - for (unsigned i = 0, end = ci->GetAccessCount(); i < end; ++i) - { - ChanAccess *a = ci->GetAccess(i); - - if (XOPChanAccess::DetermineLevel(a) != level) - continue; - else if (!nick.empty() && !Anope::Match(a->mask, nick)) - continue; - - if (!SentHeader) - { - SentHeader = true; - source.Reply(_("%s list for %s:\n Num Nick"), source.command.c_str(), ci->name.c_str()); - } - - XOPListCallback::DoList(source, a, i + 1, level); - } - - if (!SentHeader) - source.Reply(_("No matching entries on %s %s list."), ci->name.c_str(), source.command.c_str()); - } - - return; - } - - void DoClear(CommandSource &source, ChannelInfo *ci, XOPType level) - { - User *u = source.u; - - if (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 (!ci->HasPriv(u, CA_FOUNDER) && !u->HasPriv("chanserv/access/modify")) - { - source.Reply(ACCESS_DENIED); - return; - } - - bool override = !ci->HasPriv(u, CA_FOUNDER); - Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "CLEAR level " << level; - - for (unsigned i = ci->GetAccessCount(); i > 0; --i) - { - ChanAccess *access = ci->GetAccess(i - 1); - if (XOPChanAccess::DetermineLevel(access) == level) - ci->EraseAccess(i - 1); - } - - FOREACH_MOD(I_OnAccessClear, OnAccessClear(ci, u)); - - source.Reply(_("Channel %s %s list has been cleared."), ci->name.c_str(), source.command.c_str()); - - return; - } - - protected: - void DoXop(CommandSource &source, const std::vector ¶ms, XOPType level) - { - ChannelInfo *ci = cs_findchan(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, level); - else if (cmd.equals_ci("DEL")) - return this->DoDel(source, ci, params, level); - else if (cmd.equals_ci("LIST")) - return this->DoList(source, ci, params, level); - else if (cmd.equals_ci("CLEAR")) - return this->DoClear(source, ci, level); - else - this->OnSyntaxError(source, ""); - - return; - } - public: - XOPBase(Module *modname, const Anope::string &command) : Command(modname, command, 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"); - } - - virtual ~XOPBase() - { - } - - virtual void Execute(CommandSource &source, const std::vector ¶ms) = 0; - - virtual bool OnHelp(CommandSource &source, const Anope::string &subcommand) = 0; -}; - -class CommandCSQOP : public XOPBase -{ - public: - CommandCSQOP(Module *creator) : XOPBase(creator, "chanserv/qop") - { - this->SetDesc(_("Modify the list of QOP users")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - return this->DoXop(source, params, XOP_QOP); - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Maintains the \002QOP\002 (AutoOwner) \002list\002 for a channel. The QOP \n" - "list gives users the right to be auto-owner on your channel,\n" - "which gives them almost (or potentially, total) access.\n" - " \n" - "The \002QOP ADD\002 command adds the given nickname to the\n" - "QOP list.\n" - " \n" - "The \002QOP DEL\002 command removes the given nick from the\n" - "QOP list. If a list of entry numbers is given, those\n" - "entries are deleted. (See the example for LIST below.)\n" - " \n" - "The \002QOP LIST\002 command displays the QOP 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" - " \002QOP #channel LIST 2-5,7-9\002\n" - " Lists QOP entries numbered 2 through 5 and\n" - " 7 through 9.\n" - " \n" - "The \002QOP CLEAR\002 command clears all entries of the\n" - "QOP list.\n")); - source.Reply(_(" \n" - "The \002QOP\002 commands are limited to\n" - "founders (unless SECUREOPS is off). However, any user on the\n" - "QOP list may use the \002QOP LIST\002 command.\n" - " \n")); - source.Reply(_("This command may have been disabled for your channel, and\n" - "in that case you need to use the access list. See \n" - "\002%s%s HELP ACCESS\002 for information about the access list,\n" - "and \002%s%s HELP SET XOP\002 to know how to toggle between \n" - "the access list and xOP list systems."), - Config->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str(), - Config->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str()); - return true; - } -}; - -class CommandCSAOP : public XOPBase -{ - public: - CommandCSAOP(Module *creator) : XOPBase(creator, "chanserv/aop") - { - this->SetDesc(_("Modify the list of AOP users")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - return this->DoXop(source, params, XOP_AOP); - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Maintains the \002AOP\002 (AutoOP) \002list\002 for a channel. The AOP \n" - "list gives users the right to be auto-opped on your channel,\n" - "to unban or invite themselves if needed, to have their\n" - "greet message showed on join, and so on.\n" - " \n" - "The \002AOP ADD\002 command adds the given nickname to the\n" - "AOP list.\n" - " \n" - "The \002AOP DEL\002 command removes the given nick from the\n" - "AOP list. If a list of entry numbers is given, those\n" - "entries are deleted. (See the example for LIST below.)\n" - " \n" - "The \002AOP LIST\002 command displays the AOP 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" - " \002AOP #channel LIST 2-5,7-9\002\n" - " Lists AOP entries numbered 2 through 5 and\n" - " 7 through 9.\n" - " \n" - "The \002AOP CLEAR\002 command clears all entries of the\n" - "AOP list.\n")); - source.Reply(_(" \n" - "The \002AOP ADD\002 and \002AOP DEL\002 commands are limited to\n" - "SOPs or above, while the \002AOP CLEAR\002 command can only\n" - "be used by the channel founder. However, any user on the\n" - "AOP list may use the \002AOP LIST\002 command.\n" - " \n")); - source.Reply(_("This command may have been disabled for your channel, and\n" - "in that case you need to use the access list. See \n" - "\002%s%s HELP ACCESS\002 for information about the access list,\n" - "and \002%s%s HELP SET XOP\002 to know how to toggle between \n" - "the access list and xOP list systems."), - Config->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str(), - Config->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str()); - return true; - } -}; - -class CommandCSHOP : public XOPBase -{ - public: - CommandCSHOP(Module *creator) : XOPBase(creator, "chanserv/hop") - { - this->SetDesc(_("Maintains the HOP (HalfOP) list for a channel")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - return this->DoXop(source, params, XOP_HOP); - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Maintains the \002HOP\002 (HalfOP) \002list\002 for a channel. The HOP \n" - "list gives users the right to be auto-halfopped on your \n" - "channel.\n" - " \n" - "The \002HOP ADD\002 command adds the given nickname to the\n" - "HOP list.\n" - " \n" - "The \002HOP DEL\002 command removes the given nick from the\n" - "HOP list. If a list of entry numbers is given, those\n" - "entries are deleted. (See the example for LIST below.)\n" - " \n" - "The \002HOP LIST\002 command displays the HOP 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" - " \002HOP #channel LIST 2-5,7-9\002\n" - " Lists HOP entries numbered 2 through 5 and\n" - " 7 through 9.\n" - " \n" - "The \002HOP CLEAR\002 command clears all entries of the\n" - "HOP list.\n")); - source.Reply(_(" \n" - "The \002HOP ADD\002, \002HOP DEL\002 and \002HOP LIST\002 commands are \n" - "limited to AOPs or above, while the \002HOP CLEAR\002 command \n" - "can only be used by the channel founder.\n" - " \n")); - source.Reply(_("This command may have been disabled for your channel, and\n" - "in that case you need to use the access list. See \n" - "\002%s%s HELP ACCESS\002 for information about the access list,\n" - "and \002%s%s HELP SET XOP\002 to know how to toggle between \n" - "the access list and xOP list systems."), - Config->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str(), - Config->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str()); - return true; - } -}; - -class CommandCSSOP : public XOPBase -{ - public: - CommandCSSOP(Module *creator) : XOPBase(creator, "chanserv/sop") - { - this->SetDesc(_("Modify the list of SOP users")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - return this->DoXop(source, params, XOP_SOP); - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Maintains the \002SOP\002 (SuperOP) \002list\002 for a channel. The SOP \n" - "list gives users all rights given by the AOP list, and adds\n" - "those needed to use the AutoKick and the BadWords lists, \n" - "to send and read channel memos, and so on.\n" - " \n" - "The \002SOP ADD\002 command adds the given nickname to the\n" - "SOP list.\n" - " \n" - "The \002SOP DEL\002 command removes the given nick from the\n" - "SOP list. If a list of entry numbers is given, those\n" - "entries are deleted. (See the example for LIST below.)\n" - " \n" - "The \002SOP LIST\002 command displays the SOP 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" - " \002SOP #channel LIST 2-5,7-9\002\n" - " Lists AOP entries numbered 2 through 5 and\n" - " 7 through 9.\n" - " \n" - "The \002SOP CLEAR\002 command clears all entries of the\n" - "SOP list.\n")); - source.Reply(_( - " \n" - "The \002SOP ADD\002, \002SOP DEL\002 and \002SOP CLEAR\002 commands are \n" - "limited to the channel founder. However, any user on the\n" - "AOP list may use the \002SOP LIST\002 command.\n" - " \n")); - source.Reply(_("This command may have been disabled for your channel, and\n" - "in that case you need to use the access list. See \n" - "\002%s%s HELP ACCESS\002 for information about the access list,\n" - "and \002%s%s HELP SET XOP\002 to know how to toggle between \n" - "the access list and xOP list systems."), - Config->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str(), - Config->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str()); - return true; - } -}; - -class CommandCSVOP : public XOPBase -{ - public: - CommandCSVOP(Module *creator) : XOPBase(creator, "chanserv/vop") - { - this->SetDesc(_("Maintains the VOP (VOicePeople) list for a channel")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - return this->DoXop(source, params, XOP_VOP); - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Maintains the \002VOP\002 (VOicePeople) \002list\002 for a channel. \n" - "The VOP list allows users to be auto-voiced and to voice \n" - "themselves if they aren't.\n" - " \n" - "The \002VOP ADD\002 command adds the given nickname to the\n" - "VOP list.\n" - " \n" - "The \002VOP DEL\002 command removes the given nick from the\n" - "VOP list. If a list of entry numbers is given, those\n" - "entries are deleted. (See the example for LIST below.)\n" - " \n" - "The \002VOP LIST\002 command displays the VOP 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" - " \002VOP #channel LIST 2-5,7-9\002\n" - " Lists VOP entries numbered 2 through 5 and\n" - " 7 through 9.\n" - " \n" - "The \002VOP CLEAR\002 command clears all entries of the\n" - "VOP list.\n")); - source.Reply(_( - " \n" - "The \002VOP ADD\002, \002VOP DEL\002 and \002VOP LIST\002 commands are \n" - "limited to AOPs or above, while the \002VOP CLEAR\002 command \n" - "can only be used by the channel founder.\n" - " \n")); - source.Reply(_("This command may have been disabled for your channel, and\n" - "in that case you need to use the access list. See \n" - "\002%s%s HELP ACCESS\002 for information about the access list,\n" - "and \002%s%s HELP SET XOP\002 to know how to toggle between \n" - "the access list and xOP list systems."), - Config->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str(), - Config->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str()); - return true; - } -}; - -class CSXOP : public Module -{ - XOPAccessProvider accessprovider; - CommandCSQOP commandcsqop; - CommandCSSOP commandcssop; - CommandCSAOP commandcsaop; - CommandCSHOP commandcshop; - CommandCSVOP commandcsvop; - - public: - CSXOP(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - accessprovider(this), commandcsqop(this), commandcssop(this), commandcsaop(this), commandcshop(this), commandcsvop(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&accessprovider); - ModuleManager::RegisterService(&commandcssop); - ModuleManager::RegisterService(&commandcsaop); - ModuleManager::RegisterService(&commandcsqop); - ModuleManager::RegisterService(&commandcsvop); - ModuleManager::RegisterService(&commandcshop); - } -}; - -MODULE_INIT(CSXOP) diff --git a/modules/core/db_mysql.cpp b/modules/core/db_mysql.cpp deleted file mode 100644 index 4e6a7de4f..000000000 --- a/modules/core/db_mysql.cpp +++ /dev/null @@ -1,1566 +0,0 @@ -#include "module.h" -#include "../extra/sql.h" -#include "os_session.h" - -static Anope::string ToString(const std::vector &strings) -{ - Anope::string ret; - - for (unsigned i = 0; i < strings.size(); ++i) - ret += " " + strings[i]; - - if (!ret.empty()) - ret.erase(ret.begin()); - - return ret; -} - -static std::vector MakeVector(const Anope::string &buf) -{ - Anope::string s; - spacesepstream sep(buf); - std::vector params; - - while (sep.GetToken(s)) - { - if (s[0] == ':') - { - s.erase(s.begin()); - if (!s.empty() && !sep.StreamEnd()) - params.push_back(s + " " + sep.GetRemaining()); - else if (!s.empty()) - params.push_back(s); - } - else - params.push_back(s); - } - - return params; -} - -static NickAlias *CurNick = NULL; -static NickCore *CurCore = NULL; -static ChannelInfo *CurChannel = NULL; -static BotInfo *CurBot = NULL; - -static void Write(const Anope::string &data); -static void WriteNickMetadata(const Anope::string &key, const Anope::string &data); -static void WriteCoreMetadata(const Anope::string &key, const Anope::string &data); -static void WriteChannelMetadata(const Anope::string &key, const Anope::string &data); -static void WriteBotMetadata(const Anope::string &key, const Anope::string &data); - -class CommandSQLSync : public Command -{ - public: - CommandSQLSync(Module *creator) : Command(creator, "SQLSYNC", 0, 0) - { - this->SetDesc(_("Import your databases to SQL")); - this->SetSyntax(""); - } - - void Execute(CommandSource &source, const std::vector ¶ms); - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("This command syncs your databases with SQL. You should\n" - "only have to execute this command once, when you initially\n" - "import your databases into SQL.")); - return true; - } -}; - -class MySQLInterface : public SQLInterface -{ - public: - MySQLInterface(Module *o) : SQLInterface(o) { } - - void OnResult(const SQLResult &r); - - void OnError(const SQLResult &r); -}; - -class DBMySQL; -static DBMySQL *me; -class DBMySQL : public Module -{ - private: - CommandSQLSync commandsqlsync; - MySQLInterface sqlinterface; - service_reference SQL; - - public: - service_reference SessionInterface; - time_t lastwarn; - bool ro; - - void RunQuery(const SQLQuery &query) - { - if (SQL) - { - if (readonly && this->ro) - { - readonly = this->ro = false; - BotInfo *bi = findbot(Config->OperServ); - if (bi) - ircdproto->SendGlobops(bi, "Found SQL again, going out of readonly mode..."); - } - - SQL->Run(&sqlinterface, query); - } - else - { - if (Anope::CurTime - Config->UpdateTimeout > lastwarn) - { - BotInfo *bi = findbot(Config->OperServ); - if (bi) - ircdproto->SendGlobops(bi, "Unable to locate SQL reference, is m_mysql loaded? Going to readonly..."); - readonly = this->ro = true; - this->lastwarn = Anope::CurTime; - } - } - } - - DBMySQL(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, DATABASE), commandsqlsync(this), sqlinterface(this), SQL("mysql/main"), SessionInterface("session") - { - me = this; - - this->lastwarn = 0; - this->ro = false; - - Implementation i[] = { - I_OnLoadDatabase, I_OnServerConnect - }; - ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); - - ModuleManager::RegisterService(&commandsqlsync); - - if (CurrentUplink) - OnServerConnect(); - } - - void OnServerConnect() - { - Implementation i[] = { - /* Misc */ - I_OnSaveDatabase, I_OnPostCommand, - /* NickServ */ - I_OnNickAddAccess, I_OnNickEraseAccess, I_OnNickClearAccess, - I_OnDelCore, I_OnNickForbidden, I_OnNickGroup, - I_OnNickRegister, I_OnChangeCoreDisplay, - I_OnNickSuspended, I_OnDelNick, - /* ChanServ */ - I_OnAccessAdd, I_OnAccessDel, I_OnAccessClear, I_OnLevelChange, - I_OnChanForbidden, I_OnDelChan, I_OnChanRegistered, I_OnChanSuspend, - I_OnAkickAdd, I_OnAkickDel, I_OnMLock, I_OnUnMLock, - /* BotServ */ - I_OnBotCreate, I_OnBotChange, I_OnBotDelete, - I_OnBotAssign, I_OnBotUnAssign, - I_OnBadWordAdd, I_OnBadWordDel, - /* MemoServ */ - I_OnMemoSend, I_OnMemoDel, - /* OperServ */ - I_OnExceptionAdd, I_OnExceptionDel, - I_OnAddXLine, I_OnDelXLine, - /* HostServ */ - I_OnSetVhost, I_OnDeleteVhost - }; - ModuleManager::Attach(i, this, 40); - } - - EventReturn OnLoadDatabase() - { - if (!SQL) - { - Log() << "Error, unable to find service reference for SQL, is m_mysql loaded and configured properly?"; - return EVENT_CONTINUE; - } - - SQLQuery query; - - query = "SELECT * FROM `anope_ns_core`"; - SQLResult r = SQL->RunQuery(query); - for (int i = 0; i < r.Rows(); ++i) - { - NickCore *nc = new NickCore(r.Get(i, "display")); - nc->pass = r.Get(i, "pass"); - nc->email = r.Get(i, "email"); - nc->greet = r.Get(i, "greet"); - - spacesepstream sep(r.Get(i, "flags")); - Anope::string buf; - std::vector flags; - while (sep.GetToken(buf)) - flags.push_back(buf); - nc->FromString(flags); - - nc->language = r.Get(i, "language"); - nc->memos.memomax = r.Get(i, "memomax").is_number_only() ? convertTo(r.Get(i, "memomax")) : 20; - } - - query = "SELECT * FROM `anope_ns_access`"; - r = SQL->RunQuery(query); - for (int i = 0; i < r.Rows(); ++i) - { - NickCore *nc = findcore(r.Get(i, "display")); - if (!nc) - { - Log() << "MySQL: Got NickCore access entry for nonexistant core " << r.Get(i, "display"); - continue; - } - - nc->AddAccess(r.Get(i, "access")); - } - - query = "SELECT * FROM `anope_ns_core_metadata`"; - r = SQL->RunQuery(query); - for (int i = 0; i < r.Rows(); ++i) - { - NickCore *nc = findcore(r.Get(i, "display")); - if (!nc) - { - Log() << "MySQL: Got NickCore access entry for nonexistant core " << r.Get(i, "display"); - continue; - } - - EventReturn MOD_RESULT;; - std::vector Params = MakeVector(r.Get(i, "value")); - FOREACH_RESULT(I_OnDatabaseReadMetadata, OnDatabaseReadMetadata(nc, r.Get(i, "name"), Params)); - } - - query = "SELECT * FROM `anope_ns_alias`"; - r = SQL->RunQuery(query); - for (int i = 0; i < r.Rows(); ++i) - { - NickCore *nc = findcore(r.Get(i, "display")); - if (!nc) - { - Log() << "MySQL: Got NickAlias for nick " << r.Get(i, "nick") << " with nonexistant core " << r.Get(i, "display"); - continue; - } - - NickAlias *na = new NickAlias(r.Get(i, "nick"), nc); - na->last_quit = r.Get(i, "last_quit"); - na->last_realname = r.Get(i, "last_realname"); - na->last_usermask = r.Get(i, "last_usermask"); - na->time_registered = r.Get(i, "time_registered").is_pos_number_only() ? convertTo(r.Get(i, "time_registered")) : Anope::CurTime; - na->last_seen = r.Get(i, "last_seen").is_pos_number_only() ? convertTo(r.Get(i, "last_seen")) : Anope::CurTime; - - spacesepstream sep(r.Get(i, "flags")); - Anope::string buf; - std::vector flags; - while (sep.GetToken(buf)) - flags.push_back(buf); - - na->FromString(flags); - } - - query = "SELECT * FROM `anope_ns_alias_metadata`"; - r = SQL->RunQuery(query); - for (int i = 0; i < r.Rows(); ++i) - { - NickAlias *na = findnick(r.Get(i, "nick")); - if (!na) - { - Log() << "MySQL: Got metadata for nonexistant nick " << r.Get(i, "nick"); - continue; - } - - EventReturn MOD_RESULT; - std::vector Params = MakeVector(r.Get(i, "value")); - FOREACH_RESULT(I_OnDatabaseReadMetadata, OnDatabaseReadMetadata(na, na->nick, Params)); - } - - query = "SELECT * FROM `anope_hs_core`"; - r = SQL->RunQuery(query); - for (int i = 0; i < r.Rows(); ++i) - { - NickAlias *na = findnick(r.Get(i, "nick")); - if (!na) - { - Log() << "MySQL: Got vhost entry for nonexistant nick " << r.Get(i, "nick"); - continue; - } - - time_t creation = r.Get(i, "time").is_pos_number_only() ? convertTo(r.Get(i, "time")) : Anope::CurTime; - na->hostinfo.SetVhost(r.Get(i, "vident"), r.Get(i, "vhost"), r.Get(i, "creator"), creation); - } - - query = "SELECT * FROM `anope_bs_core`"; - r = SQL->RunQuery(query); - for (int i = 0; i < r.Rows(); ++i) - { - BotInfo *bi = findbot(r.Get(i, "nick")); - if (!bi) - bi = new BotInfo(r.Get(i, "nick"), r.Get(i, "user"), r.Get(i, "host")); - bi->realname = r.Get(i, "rname"); - bi->created = r.Get(i, "created").is_pos_number_only() ? convertTo(r.Get(i, "created")) : Anope::CurTime; - - spacesepstream sep(r.Get(i, "flags")); - Anope::string buf; - std::vector flags; - while (sep.GetToken(buf)) - flags.push_back(buf); - - bi->FromString(flags); - } - - query = "SELECT * FROM `anope_bs_info_metadata`"; - r = SQL->RunQuery(query); - for (int i = 0; i < r.Rows(); ++i) - { - BotInfo *bi = findbot(r.Get(i, "botname")); - if (!bi) - { - Log() << "MySQL: BotInfo metadata for nonexistant bot " << r.Get(i, "botname"); - continue; - } - - EventReturn MOD_RESULT; - std::vector Params = MakeVector(r.Get(i, "value")); - FOREACH_RESULT(I_OnDatabaseReadMetadata, OnDatabaseReadMetadata(bi, bi->nick, Params)); - } - - query = "SELECT * FROM `anope_cs_info`"; - r = SQL->RunQuery(query); - for (int i = 0; i < r.Rows(); ++i) - { - NickCore *nc = NULL; - - if (!r.Get(i, "founder").empty()) - { - nc = findcore(r.Get(i, "founder")); - if (!nc) - { - Log() << "MySQL: Channel " << r.Get(i, "name") << " with nonexistant founder " << r.Get(i, "founder"); - continue; - } - - ChannelInfo *ci = new ChannelInfo(r.Get(i, "name")); - ci->SetFounder(nc); - if (!r.Get(i, "successor").empty()) - ci->successor = findcore(r.Get(i, "successor")); - ci->desc = r.Get(i, "descr"); - ci->time_registered = r.Get(i, "time_registered").is_pos_number_only() ? convertTo(r.Get(i, "time_registered")) : Anope::CurTime; - ci->last_used = r.Get(i, "last_used").is_pos_number_only() ? convertTo(r.Get(i, "last_used")) : Anope::CurTime; - ci->last_topic = r.Get(i, "last_topic"); - ci->last_topic_setter = r.Get(i, "last_topic_setter"); - ci->last_topic_time = r.Get(i, "last_topic_time").is_number_only() ? convertTo(r.Get(i, "last_topic_time")) : Anope::CurTime; - ci->bantype = r.Get(i, "bantype").is_number_only() ? convertTo(r.Get(i, "bantype")) : 2; - ci->memos.memomax = r.Get(i, "memomax").is_number_only() ? convertTo(r.Get(i, "memomax")) : 20; - ci->capsmin = r.Get(i, "capsmin").is_number_only() ? convertTo(r.Get(i, "capsmin")) : 0; - ci->capspercent = r.Get(i, "capspercent").is_number_only() ? convertTo(r.Get(i, "capspercent")) : 0; - ci->floodlines = r.Get(i, "floodlines").is_number_only() ? convertTo(r.Get(i, "floodlines")) : 0; - ci->floodsecs = r.Get(i, "floodsecs").is_number_only() ? convertTo(r.Get(i, "floodsecs")) : 0; - ci->repeattimes = r.Get(i, "repeattimes").is_number_only() ? convertTo(r.Get(i, "repeattimes")) : 0; - ci->bi = findbot(r.Get(i, "botnick")); - if (ci->bi && !r.Get(i, "botflags").empty()) - { - spacesepstream sep(r.Get(i, "botflags")); - Anope::string buf; - std::vector flags; - while (sep.GetToken(buf)) - flags.push_back(buf); - - ci->botflags.FromString(flags); - } - - if (!r.Get(i, "flags").empty()) - { - spacesepstream sep(r.Get(i, "flags")); - Anope::string buf; - std::vector flags; - while (sep.GetToken(buf)) - flags.push_back(buf); - - ci->FromString(flags); - } - } - } - - query = "SELECT * FROM `anope_cs_ttb`"; - r = SQL->RunQuery(query); - for (int i = 0; i < r.Rows(); ++i) - { - ChannelInfo *ci = cs_findchan(r.Get(i, "channel")); - if (!ci) - { - Log() << "MySQL: Channel ttb for nonexistant channel " << r.Get(i, "channel"); - continue; - } - - ci->ttb[atoi(r.Get(i, "ttb_id").c_str())] = atoi(r.Get(i, "value").c_str()); - } - - query = "SELECT * FROM `anope_bs_badwords`"; - r = SQL->RunQuery(query); - for (int i = 0; i < r.Rows(); ++i) - { - ChannelInfo *ci = cs_findchan(r.Get(i, "channel")); - if (!ci) - { - Log() << "MySQL: Channel badwords entry for nonexistant channel " << r.Get(i, "channel"); - continue; - } - - BadWordType BWTYPE = BW_ANY; - if (r.Get(i, "type").equals_cs("SINGLE")) - BWTYPE = BW_SINGLE; - else if (r.Get(i, "type").equals_cs("START")) - BWTYPE = BW_START; - else if (r.Get(i, "type").equals_cs("END")) - BWTYPE = BW_END; - ci->AddBadWord(r.Get(i, "word"), BWTYPE); - } - - query = "SELECT * FROM `anope_cs_access`"; - r = SQL->RunQuery(query); - for (int i = 0; i < r.Rows(); ++i) - { - ChannelInfo *ci = cs_findchan(r.Get(i, "channel")); - if (!ci) - { - Log() << "MySQL: Channel access entry for nonexistant channel " << r.Get(i, "channel"); - continue; - } - - const Anope::string &provider = r.Get(i, "provider"), &data = r.Get(i, "data"); - service_reference ap(provider); - if (!ap) - { - Log() << "MySQL: Access entry for " << ci->name << " using nonexistant provider " << provider; - continue; - } - - ChanAccess *access = ap->Create(); - access->ci = ci; - access->mask = r.Get(i, "mask"); - access->creator = r.Get(i, "creator"); - access->last_seen = r.Get(i, "last_seen").is_pos_number_only() ? convertTo(r.Get(i, "last_seen")) : Anope::CurTime; - access->created = r.Get(i, "created").is_pos_number_only() ? convertTo(r.Get(i, "created")) : Anope::CurTime; - access->Unserialize(data); - ci->AddAccess(access); - } - - query = "SELECT * FROM `anope_cs_akick`"; - r = SQL->RunQuery(query); - for (int i = 0; i < r.Rows(); ++i) - { - ChannelInfo *ci = cs_findchan(r.Get(i, "channel")); - if (!ci) - { - Log() << "MySQL: Channel access entry for nonexistant channel " << r.Get(i, "channel"); - continue; - } - - NickCore *nc = NULL; - spacesepstream sep(r.Get(i, "flags")); - Anope::string flag, mask; - while (sep.GetToken(flag)) - { - if (flag.equals_cs("ISNICK")) - nc = findcore(r.Get(i, "mask")); - - AutoKick *ak; - if (nc) - ak = ci->AddAkick(r.Get(i, "creator"), nc, r.Get(i, "reason"), atol(r.Get(i, "created").c_str()), atol(r.Get(i, "last_used").c_str())); - else - ak = ci->AddAkick(r.Get(i, "creator"), r.Get(i, "mask"), r.Get(i, "reason"), atol(r.Get(i, "created").c_str()), atol(r.Get(i, "last_used").c_str())); - if (nc) - ak->SetFlag(AK_ISNICK); - } - } - - query = "SELECT * FROM `anope_cs_levels`"; - r = SQL->RunQuery(query); - for (int i = 0; i < r.Rows(); ++i) - { - ChannelInfo *ci = cs_findchan(r.Get(i, "channel")); - if (!ci) - { - Log() << "MySQL: Channel level entry for nonexistant channel " << r.Get(i, "channel"); - continue; - } - - ci->levels[atoi(r.Get(i, "position").c_str())] = atoi(r.Get(i, "level").c_str()); - } - - query = "SELECT * FROM `anope_cs_info_metadata`"; - r = SQL->RunQuery(query); - for (int i = 0; i < r.Rows(); ++i) - { - ChannelInfo *ci = cs_findchan(r.Get(i, "channel")); - if (!ci) - { - Log() << "MySQL: Channel metadata for nonexistant channel " << r.Get(i, "channel"); - continue; - } - - EventReturn MOD_RESULT; - std::vector Params = MakeVector(r.Get(i, "value")); - FOREACH_RESULT(I_OnDatabaseReadMetadata, OnDatabaseReadMetadata(ci, ci->name, Params)); - } - - query = "SELECT * FROM `anope_cs_mlock`"; - r = SQL->RunQuery(query); - for (int i = 0; i < r.Rows(); ++i) - { - ChannelInfo *ci = cs_findchan(r.Get(i, "channel")); - if (!ci) - { - Log() << "MySQL: Channel mlock for nonexistant channel " << r.Get(i, "channel"); - continue; - } - - std::vector mlocks; - ci->GetExtRegular("db_mlock", mlocks); - - Anope::string modestring = r.Get(i, "status") + " " + r.Get(i, "mode") + " " + r.Get(i, "setter") + " " + r.Get(i, "created") + " " + r.Get(i, "param"); - - mlocks.push_back(modestring); - - ci->Extend("db_mlock", new ExtensibleItemRegular >(mlocks)); - } - - query = "SELECT * FROM `anope_ms_info`"; - r = SQL->RunQuery(query); - for (int i = 0; i < r.Rows(); ++i) - { - MemoInfo *mi = NULL; - if (r.Get(i, "serv").equals_cs("NICK")) - { - NickCore *nc = findcore(r.Get(i, "receiver")); - if (nc) - mi = &nc->memos; - } - else if (r.Get(i, "serv").equals_cs("CHAN")) - { - ChannelInfo *ci = cs_findchan(r.Get(i, "receiver")); - if (ci) - mi = &ci->memos; - } - if (mi) - { - Memo *m = new Memo(); - mi->memos.push_back(m); - m->sender = r.Get(i, "sender"); - m->time = r.Get(i, "time").is_pos_number_only() ? convertTo(r.Get(i, "time")) : Anope::CurTime; - m->text = r.Get(i, "text"); - - if (!r.Get(i, "flags").empty()) - { - spacesepstream sep(r.Get(i, "flags")); - Anope::string buf; - std::vector flags; - while (sep.GetToken(buf)) - flags.push_back(buf); - - m->FromString(flags); - } - } - } - - for (std::list::iterator it = XLineManager::XLineManagers.begin(), it_end = XLineManager::XLineManagers.end(); it != it_end; ++it) - { - XLineManager *xm = *it; - - query = "SELECT * FROM `anope_os_xlines` WHERE `type` = @type"; - query.setValue("type", Anope::string(xm->Type())); - r = SQL->RunQuery(query); - for (int i = 0; i < r.Rows(); ++i) - { - Anope::string user = r.Get(i, "user"); - Anope::string host = r.Get(i, "host"); - Anope::string by = r.Get(i, "xby"); - Anope::string reason = r.Get(i, "reason"); - time_t seton = r.Get(i, "seton").is_pos_number_only() ? convertTo(r.Get(i, "seton")) : Anope::CurTime; - time_t expires = r.Get(i, "expires").is_pos_number_only() ? convertTo(r.Get(i, "expires")) : Anope::CurTime; - - XLine *x = xm->Add(user + "@" + host, by, expires, reason); - if (x) - x->Created = seton; - } - } - - query = "SELECT * FROM `anope_os_exceptions`"; - r = SQL->RunQuery(query); - for (int i = 0; i < r.Rows(); ++i) - { - Anope::string mask = r.Get(i, "mask"); - unsigned limit = convertTo(r.Get(i, "slimit")); - Anope::string creator = r.Get(i, "who"); - Anope::string reason = r.Get(i, "reason"); - time_t expires = convertTo(r.Get(i, "expires")); - - if (SessionInterface) - { - Exception *e = new Exception(); - e->mask = mask; - e->limit = limit; - e->who = creator; - e->reason = reason; - e->time = expires; - SessionInterface->AddException(e); - } - } - - query = "SELECT * FROM `anope_extra`"; - r = SQL->RunQuery(query); - for (int i = 0; i < r.Rows(); ++i) - { - std::vector params = MakeVector(r.Get(i, "data")); - EventReturn MOD_RESULT; - FOREACH_RESULT(I_OnDatabaseRead, OnDatabaseRead(params)); - } - - query = "SELECT * FROM `anope_ns_core_metadata`"; - r = SQL->RunQuery(query); - for (int i = 0; i < r.Rows(); ++i) - { - NickCore *nc = findcore(r.Get(i, "nick")); - if (!nc) - continue; - if (r.Get(i, "name") == "MEMO_IGNORE") - nc->memos.ignores.push_back(r.Get(i, "value").ci_str()); - } - - query = "SELECT * FROM `anope_cs_info_metadata`"; - r = SQL->RunQuery(query); - for (int i = 0; i < r.Rows(); ++i) - { - ChannelInfo *ci = cs_findchan(r.Get(i, "channel")); - if (!ci) - continue; - if (r.Get(i, "name") == "MEMO_IGNORE") - ci->memos.ignores.push_back(r.Get(i, "value").ci_str()); - } - - return EVENT_STOP; - } - - EventReturn OnSaveDatabase() - { - SQLQuery query; - - query = "TRUNCATE TABLE `anope_os_core`"; - this->RunQuery(query); - - query = "INSERT INTO `anope_os_core` (maxusercnt, maxusertime, akills_count, snlines_count, sqlines_count, szlines_count) VALUES(@maxusercnt, @maxusertime, @akills_count, @snlines_count, @sqlines_count, @szlines_count)"; - query.setValue("maxusercnt", maxusercnt); - query.setValue("maxusertime", maxusertime); - this->RunQuery(query); - - for (nickcore_map::const_iterator it = NickCoreList.begin(), it_end = NickCoreList.end(); it != it_end; ++it) - { - CurCore = it->second; - FOREACH_MOD(I_OnDatabaseWriteMetadata, OnDatabaseWriteMetadata(WriteCoreMetadata, CurCore)); - } - - for (nickalias_map::const_iterator it = NickAliasList.begin(), it_end = NickAliasList.end(); it != it_end; ++it) - { - CurNick = it->second; - FOREACH_MOD(I_OnDatabaseWriteMetadata, OnDatabaseWriteMetadata(WriteNickMetadata, CurNick)); - } - - for (registered_channel_map::const_iterator it = RegisteredChannelList.begin(), it_end = RegisteredChannelList.end(); it != it_end; ++it) - { - CurChannel = it->second; - FOREACH_MOD(I_OnDatabaseWriteMetadata, OnDatabaseWriteMetadata(WriteChannelMetadata, CurChannel)); - } - - for (Anope::insensitive_map::const_iterator it = BotListByNick.begin(), it_end = BotListByNick.end(); it != it_end; ++it) - { - if (it->second->HasFlag(BI_CONF)) - continue; - - CurBot = it->second; - FOREACH_MOD(I_OnDatabaseWriteMetadata, OnDatabaseWriteMetadata(WriteBotMetadata, CurBot)); - - query = "INSERT INTO `anope_bs_core` (nick, user, host, rname, flags, created, chancount) VALUES(@nick, @user, @host, @rname, @flags, @created, @chancount) ON DUPLICATE KEY UPDATE nick=VALUES(nick), user=VALUES(user), host=VALUES(host), rname=VALUES(rname), flags=VALUES(flags), created=VALUES(created), chancount=VALUES(chancount)"; - query.setValue("nick", CurBot->nick); - query.setValue("user", CurBot->GetIdent()); - query.setValue("host", CurBot->host); - query.setValue("rname", CurBot->realname); - query.setValue("flags", ToString(CurBot->ToString())); - query.setValue("created", CurBot->created); - query.setValue("chancount", CurBot->chancount); - this->RunQuery(query); - } - - query = "TRUNCATE TABLE `anope_extra`"; - this->RunQuery(query); - FOREACH_MOD(I_OnDatabaseWrite, OnDatabaseWrite(Write)); - - return EVENT_CONTINUE; - } - - void OnPostCommand(CommandSource &source, Command *command, const std::vector ¶ms) - { - User *u = source.u; - - if (command->name.find("nickserv/set/") == 0 || command->name.find("nickserv/saset/") == 0) - { - NickAlias *na = findnick(command->name.find("nickserv/set/") == 0 ? source.u->nick : params[1]); - if (!na) - return; - - if (command->name == "nickserv/set/password" || command->name == "nickserv/saset/password") - { - SQLQuery query("UPDATE `anope_ns_core` SET `pass` = @pass WHERE `display` = @display"); - query.setValue("pass", na->nc->pass); - query.setValue("display", na->nc->display); - this->RunQuery(query); - } - else if (command->name == "nickserv/set/language" || command->name == "nickserv/saset/language") - { - SQLQuery query("UPDATE `anope_ns_core` SET `language` = @language WHERE `display` = @display"); - query.setValue("language", na->nc->language); - query.setValue("display", na->nc->display); - this->RunQuery(query); - } - else if (command->name == "nickserv/set/email" || command->name == "nickserv/saset/email") - { - SQLQuery query("UPDATE `anope_ns_core` SET `email` = @email WHERE `display` = @display"); - query.setValue("email", na->nc->email); - query.setValue("display", na->nc->display); - this->RunQuery(query); - } - else if (command->name == "nickserv/set/greet" || command->name == "nickserv/saset/greet") - { - SQLQuery query("UPDATE `anope_ns_core` SET `greet` = @greet WHERE `display` = @display"); - query.setValue("greet", na->nc->greet); - query.setValue("display", na->nc->display); - this->RunQuery(query); - } - else - { - SQLQuery query("UPDATE `anope_ns_core` SET `flags` = @flags WHERE `display` = @display"); - query.setValue("flags", ToString(na->nc->ToString())); - query.setValue("display", na->nc->display); - this->RunQuery(query); - } - } - else if (command->name.find("chanserv/set") == 0 || command->name.find("chanserv/saset") == 0) - { - ChannelInfo *ci = params.size() > 0 ? cs_findchan(params[0]) : NULL; - if (!ci) - return; - - if (command->name == "chanserv/set/founder" || command->name == "chanserv/saset/founder") - { - SQLQuery query("UPDATE `anope_cs_info` SET `founder` = @founder WHERE `name` = @name"); - query.setValue("founder", ci->GetFounder() ? ci->GetFounder()->display : ""); - query.setValue("name", ci->name); - this->RunQuery(query); - } - else if (command->name == "chanserv/set/successor" || command->name == "chanserv/saset/successor") - { - SQLQuery query("UPDATE `anope_cs_info` SET `successor` = @successor WHERE `name` = @name"); - query.setValue("successor", ci->successor ? ci->successor->display : ""); - query.setValue("name", ci->name); - this->RunQuery(query); - } - else if (command->name == "chanserv/set/desc" || command->name == "chanserv/saset/desc") - { - SQLQuery query("UPDATE `anope_cs_info` SET `descr` = @descr WHERE `name` = @name"); - query.setValue("descr", ci->desc); - query.setValue("name", ci->name); - this->RunQuery(query); - } - else if (command->name == "chanserv/set/bantype" || command->name == "chanserv/saset/bantype") - { - SQLQuery query("UPDATE `anope_cs_info` SET `bantype` = @bantype WHERE `name` = @name"); - query.setValue("bantype", ci->bantype); - query.setValue("name", ci->name); - this->RunQuery(query); - } - else - { - SQLQuery query("UPDATE `anope_cs_info` SET `flags` = @flags WHERE `name` = @name"); - query.setValue("flags", ToString(ci->ToString())); - query.setValue("name", ci->name); - this->RunQuery(query); - } - } - else if (command->name == "botserv/kick" && params.size() > 2) - { - ChannelInfo *ci = cs_findchan(params[0]); - if (!ci) - return; - if (!ci->HasPriv(u, CA_SET) && !u->HasPriv("botserv/administration")) - return; - if (params[1].equals_ci("BADWORDS") || params[1].equals_ci("BOLDS") || params[1].equals_ci("CAPS") || params[1].equals_ci("COLORS") || params[1].equals_ci("FLOOD") || params[1].equals_ci("REPEAT") || params[1].equals_ci("REVERSES") || params[1].equals_ci("UNDERLINES")) - { - if (params[2].equals_ci("ON") || params[2].equals_ci("OFF")) - { - for (int i = 0; i < TTB_SIZE; ++i) - { - SQLQuery query("INSERT INTO `anope_cs_ttb` (channel, ttb_id, value) VALUES(@channel, @ttb_id, @value) ON DUPLICATE KEY UPDATE channel=VALUES(channel), ttb_id=VALUES(ttb_id), value=VALUES(value)"); - query.setValue("channel", ci->name); - query.setValue("ttb_id", i); - query.setValue("value", ci->ttb[i]); - this->RunQuery(query); - } - - { - SQLQuery query("UPDATE `anope_cs_info` SET `botflags` = @botflags WHERE `name` = @name"); - query.setValue("botflags", ToString(ci->botflags.ToString())); - query.setValue("name", ci->name); - this->RunQuery(query); - } - - if (params[1].equals_ci("CAPS")) - { - SQLQuery query("UPDATE `anope_cs_info` SET `capsmin` = @capsmin, `capspercent` = @capspercent WHERE `name` = @name"); - query.setValue("capsmin", ci->capsmin); - query.setValue("capspercent", ci->capspercent); - query.setValue("name", ci->name); - this->RunQuery(query); - } - else if (params[1].equals_ci("FLOOD")) - { - SQLQuery query("UPDATE `anope_cs_info` SET `floodlines` = @floodlines, `floodsecs` = @floodsecs WHERE `name` = @name"); - query.setValue("floodlines", ci->floodlines); - query.setValue("floodsecs", ci->floodsecs); - query.setValue("name", ci->name); - this->RunQuery(query); - } - else if (params[1].equals_ci("REPEAT")) - { - SQLQuery query("UPDATE `anope_cs_info` SET `repeattimes` = @ WHERE `name` = @"); - query.setValue("repeattimes", ci->repeattimes); - query.setValue("name", ci->name); - this->RunQuery(query); - } - } - } - } - else if (command->name == "botserv/set" && params.size() > 1) - { - ChannelInfo *ci = cs_findchan(params[0]); - if (ci && !ci->HasPriv(u, CA_SET) && !u->HasPriv("botserv/administration")) - return; - BotInfo *bi = NULL; - if (!ci) - bi = findbot(params[0]); - if (bi && params[1].equals_ci("PRIVATE") && u->HasPriv("botserv/set/private")) - { - SQLQuery query("UPDATE `anope_bs_core` SET `flags` = @ WHERE `nick` = @"); - query.setValue("flags", ToString(bi->ToString())); - query.setValue("nick", bi->nick); - this->RunQuery(query); - } - else if (!ci) - return; - else if (params[1].equals_ci("DONTKICKOPS") || params[1].equals_ci("DONTKICKVOICES") || params[1].equals_ci("FANTASY") || params[1].equals_ci("GREET") || params[1].equals_ci("SYMBIOSIS") || params[1].equals_ci("NOBOT")) - { - SQLQuery query("UPDATE `anope_cs_info` SET `botflags` = @ WHERE `name` = @"); - query.setValue("botflags", ToString(ci->botflags.ToString())); - query.setValue("name", ci->name); - this->RunQuery(query); - } - } - else if (command->name == "memoserv/ignore" && params.size() > 0) - { - Anope::string target = params[0]; - NickCore *nc = NULL; - ChannelInfo *ci = NULL; - if (target[0] != '#') - { - target = u->nick; - nc = u->Account(); - if (!nc) - return; - } - else - { - ci = cs_findchan(target); - if (!ci || !ci->HasPriv(u, CA_MEMO)) - return; - } - - MemoInfo *mi = ci ? &ci->memos : &nc->memos; - Anope::string table = ci ? "anope_cs_info_metadata" : "anope_ns_core_metadata"; - Anope::string ename = ci ? "channel" : "nick"; - - SQLQuery query("DELETE FROM `" + table + "` WHERE `" + ename + "` = @target AND `name` = 'MEMO_IGNORE'"); - query.setValue("target", target); - this->RunQuery(query); - - query = "INSERT INTO `" + table + "` VALUES(" + ename + ", name, value) (@target, 'MEMO_IGNORE, @ignore)"; - query.setValue("target", target); - for (unsigned j = 0; j < mi->ignores.size(); ++j) - { - query.setValue("ignore", mi->ignores[j]); - this->RunQuery(query); - } - } - } - - void OnNickAddAccess(NickCore *nc, const Anope::string &entry) - { - SQLQuery query("INSERT INTO `anope_ns_access` (display, access) VALUES(@display, @access)"); - query.setValue("display", nc->display); - query.setValue("access", entry); - this->RunQuery(query); - } - - void OnNickEraseAccess(NickCore *nc, const Anope::string &entry) - { - SQLQuery query("DELETE FROM `anope_ns_access` WHERE `display` = @display AND `access` = @access"); - query.setValue("display", nc->display); - query.setValue("access", entry); - this->RunQuery(query); - } - - void OnNickClearAccess(NickCore *nc) - { - SQLQuery query("DELETE FROM `anope_ns_access` WHERE `display` = @display"); - query.setValue("display", nc->display); - this->RunQuery(query); - } - - void OnDelCore(NickCore *nc) - { - SQLQuery query("DELETE FROM `anope_ns_core` WHERE `display` = @display"); - query.setValue("display", nc->display); - this->RunQuery(query); - } - - void OnNickForbidden(NickAlias *na) - { - SQLQuery query("UPDATE `anope_ns_alias` SET `flags` = @flags WHERE `nick` = @nick"); - query.setValue("flags", ToString(na->ToString())); - query.setValue("nick", na->nick); - this->RunQuery(query); - } - - void OnNickGroup(User *u, NickAlias *) - { - OnNickRegister(findnick(u->nick)); - } - - void InsertAlias(NickAlias *na) - { - SQLQuery query("INSERT INTO `anope_ns_alias` (nick, last_quit, last_realname, last_usermask, time_registered, last_seen, flags, display) VALUES(@nick, @last_quit, @last_realname, @last_usermask, @time_registered, @last_seen, @flags, @display) ON DUPLICATE KEY UPDATE last_quit=VALUES(last_quit), last_realname=VALUES(last_realname), last_usermask=VALUES(last_usermask), time_registered=VALUES(time_registered), last_seen=VALUES(last_seen), flags=VALUES(flags), display=VALUES(display)"); - query.setValue("nick", na->nick); - query.setValue("last_quit", na->last_quit); - query.setValue("last_realname", na->last_realname); - query.setValue("last_usermask", na->last_usermask); - query.setValue("time_registered", na->time_registered); - query.setValue("last_seen", na->last_seen); - query.setValue("flags", ToString(na->ToString())); - query.setValue("display", na->nc->display); - this->RunQuery(query); - } - - void InsertCore(NickCore *nc) - { - SQLQuery query("INSERT INTO `anope_ns_core` (display, pass, email, greet, flags, language, memomax) VALUES(@display, @pass, @email, @greet, @flags, @language, @memomax) ON DUPLICATE KEY UPDATE pass=VALUES(pass), email=VALUES(email), greet=VALUES(greet), flags=VALUES(flags), language=VALUES(language), memomax=VALUES(memomax)"); - query.setValue("display", nc->display); - query.setValue("pass", nc->pass); - query.setValue("email", nc->email); - query.setValue("greet", nc->greet); - query.setValue("flags", ToString(nc->ToString())); - query.setValue("language", nc->language); - query.setValue("memomax", nc->memos.memomax); - this->RunQuery(query); - } - - void OnNickRegister(NickAlias *na) - { - this->InsertCore(na->nc); - this->InsertAlias(na); - } - - void OnChangeCoreDisplay(NickCore *nc, const Anope::string &newdisplay) - { - SQLQuery query("UPDATE `anope_ns_core` SET `display` = @newdisplay WHERE `display` = @olddisplay"); - query.setValue("newdisplay", newdisplay); - query.setValue("olddisplay", nc->display); - this->RunQuery(query); - } - - void OnNickSuspend(NickAlias *na) - { - SQLQuery query("UPDATE `anope_ns_core` SET `flags` = @flags WHERE `display` = @display"); - query.setValue("flags", ToString(na->nc->ToString())); - query.setValue("display", na->nc->display); - this->RunQuery(query); - } - - void OnDelNick(NickAlias *na) - { - SQLQuery query("DELETE FROM `anope_ns_alias` WHERE `nick` = @nick"); - query.setValue("nick", na->nick); - this->RunQuery(query); - } - - void OnAccessAdd(ChannelInfo *ci, User *, ChanAccess *access) - { - SQLQuery query("INSERT INTO `anope_cs_access` (provider, data, mask, channel, last_seen, creator) VALUES (@provider, @data, @mask, @channel, @last_seen, @creator) ON DUPLICATE KEY UPDATE level=VALUES(level), display=VALUES(display), channel=VALUES(channel), last_seen=VALUES(last_seen), creator=VALUES(creator)"); - query.setValue("provider", access->provider->name); - query.setValue("data", access->Serialize()); - query.setValue("mask", access->mask); - query.setValue("channel", ci->name); - query.setValue("last_seen", access->last_seen); - query.setValue("creator", access->creator); - this->RunQuery(query); - } - - void OnAccessDel(ChannelInfo *ci, User *u, ChanAccess *access) - { - SQLQuery query("DELETE FROM `anope_cs_access` WHERE `mask` = @mask AND `channel` = @channel"); - query.setValue("mask", access->mask); - query.setValue("channel", ci->name); - this->RunQuery(query); - } - - void OnAccessClear(ChannelInfo *ci, User *u) - { - SQLQuery query("DELETE FROM `anope_cs_access` WHERE `channel` = @channel"); - query.setValue("channel", ci->name); - this->RunQuery(query); - } - - void OnLevelChange(User *u, ChannelInfo *ci, int pos, int what) - { - SQLQuery query("UPDATE `anope_cs_levels` SET `level` = @level WHERE `channel` = @channel AND `position` = @pos ON DUPLICATE KEY UPDATE level=VALUES(level), position=VALUES(position)"); - if (pos >= 0) - { - query.setValue("level", what); - query.setValue("channel", ci->name); - query.setValue("pos", pos); - this->RunQuery(query); - } - else - { - query.setValue("channel", ci->name); - for (int i = 0; i < CA_SIZE; ++i) - { - query.setValue("level", ci->levels[i]); - query.setValue("pos", i); - this->RunQuery(query); - } - } - } - - void OnChanForbidden(ChannelInfo *ci) - { - SQLQuery query("INSERT INTO `anope_cs_info` (name, time_registered, last_used, flags) VALUES (@name, @time_registered, @last_used, @flags)"); - query.setValue("name", ci->name); - query.setValue("time_registered", ci->time_registered); - query.setValue("last_used", ci->last_used); - query.setValue("flags", ToString(ci->ToString())); - this->RunQuery(query); - } - - void OnDelChan(ChannelInfo *ci) - { - SQLQuery query("DELETE FROM `anope_cs_info` WHERE `name` = @name"); - query.setValue("name", ci->name); - this->RunQuery(query); - } - - void OnChanRegistered(ChannelInfo *ci) - { - SQLQuery query("INSERT INTO `anope_cs_info` (name, founder, successor, descr, time_registered, last_used, last_topic, last_topic_setter, last_topic_time, flags, bantype, memomax, botnick, botflags, capsmin, capspercent, floodlines, floodsecs, repeattimes) VALUES(@name, @founder, @successor, @descr, @time_registered, @last_used, @last_topic_text, @last_topic_setter, @last_topic_time, @flags, @bantype, @memomax, @botnick, @botflags, @capsmin, @capspercent, @floodlines, @floodsecs, @repeattimes) ON DUPLICATE KEY UPDATE founder=VALUES(founder), successor=VALUES(successor), descr=VALUES(descr), time_registered=VALUES(time_registered), last_used=VALUES(last_used), last_topic=VALUES(last_topic), last_topic_setter=VALUES(last_topic_setter), last_topic_time=VALUES(last_topic_time), flags=VALUES(flags), bantype=VALUES(bantype), memomax=VALUES(memomax), botnick=VALUES(botnick), botflags=VALUES(botflags), capsmin=VALUES(capsmin), capspercent=VALUES(capspercent), floodlines=VALUES(floodlines), floodsecs=VALUES(floodsecs), repeattimes=VALUES(repeattimes)"); - query.setValue("name", ci->name); - query.setValue("founder", ci->GetFounder() ? ci->GetFounder()->display : ""); - query.setValue("successor", ci->successor ? ci->successor->display : ""); - query.setValue("descr", ci->desc); - query.setValue("time_registered", ci->time_registered); - query.setValue("last_used", ci->last_used); - query.setValue("last_topic_text", ci->last_topic); - query.setValue("last_topic_setter", ci->last_topic_setter); - query.setValue("last_topic_time", ci->last_topic_time); - query.setValue("flags", ToString(ci->ToString())); - query.setValue("bantype", ci->bantype); - query.setValue("memomax", ci->memos.memomax); - query.setValue("botnick", ci->bi ? ci->bi->nick : ""); - query.setValue("botflags", ToString(ci->botflags.ToString())); - query.setValue("capsmin", ci->capsmin); - query.setValue("capspercent", ci->capspercent); - query.setValue("floodlines", ci->floodlines); - query.setValue("floodsecs", ci->floodsecs); - query.setValue("repeattimes", ci->repeattimes); - this->RunQuery(query); - - query = "DELETE from `anope_cs_mlock` WHERE `channel` = @name"; - query.setValue("name", ci->name); - this->RunQuery(query); - for (std::multimap::const_iterator it = ci->GetMLock().begin(), it_end = ci->GetMLock().end(); it != it_end; ++it) - { - const ModeLock &ml = it->second; - ChannelMode *cm = ModeManager::FindChannelModeByName(ml.name); - - if (cm != NULL) - { - query = "INSERT INTO `anope_cs_mlock` (channel, mode, status, setter, created, param) VALUES(@channel, @mode, @status, @setter, @created, @param)"; - query.setValue("channel", ci->name); - query.setValue("mode", cm->NameAsString()); - query.setValue("status", ml.set ? 1 : 0); - query.setValue("setter", ml.setter); - query.setValue("created", ml.created); - query.setValue("param", ml.param); - this->RunQuery(query); - } - } - } - - void OnChanSuspend(ChannelInfo *ci) - { - SQLQuery query("UPDATE `anope_cs_info` SET `flags` = @flags WHERE `name` = @name"); - query.setValue("flags", ToString(ci->ToString())); - query.setValue("name", ci->name); - this->RunQuery(query); - } - - void OnAkickAdd(ChannelInfo *ci, AutoKick *ak) - { - SQLQuery query("INSERT INTO `anope_cs_akick` (channel, flags, mask, reason, creator, created, last_used) VALUES(@channel, @flags, @mask, @reason, @creator, @created, @last_used)"); - query.setValue("channel", ci->name); - query.setValue("flags", ak->HasFlag(AK_ISNICK) ? "ISNICK" : ""); - query.setValue("mask", ak->HasFlag(AK_ISNICK) ? ak->nc->display : ak->mask); - query.setValue("reason", ak->reason); - query.setValue("creator", ak->creator); - query.setValue("created", ak->addtime); - query.setValue("last_used", ak->last_used); - this->RunQuery(query); - } - - void OnAkickDel(ChannelInfo *ci, AutoKick *ak) - { - SQLQuery query("DELETE FROM `anope_cs_akick` WHERE `channel`= @mask AND `mask` = @mask"); - query.setValue("channel", ci->name); - query.setValue("mask", ak->HasFlag(AK_ISNICK) ? ak->nc->display : ak->mask); - this->RunQuery(query); - } - - EventReturn OnMLock(ChannelInfo *ci, ModeLock *lock) - { - ChannelMode *cm = ModeManager::FindChannelModeByName(lock->name); - if (cm != NULL) - { - SQLQuery query("INSERT INTO `anope_cs_mlock` (channel, mode, status, setter, created, param) VALUES(@channel, @mode, @status, @setter, @created, @param)"); - query.setValue("channel", ci->name); - query.setValue("mode", cm->NameAsString()); - query.setValue("status", lock->set ? 1 : 0); - query.setValue("setter", lock->setter); - query.setValue("created", lock->created); - query.setValue("param", lock->param); - this->RunQuery(query); - } - return EVENT_CONTINUE; - } - - EventReturn OnUnMLock(ChannelInfo *ci, ChannelMode *mode, const Anope::string ¶m) - { - ChannelMode *cm = ModeManager::FindChannelModeByName(mode->Name); - if (cm != NULL) - { - SQLQuery query("DELETE FROM `anope_cs_mlock` WHERE `channel` = @channel AND `mode` = @mode AND `param` = @param"); - query.setValue("channel", ci->name); - query.setValue("mode", mode->NameAsString()); - query.setValue("param", param); - this->RunQuery(query); - } - return EVENT_CONTINUE; - } - - void OnBotCreate(BotInfo *bi) - { - SQLQuery query("INSERT INTO `anope_bs_core` (nick, user, host, rname, flags, created, chancount) VALUES(@nick, @user, @host, @rname, @flags, @created, @chancuont) ON DUPLICATE KEY UPDATE nick=VALUES(nick), user=VALUES(user), host=VALUES(host), rname=VALUES(rname), flags=VALUES(flags), created=VALUES(created), chancount=VALUES(chancount)"); - query.setValue("nick", bi->nick); - query.setValue("user", bi->GetIdent()); - query.setValue("host", bi->host); - query.setValue("rname", bi->realname); - query.setValue("flags", ToString(bi->ToString())); - query.setValue("created", bi->created); - query.setValue("chancount", bi->chancount); - this->RunQuery(query); - } - - void OnBotChange(BotInfo *bi) - { - OnBotCreate(bi); - } - - void OnBotDelete(BotInfo *bi) - { - SQLQuery query("UPDATE `anope_cs_info` SET `botnick` = '' WHERE `botnick` = @botnick"); - query.setValue("botnick", bi->nick); - this->RunQuery(query); - } - - EventReturn OnBotAssign(User *sender, ChannelInfo *ci, BotInfo *bi) - { - SQLQuery query("UPDATE `anope_cs_info` SET `botnick` = @botnick WHERE `name` = @channel"); - query.setValue("botnick", bi->nick); - query.setValue("channel", ci->name); - this->RunQuery(query); - return EVENT_CONTINUE; - } - - EventReturn OnBotUnAssign(User *sender, ChannelInfo *ci) - { - SQLQuery query("UPDATE `anope_cs_info` SET `botnick` = '' WHERE `name` = @channel"); - query.setValue("channel", ci->name); - this->RunQuery(query); - return EVENT_CONTINUE; - } - - void OnBadWordAdd(ChannelInfo *ci, BadWord *bw) - { - SQLQuery query("INSERT INTO `anope_bs_badwords` (channel, word, type) VALUES(@channel, @word, @type) ON DUPLICATE KEY UPDATE channel=VALUES(channel), word=VALUES(word), type=VALUES(type)"); - query.setValue("channel", ci->name); - query.setValue("word", bw->word); - switch (bw->type) - { - case BW_SINGLE: - query.setValue("type", "SINGLE"); - break; - case BW_START: - query.setValue("type", "START"); - break; - case BW_END: - query.setValue("type", "END"); - break; - default: - query.setValue("type", "ANY"); - } - this->RunQuery(query); - } - - void OnBadWordDel(ChannelInfo *ci, BadWord *bw) - { - SQLQuery query("DELETE FROM `anope_bs_badwords` WHERE `channel` = @channel AND `word` = @word AND `type` = @type"); - query.setValue("channel", ci->name); - query.setValue("word", bw->word); - switch (bw->type) - { - case BW_SINGLE: - query.setValue("type", "SINGLE"); - break; - case BW_START: - query.setValue("type", "START"); - break; - case BW_END: - query.setValue("type", "END"); - break; - default: - query.setValue("type", "ANY"); - } - this->RunQuery(query); - } - - void OnMemoSend(const Anope::string &source, const Anope::string &target, MemoInfo *mi, Memo *m) - { - const Anope::string &mtype = (!target.empty() && target[0] == '#' ? "CHAN" : "NICK"); - SQLQuery query("INSERT INTO `anope_ms_info` (receiver, flags, time, sender, text, serv) VALUES(@receiver, @flags, @time, @sender, @text, @serv)"); - query.setValue("receiver", target); - query.setValue("flags", ToString(m->ToString())); - query.setValue("time", m->time); - query.setValue("sender", source); - query.setValue("text", m->text); - query.setValue("serv", mtype); - this->RunQuery(query); - } - - void OnMemoDel(const NickCore *nc, MemoInfo *mi, Memo *m) - { - SQLQuery query; - - if (m) - { - query = "DELETE FROM `anope_ms_info` WHERE `receiver` = @receiver AND `time` = @time"; - query.setValue("receiver", nc->display); - query.setValue("time", m->time); - } - else - { - query = "DELETE FROM `anope_ms_info` WHERE `receiver` = @receiver"; - query.setValue("receiver", nc->display); - } - - this->RunQuery(query); - } - - void OnMemoDel(ChannelInfo *ci, MemoInfo *mi, Memo *m) - { - SQLQuery query; - - if (m) - { - query = "DELETE FROM `anope_ms_info` WHERE `receiver` = @receiver AND `time` = @time"; - query.setValue("receiver", ci->name); - query.setValue("time", m->time); - } - else - { - query = "DELETE FROM `anope_ms_info` WHERE `receiver` = @receiver"; - query.setValue("receiver", ci->name); - } - - this->RunQuery(query); - } - - EventReturn OnExceptionAdd(Exception *ex) - { - SQLQuery query("INSERT INTO `anope_os_exceptions` (mask, slimit, who, reason, time, expires) VALUES(@mask, @slimit, @who, @reason, @time, @expires)"); - query.setValue("mask", ex->mask); - query.setValue("slimit", ex->limit); - query.setValue("who", ex->who); - query.setValue("reason", ex->reason); - query.setValue("time", ex->time); - query.setValue("expires", ex->expires); - return EVENT_CONTINUE; - } - - void OnExceptionDel(User *, Exception *ex) - { - SQLQuery query("DELETE FROM `anope_os_exceptions` WHERE `mask` = @mask"); - query.setValue("mask", ex->mask); - this->RunQuery(query); - } - - EventReturn OnAddXLine(XLine *x, XLineManager *xlm) - { - SQLQuery query("INSERT INTO `anope_os_xlines` (type, mask, xby, reason, seton, expire) VALUES(@type, @mask, @xby, @reason, @seton, @expire)"); - query.setValue("type", Anope::string(xlm->Type())); - query.setValue("mask", x->Mask); - query.setValue("xby", x->By); - query.setValue("reason", x->Reason); - query.setValue("seton", x->Created); - query.setValue("expire", x->Expires); - this->RunQuery(query); - return EVENT_CONTINUE; - } - - void OnDelXLine(User *, XLine *x, XLineManager *xlm) - { - SQLQuery query; - - if (x) - { - query = "DELETE FROM `anope_os_xlines` WHERE `mask` = @mask AND `type` = @type"; - query.setValue("mask", x->Mask); - query.setValue("type", Anope::string(xlm->Type())); - } - else - { - query = "DELETE FROM `anope_os_xlines` WHERE `type` = @type"; - query.setValue("type", Anope::string(xlm->Type())); - } - - this->RunQuery(query); - } - - void OnDeleteVhost(NickAlias *na) - { - SQLQuery query("DELETE FROM `anope_hs_core` WHERE `nick` = @nick"); - query.setValue("nick", na->nick); - this->RunQuery(query); - } - - void OnSetVhost(NickAlias *na) - { - SQLQuery query("INSERT INTO `anope_hs_core` (nick, vident, vhost, creator, time) VALUES(@nick, @vident, @vhost, @creator, @time)"); - query.setValue("nick", na->nick); - query.setValue("vident", na->hostinfo.GetIdent()); - query.setValue("vhost", na->hostinfo.GetHost()); - query.setValue("creator", na->hostinfo.GetCreator()); - query.setValue("time", na->hostinfo.GetTime()); - this->RunQuery(query); - } -}; - -void MySQLInterface::OnResult(const SQLResult &r) -{ - Log(LOG_DEBUG) << "MySQL successfully executed query: " << r.finished_query; -} - -void MySQLInterface::OnError(const SQLResult &r) -{ - 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(); -} - - -static void Write(const Anope::string &data) -{ - SQLQuery query("INSERT INTO `anope_extra` (data) VALUES(@data)"); - query.setValue("data", data); - me->RunQuery(data); -} - -static void WriteNickMetadata(const Anope::string &key, const Anope::string &data) -{ - if (!CurNick) - throw CoreException("WriteNickMetadata without a nick to write"); - - SQLQuery query("INSERT INTO `anope_ns_alias_metadata` (nick, name, value) VALUES(@nick, @name, @value)"); - query.setValue("nick", CurNick->nick); - query.setValue("name", key); - query.setValue("value", data); - me->RunQuery(query); -} - -static void WriteCoreMetadata(const Anope::string &key, const Anope::string &data) -{ - if (!CurCore) - throw CoreException("WritCoreMetadata without a core to write"); - - SQLQuery query("INSERT INTO `anope_ns_core_metadata` (nick, name, value) VALUES(@nick, @name, @value)"); - query.setValue("nick", CurCore->display); - query.setValue("name", key); - query.setValue("value", data); - me->RunQuery(query); -} - -static void WriteChannelMetadata(const Anope::string &key, const Anope::string &data) -{ - if (!CurChannel) - throw CoreException("WriteChannelMetadata without a channel to write"); - - SQLQuery query("INSERT INTO `anope_cs_info_metadata` (channel, name, value) VALUES(@channel, @name, @value)"); - query.setValue("channel", CurChannel->name); - query.setValue("name", key); - query.setValue("value", data); - me->RunQuery(query); -} - -static void WriteBotMetadata(const Anope::string &key, const Anope::string &data) -{ - if (!CurBot) - throw CoreException("WriteBotMetadata without a bot to write"); - - SQLQuery query("INSERT INTO `anope_bs_info_metadata` (botname, name, value) VALUES(@botname, @name, @value)"); - query.setValue("botname", CurBot->nick); - query.setValue("name", key); - query.setValue("value", data); - me->RunQuery(query); -} - -static void SaveDatabases() -{ - SQLQuery query; - - query = "TRUNCATE TABLE `anope_ns_core`"; - me->RunQuery(query); - - query = "TRUNCATE TABLE `anope_ms_info`"; - me->RunQuery(query); - - query = "TRUNCATE TABLE `anope_ns_alias`"; - me->RunQuery(query); - - for (nickcore_map::const_iterator nit = NickCoreList.begin(), nit_end = NickCoreList.end(); nit != nit_end; ++nit) - { - NickCore *nc = nit->second; - - me->InsertCore(nc); - - for (std::list::iterator it = nc->aliases.begin(), it_end = nc->aliases.end(); it != it_end; ++it) - { - me->InsertAlias(*it); - if ((*it)->hostinfo.HasVhost()) - me->OnSetVhost(*it); - } - - for (std::vector::iterator it = nc->access.begin(), it_end = nc->access.end(); it != it_end; ++it) - { - query = "INSERT INTO `anope_ns_access` (display, access) VALUES(@display, @access)"; - query.setValue("display", nc->display); - query.setValue("access", *it); - me->RunQuery(query); - } - - for (unsigned j = 0, end = nc->memos.memos.size(); j < end; ++j) - { - Memo *m = nc->memos.memos[j]; - - me->OnMemoSend(m->sender, nc->display, &nc->memos, m); - } - } - - query = "TRUNCATE TABLE `anope_bs_core`"; - me->RunQuery(query); - - for (Anope::insensitive_map::const_iterator it = BotListByNick.begin(), it_end = BotListByNick.end(); it != it_end; ++it) - me->OnBotCreate(it->second); - - query = "TRUNCATE TABLE `anope_cs_info`"; - me->RunQuery(query); - - query = "TRUNCATE TABLE `anope_bs_badwords`"; - me->RunQuery(query); - - query = "TRUNCATE TABLE `anope_cs_access`"; - me->RunQuery(query); - - query = "TRUNCATE TABLE `anope_cs_akick`"; - me->RunQuery(query); - - query = "TRUNCATE TABLE `anope_cs_levels`"; - me->RunQuery(query); - - for (registered_channel_map::const_iterator it = RegisteredChannelList.begin(), it_end = RegisteredChannelList.end(); it != it_end; ++it) - { - ChannelInfo *ci = it->second; - - me->OnChanRegistered(ci); - - for (unsigned j = 0, end = ci->GetBadWordCount(); j < end; ++j) - { - BadWord *bw = ci->GetBadWord(j); - - me->OnBadWordAdd(ci, bw); - } - - for (unsigned j = 0, end = ci->GetAccessCount(); j < end; ++j) - { - ChanAccess *access = ci->GetAccess(j); - - me->OnAccessAdd(ci, NULL, access); - } - - for (unsigned j = 0, end = ci->GetAkickCount(); j < end; ++j) - { - AutoKick *ak = ci->GetAkick(j); - - me->OnAkickAdd(ci, ak); - } - - for (int k = 0; k < CA_SIZE; ++k) - me->OnLevelChange(NULL, ci, -1, -1); - - for (unsigned j = 0, end = ci->memos.memos.size(); j < end; ++j) - { - Memo *m = ci->memos.memos[j]; - - me->OnMemoSend(m->sender, ci->name, &ci->memos, m); - } - } - - for (std::list::iterator it = XLineManager::XLineManagers.begin(), it_end = XLineManager::XLineManagers.end(); it != it_end; ++it) - for (unsigned i = 0, end = (*it)->GetCount(); i < end; ++i) - me->OnAddXLine((*it)->GetEntry(i), *it); - - if (me->SessionInterface) - for (SessionService::ExceptionVector::iterator it = me->SessionInterface->GetExceptions().begin(); it != me->SessionInterface->GetExceptions().end(); ++it) - me->OnExceptionAdd(*it); -} - -void CommandSQLSync::Execute(CommandSource &source, const std::vector ¶ms) -{ - SaveDatabases(); - source.Reply(_("Updating MySQL.")); - return; -} - -MODULE_INIT(DBMySQL) - diff --git a/modules/core/db_mysql_live.cpp b/modules/core/db_mysql_live.cpp deleted file mode 100644 index ecfdc70d1..000000000 --- a/modules/core/db_mysql_live.cpp +++ /dev/null @@ -1,287 +0,0 @@ -#include "module.h" -#include "../extra/async_commands.h" -#include "../extra/sql.h" - -class SQLCache : public Timer -{ - typedef std::map > cache_map; - cache_map cache; - public: - - SQLCache() : Timer(300, Anope::CurTime, true) { } - - bool Check(const Anope::string &item) - { - cache_map::iterator it = this->cache.find(item); - if (it != this->cache.end() && Anope::CurTime - it->second < 5) - return true; - - this->cache[item] = Anope::CurTime; - return false; - } - - void Tick(time_t) - { - for (cache_map::iterator it = this->cache.begin(), next_it; it != this->cache.end(); it = next_it) - { - next_it = it; - ++next_it; - - if (Anope::CurTime - it->second > 5) - this->cache.erase(it); - } - } -}; - -static void ChanInfoUpdate(const SQLResult &res) -{ - try - { - ChannelInfo *ci = cs_findchan(res.Get(0, "name")); - if (!ci) - ci = new ChannelInfo(res.Get(0, "name")); - ci->SetFounder(findcore(res.Get(0, "founder"))); - ci->successor = findcore(res.Get(0, "successor")); - ci->desc = res.Get(0, "descr"); - ci->time_registered = convertTo(res.Get(0, "time_registered")); - ci->ClearFlags(); - ci->FromString(BuildStringVector(res.Get(0, "flags"))); - ci->bantype = convertTo(res.Get(0, "bantype")); - ci->memos.memomax = convertTo(res.Get(0, "memomax")); - - if (res.Get(0, "botnick").equals_cs(ci->bi ? ci->bi->nick : "") == false) - { - if (ci->bi) - ci->bi->UnAssign(NULL, ci); - BotInfo *bi = findbot(res.Get(0, "botnick")); - if (bi) - bi->Assign(NULL, ci); - } - - ci->capsmin = convertTo(res.Get(0, "capsmin")); - ci->capspercent = convertTo(res.Get(0, "capspercent")); - ci->floodlines = convertTo(res.Get(0, "floodlines")); - ci->floodsecs = convertTo(res.Get(0, "floodsecs")); - ci->repeattimes = convertTo(res.Get(0, "repeattimes")); - - if (ci->c) - check_modes(ci->c); - } - catch (const SQLException &ex) - { - Log(LOG_DEBUG) << ex.GetReason(); - } - catch (const ConvertException &) { } -} - -static void NickInfoUpdate(const SQLResult &res) -{ - try - { - NickCore *nc = findcore(res.Get(0, "display")); - if (!nc) - return; - NickAlias *na = findnick(res.Get(0, "nick")); - if (!na) - na = new NickAlias(res.Get(0, "nick"), nc); - - na->last_quit = res.Get(0, "last_quit"); - na->last_realname = res.Get(0, "last_realname"); - na->last_usermask = res.Get(0, "last_usermask"); - na->time_registered = convertTo(res.Get(0, "time_registered")); - na->last_seen = convertTo(res.Get(0, "last_seen")); - na->ClearFlags(); - na->FromString(BuildStringVector(res.Get(0, "flags"))); - - if (na->nc != nc) - { - std::list::iterator it = std::find(na->nc->aliases.begin(), na->nc->aliases.end(), na); - if (it != na->nc->aliases.end()) - na->nc->aliases.erase(it); - - na->nc = nc; - na->nc->aliases.push_back(na); - } - } - catch (const SQLException &ex) - { - Log(LOG_DEBUG) << ex.GetReason(); - } - catch (const ConvertException &) { } -} - -static void NickCoreUpdate(const SQLResult &res) -{ - try - { - NickCore *nc = findcore(res.Get(0, "display")); - if (!nc) - nc = new NickCore(res.Get(0, "display")); - - nc->pass = res.Get(0, "pass"); - nc->email = res.Get(0, "email"); - nc->greet = res.Get(0, "greet"); - nc->ClearFlags(); - nc->FromString(BuildStringVector(res.Get(0, "flags"))); - nc->language = res.Get(0, "language"); - } - catch (const SQLException &ex) - { - Log(LOG_DEBUG) << ex.GetReason(); - } - catch (const ConvertException &) { } -} - -class MySQLLiveModule : public Module -{ - service_reference SQL; - service_reference ACS; - - SQLCache chan_cache, nick_cache, core_cache; - - SQLResult RunQuery(const SQLQuery &query) - { - if (!this->SQL) - throw SQLException("Unable to locate SQL reference, is m_mysql loaded and configured correctly?"); - - SQLResult res = SQL->RunQuery(query); - if (!res.GetError().empty()) - throw SQLException(res.GetError()); - return res; - } - - CommandMutex *CurrentCommand() - { - if (this->ACS) - return this->ACS->CurrentCommand(); - return NULL; - } - - public: - MySQLLiveModule(const Anope::string &modname, const Anope::string &creator) : - Module(modname, creator, DATABASE), SQL("mysql/main"), ACS("asynch_commands") - { - Implementation i[] = { I_OnFindChan, I_OnFindNick, I_OnFindCore, I_OnShutdown }; - ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); - } - - void OnShutdown() - { - Implementation i[] = { I_OnFindChan, I_OnFindNick, I_OnFindCore }; - for (size_t j = 0; j < 3; ++j) - ModuleManager::Detach(i[j], this); - } - - void OnFindChan(const Anope::string &chname) - { - if (chan_cache.Check(chname)) - return; - - try - { - SQLQuery query("SELECT * FROM `anope_cs_info` WHERE `name` = @name"); - query.setValue("name", chname); - CommandMutex *current_command = this->CurrentCommand(); - if (current_command) - { - current_command->Unlock(); - try - { - SQLResult res = this->RunQuery(query); - current_command->Lock(); - ChanInfoUpdate(res); - } - catch (const SQLException &) - { - current_command->Lock(); - throw; - } - } - else - { - SQLResult res = this->RunQuery(query); - ChanInfoUpdate(res); - } - } - catch (const SQLException &ex) - { - Log(LOG_DEBUG) << "OnFindChan: " << ex.GetReason(); - } - } - - void OnFindNick(const Anope::string &nick) - { - if (nick_cache.Check(nick)) - return; - - try - { - SQLQuery query("SELECT * FROM `anope_ns_alias` WHERE `nick` = @nick"); - query.setValue("nick", nick); - CommandMutex *current_command = this->CurrentCommand(); - if (current_command) - { - current_command->Unlock(); - try - { - SQLResult res = this->RunQuery(query); - current_command->Lock(); - NickInfoUpdate(res); - } - catch (const SQLException &) - { - current_command->Lock(); - throw; - } - } - else - { - SQLResult res = this->RunQuery(query); - NickInfoUpdate(res); - } - } - catch (const SQLException &ex) - { - Log(LOG_DEBUG) << "OnFindNick: " << ex.GetReason(); - } - } - - void OnFindCore(const Anope::string &nick) - { - if (core_cache.Check(nick)) - return; - - try - { - SQLQuery query("SELECT * FROM `anope_ns_core` WHERE `display` = @display"); - query.setValue("display", nick); - CommandMutex *current_command = this->CurrentCommand(); - if (current_command) - { - current_command->Unlock(); - try - { - SQLResult res = this->RunQuery(query); - current_command->Lock(); - NickCoreUpdate(res); - } - catch (const SQLException &) - { - current_command->Lock(); - throw; - } - } - else - { - SQLResult res = this->RunQuery(query); - NickCoreUpdate(res); - } - } - catch (const SQLException &ex) - { - Log(LOG_DEBUG) << "OnFindCore: " << ex.GetReason(); - } - } -}; - -MODULE_INIT(MySQLLiveModule) diff --git a/modules/core/db_plain.cpp b/modules/core/db_plain.cpp deleted file mode 100644 index c41cea2d2..000000000 --- a/modules/core/db_plain.cpp +++ /dev/null @@ -1,908 +0,0 @@ -/* - * (C) 2003-2011 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 "os_session.h" - -Anope::string DatabaseFile; -std::stringstream db_buffer; -service_reference SessionInterface("session"); - -/** Enum used for what METADATA type we are reading - */ -enum MDType -{ - MD_NONE, - MD_NC, - MD_NA, - MD_BI, - MD_CH -}; - -/* Character for newlines, in my research I have concluded it is significantly faster - * to use \n instead of std::endl because std::endl also flushes the buffer, which - * is not necessary here - */ -static const char endl = '\n'; - -/** Read from the database and call the events - * @param m If specified it only calls the read events for the specific module - */ -static void ReadDatabase(Module *m = NULL) -{ - EventReturn MOD_RESULT; - MDType Type = MD_NONE; - - std::fstream db; - db.open(DatabaseFile.c_str(), std::ios_base::in); - - if (!db.is_open()) - { - Log() << "Unable to open " << DatabaseFile << " for reading!"; - return; - } - - NickCore *nc = NULL; - NickAlias *na = NULL; - BotInfo *bi = NULL; - ChannelInfo *ci = NULL; - - Anope::string buf; - while (std::getline(db, buf.str())) - { - if (buf.empty()) - continue; - - spacesepstream sep(buf); - std::vector params; - while (sep.GetToken(buf)) - { - if (buf[0] == ':') - { - buf.erase(buf.begin()); - if (!buf.empty() && !sep.StreamEnd()) - params.push_back(buf + " " + sep.GetRemaining()); - else if (!sep.StreamEnd()) - params.push_back(sep.GetRemaining()); - else if (!buf.empty()) - params.push_back(buf); - break; - } - else - params.push_back(buf); - } - - if (m) - MOD_RESULT = m->OnDatabaseRead(params); - else - FOREACH_RESULT(I_OnDatabaseRead, OnDatabaseRead(params)); - if (MOD_RESULT == EVENT_STOP) - continue; - - if (!params.empty()) - { - if (params[0].equals_ci("NC")) - { - nc = findcore(params[1]); - Type = MD_NC; - } - else if (params[0].equals_ci("NA")) - { - na = findnick(params[2]); - Type = MD_NA; - } - else if (params[0].equals_ci("BI")) - { - bi = findbot(params[1]); - Type = MD_BI; - } - else if (params[0].equals_ci("CH")) - { - ci = cs_findchan(params[1]); - Type = MD_CH; - } - else if (params[0].equals_ci("MD")) - { - Anope::string key = params[1]; - params.erase(params.begin()); - params.erase(params.begin()); - - if (Type == MD_NC && nc) - { - try - { - if (m) - m->OnDatabaseReadMetadata(nc, key, params); - else - FOREACH_RESULT(I_OnDatabaseReadMetadata, OnDatabaseReadMetadata(nc, key, params)); - } - catch (const DatabaseException &ex) - { - Log() << "[db_plain]: " << ex.GetReason(); - } - } - else if (Type == MD_NA && na) - { - try - { - if (m) - m->OnDatabaseReadMetadata(na, key, params); - else - FOREACH_RESULT(I_OnDatabaseReadMetadata, OnDatabaseReadMetadata(na, key, params)); - } - catch (const DatabaseException &ex) - { - Log() << "[db_plain]: " << ex.GetReason(); - } - } - else if (Type == MD_BI && bi) - { - try - { - if (m) - m->OnDatabaseReadMetadata(bi, key, params); - else - FOREACH_RESULT(I_OnDatabaseReadMetadata, OnDatabaseReadMetadata(bi, key, params)); - } - catch (const DatabaseException &ex) - { - Log() << "[db_plain]: " << ex.GetReason(); - } - } - else if (Type == MD_CH && ci) - { - try - { - if (m) - m->OnDatabaseReadMetadata(ci, key, params); - else - FOREACH_RESULT(I_OnDatabaseReadMetadata, OnDatabaseReadMetadata(ci, key, params)); - } - catch (const DatabaseException &ex) - { - Log() << "[db_plain]: " << ex.GetReason(); - } - } - } - } - } - - db.close(); -} - -struct ChannelLevel -{ - ChannelAccess Level; - Anope::string Name; -}; - -ChannelLevel ChannelLevels[] = { - { CA_ACCESS_LIST, "ACCESS_LIST" }, - { CA_NOKICK, "NOKICK" }, - { CA_FANTASIA, "FANTASIA" }, - { CA_GREET, "GREET" }, - { CA_AUTOVOICE, "AUTOVOICE" }, - { CA_VOICEME, "VOICEME" }, - { CA_VOICE, "VOICE" }, - { CA_INFO, "INFO" }, - { CA_SAY, "SAY" }, - { CA_AUTOHALFOP, "AUTOHALFOP" }, - { CA_HALFOPME, "HALFOPME" }, - { CA_HALFOP, "HALFOP" }, - { CA_KICK, "KICK" }, - { CA_SIGNKICK, "SIGNKICK" }, - { CA_BAN, "BAN" }, - { CA_TOPIC, "TOPIC" }, - { CA_MODE, "MODE" }, - { CA_GETKEY, "GETKEY" }, - { CA_INVITE, "INVITE" }, - { CA_UNBAN, "UNBAN" }, - { CA_AUTOOP, "AUTOOP" }, - { CA_OPDEOPME, "OPDEOPME" }, - { CA_OPDEOP, "OPDEOP" }, - { CA_AUTOPROTECT, "AUTOPROTECT" }, - { CA_AKICK, "AKICK" }, - { CA_BADWORDS, "BADWORDS" }, - { CA_ASSIGN, "ASSIGN" }, - { CA_MEMO, "MEMO" }, - { CA_ACCESS_CHANGE, "ACCESS_CHANGE" }, - { CA_PROTECTME, "PROTECTME" }, - { CA_PROTECT, "PROTECT" }, - { CA_SET, "SET" }, - { CA_AUTOOWNER, "AUTOPROTECT" }, - { CA_OWNERME, "OWNERME" }, - { CA_OWNER, "OWNER" }, - { CA_FOUNDER, "FOUNDER" }, - { CA_SIZE, "" } -}; - -static Anope::string ToString(const std::vector &strings) -{ - Anope::string ret; - - for (unsigned i = 0; i < strings.size(); ++i) - ret += " " + strings[i]; - - if (!ret.empty()) - ret.erase(ret.begin()); - - return ret; -} - -static void LoadNickCore(const std::vector ¶ms) -{ - NickCore *nc = new NickCore(params[0]); - /* Clear default flags */ - nc->ClearFlags(); - - nc->pass = params.size() > 1 ? params[1] : ""; - - Log(LOG_DEBUG_2) << "[db_plain]: Loaded NickCore " << nc->display; -} - -static void LoadNickAlias(const std::vector ¶ms) -{ - NickCore *nc = findcore(params[0]); - if (!nc) - { - Log() << "[db_plain]: Unable to find core " << params[0]; - return; - } - - NickAlias *na = new NickAlias(params[1], nc); - - na->time_registered = params[2].is_pos_number_only() ? convertTo(params[2]) : 0; - - na->last_seen = params[3].is_pos_number_only() ? convertTo(params[3]) : 0; - - Log(LOG_DEBUG_2) << "[db_plain}: Loaded nickalias for " << na->nick; -} - -static void LoadBotInfo(const std::vector ¶ms) -{ - BotInfo *bi = findbot(params[0]); - if (!bi) - bi = new BotInfo(params[0], params[1], params[2]); - - bi->created = params[3].is_pos_number_only() ? convertTo(params[3]) : 0; - bi->chancount = params[4].is_pos_number_only() ? convertTo(params[4]) : 0; - bi->realname = params[5]; - - Log(LOG_DEBUG_2) << "[db_plain]: Loaded botinfo for " << bi->nick; -} - -static void LoadChanInfo(const std::vector ¶ms) -{ - ChannelInfo *ci = new ChannelInfo(params[0]); - /* CLear default mlock */ - ci->ClearMLock(); - /* Remove default channel flags */ - ci->ClearFlags(); - ci->botflags.ClearFlags(); - - ci->time_registered = params[1].is_pos_number_only() ? convertTo(params[1]) : 0; - - ci->last_used = params[2].is_pos_number_only() ? convertTo(params[2]) : 0; - - Log(LOG_DEBUG_2) << "[db_plain]: loaded channel " << ci->name; -} - -static void LoadOperInfo(const std::vector ¶ms) -{ - if (params[0].equals_ci("STATS")) - { - maxusercnt = params[1].is_pos_number_only() ? convertTo(params[1]) : 0; - maxusertime = params[2].is_pos_number_only() ? convertTo(params[2]) : 0; - } - else if (params[0].equals_ci("SXLINE")) - { - char type = params[1][0]; - for (std::list::iterator it = XLineManager::XLineManagers.begin(), it_end = XLineManager::XLineManagers.end(); it != it_end; ++it) - { - XLineManager *xl = *it; - if (xl->Type() == type) - { - Anope::string mask = params[2]; - Anope::string by = params[3]; - time_t seton = params[4].is_pos_number_only() ? convertTo(params[4]) : 0; - time_t expires = params[5].is_pos_number_only() ? convertTo(params[5]) : 0; - Anope::string reason = params[6]; - - XLine *x = xl->Add(mask, by, expires, reason); - if (x) - x->Created = seton; - break; - } - } - } - else if (params[0].equals_ci("EXCEPTION") && SessionInterface) - { - Exception *exception = new Exception(); - exception->mask = params[1]; - exception->limit = params[2].is_pos_number_only() ? convertTo(params[2]) : 1; - exception->who = params[3]; - exception->time = params[4].is_pos_number_only() ? convertTo(params[4]) : 0; - exception->expires = params[5].is_pos_number_only() ? convertTo(params[5]) : 0; - exception->reason = params[6]; - SessionInterface->AddException(exception); - } -} - -void Write(const Anope::string &buf) -{ - db_buffer << buf << endl; -} - -void WriteMetadata(const Anope::string &key, const Anope::string &data) -{ - Write("MD " + key + " " + data); -} - -class DBPlain : public Module -{ - /* Day the last backup was on */ - int LastDay; - /* Backup file names */ - std::list Backups; - public: - DBPlain(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, DATABASE) - { - this->SetAuthor("Anope"); - - Implementation i[] = { I_OnReload, I_OnDatabaseRead, I_OnLoadDatabase, I_OnDatabaseReadMetadata, I_OnSaveDatabase, I_OnModuleLoad }; - ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); - - OnReload(); - - LastDay = 0; - } - - ~DBPlain() - { - } - - void BackupDatabase() - { - /* Do not backup a database that doesn't exist */ - if (!IsFile(DatabaseFile)) - return; - - time_t now = Anope::CurTime; - tm *tm = localtime(&now); - - if (tm->tm_mday != LastDay) - { - LastDay = tm->tm_mday; - Anope::string newname = "backups/" + DatabaseFile + "." + stringify(tm->tm_year) + stringify(tm->tm_mon) + stringify(tm->tm_mday); - - /* Backup already exists */ - if (IsFile(newname)) - return; - - Log(LOG_DEBUG) << "db_plain: Attemping to rename " << DatabaseFile << " to " << newname; - if (rename(DatabaseFile.c_str(), newname.c_str())) - { - Log() << "Unable to back up database!"; - - if (!Config->NoBackupOkay) - quitting = true; - - return; - } - - Backups.push_back(newname); - - unsigned KeepBackups = Config->KeepBackups; - if (KeepBackups && Backups.size() > KeepBackups) - { - DeleteFile(Backups.front().c_str()); - Backups.pop_front(); - } - } - } - - void OnReload() - { - ConfigReader config; - DatabaseFile = config.ReadValue("db_plain", "database", "anope.db", 0); - } - - EventReturn OnDatabaseRead(const std::vector ¶ms) - { - Anope::string key = params[0]; - std::vector otherparams = params; - otherparams.erase(otherparams.begin()); - - if (key.equals_ci("NC")) - LoadNickCore(otherparams); - else if (key.equals_ci("NA")) - LoadNickAlias(otherparams); - else if (key.equals_ci("BI")) - LoadBotInfo(otherparams); - else if (key.equals_ci("CH")) - LoadChanInfo(otherparams); - else if (key.equals_ci("OS")) - LoadOperInfo(otherparams); - - return EVENT_CONTINUE; - } - - EventReturn OnLoadDatabase() - { - ReadDatabase(); - - /* No need to ever reload this again, although this should never be trigged again */ - ModuleManager::Detach(I_OnLoadDatabase, this); - ModuleManager::Detach(I_OnDatabaseReadMetadata, this); - - return EVENT_STOP; - } - - EventReturn OnDatabaseReadMetadata(NickCore *nc, const Anope::string &key, const std::vector ¶ms) - { - try - { - if (key.equals_ci("LANGUAGE")) - nc->language = params[0]; - else if (key.equals_ci("MEMOMAX")) - nc->memos.memomax = params[0].is_pos_number_only() ? convertTo(params[0]) : -1; - else if (key.equals_ci("EMAIL")) - nc->email = params[0]; - else if (key.equals_ci("GREET")) - nc->greet = params[0]; - else if (key.equals_ci("ACCESS")) - nc->AddAccess(params[0]); - else if (key.equals_ci("CERT")) - nc->AddCert(params[0]); - else if (key.equals_ci("FLAGS")) - nc->FromString(params); - else if (key.equals_ci("MI")) - { - Memo *m = new Memo; - m->time = params[0].is_pos_number_only() ? convertTo(params[0]) : 0; - m->sender = params[1]; - for (unsigned j = 2; params[j].equals_ci("UNREAD") || params[j].equals_ci("RECEIPT"); ++j) - { - if (params[j].equals_ci("UNREAD")) - m->SetFlag(MF_UNREAD); - else if (params[j].equals_ci("RECEIPT")) - m->SetFlag(MF_RECEIPT); - } - m->text = params[params.size() - 1]; - nc->memos.memos.push_back(m); - } - else if (key.equals_ci("MIG")) - nc->memos.ignores.push_back(params[0].ci_str()); - } - catch (const ConvertException &ex) - { - throw DatabaseException(ex.GetReason()); - } - - return EVENT_CONTINUE; - } - - EventReturn OnDatabaseReadMetadata(NickAlias *na, const Anope::string &key, const std::vector ¶ms) - { - if (key.equals_ci("LAST_USERMASK")) - na->last_usermask = params[0]; - else if (key.equals_ci("LAST_REALNAME")) - na->last_realname = params[0]; - else if (key.equals_ci("LAST_QUIT")) - na->last_quit = params[0]; - else if (key.equals_ci("FLAGS")) - na->FromString(params); - else if (key.equals_ci("VHOST")) - na->hostinfo.SetVhost(params.size() > 3 ? params[3] : "", params[2], params[0], params[1].is_pos_number_only() ? convertTo(params[1]) : 0); - - return EVENT_CONTINUE; - } - - EventReturn OnDatabaseReadMetadata(BotInfo *bi, const Anope::string &key, const std::vector ¶ms) - { - if (key.equals_ci("FLAGS")) - bi->FromString(params); - - return EVENT_CONTINUE; - } - - EventReturn OnDatabaseReadMetadata(ChannelInfo *ci, const Anope::string &key, const std::vector ¶ms) - { - try - { - if (key.equals_ci("BANTYPE")) - ci->bantype = params[0].is_pos_number_only() ? convertTo(params[0]) : Config->CSDefBantype; - else if (key.equals_ci("MEMOMAX")) - ci->memos.memomax = params[0].is_pos_number_only() ? convertTo(params[0]) : -1; - else if (key.equals_ci("FOUNDER")) - ci->SetFounder(findcore(params[0])); - else if (key.equals_ci("SUCCESSOR")) - ci->successor = findcore(params[0]); - else if (key.equals_ci("LEVELS")) - { - for (unsigned j = 0, end = params.size(); j < end; j += 2) - for (int i = 0; ChannelLevels[i].Level != CA_SIZE; ++i) - if (params[j].equals_ci(ChannelLevels[i].Name)) - ci->levels[ChannelLevels[i].Level] = params[j + 1].is_number_only() ? convertTo(params[j + 1]) : 0; - } - else if (key.equals_ci("FLAGS")) - ci->FromString(params); - else if (key.equals_ci("DESC")) - ci->desc = params[0]; - else if (key.equals_ci("TOPIC")) - { - ci->last_topic_setter = params[0]; - ci->last_topic_time = params[1].is_pos_number_only() ? convertTo(params[1]) : 0; - ci->last_topic = params[2]; - } - else if (key.equals_ci("SUSPEND")) - { - ci->Extend("suspend_by", new ExtensibleItemRegular(params[0])); - ci->Extend("suspend_reason", new ExtensibleItemRegular(params[1])); - } - else if (key.equals_ci("ACCESS")) - { - service_reference provider(params[0]); - if (!provider) - throw DatabaseException("Access entry for nonexistant provider " + params[0]); - - ChanAccess *access = provider->Create(); - access->ci = ci; - access->mask = params[1]; - access->Unserialize(params[2]); - access->last_seen = params[3].is_pos_number_only() ? convertTo(params[3]) : 0; - access->creator = params[4]; - access->created = params.size() > 5 && params[5].is_pos_number_only() ? convertTo(params[5]) : Anope::CurTime; - - ci->AddAccess(access); - } - else if (key.equals_ci("AKICK")) - { - // 0 is the old stuck - bool Nick = params[1].equals_ci("NICK"); - NickCore *nc = NULL; - if (Nick) - { - nc = findcore(params[2]); - if (!nc) - throw DatabaseException("Akick for nonexistant core " + params[2] + " on " + ci->name); - } - - AutoKick *ak; - if (Nick) - ak = ci->AddAkick(params[3], nc, params.size() > 6 ? params[6] : "", params[4].is_pos_number_only() ? convertTo(params[4]) : 0, params[5].is_pos_number_only() ? convertTo(params[5]) : 0); - else - ak = ci->AddAkick(params[3], params[2], params.size() > 6 ? params[6] : "", params[4].is_pos_number_only() ? convertTo(params[4]) : 0, params[5].is_pos_number_only() ? convertTo(params[5]) : 0); - if (Nick) - ak->SetFlag(AK_ISNICK); - - } - else if (key.equals_ci("MLOCK")) - { - std::vector mlocks; - ci->GetExtRegular("db_mlock", mlocks); - - Anope::string mlock_string = params[0] + " " + params[1] + " " + params[2] + " " + params[3]; - if (params.size() > 4) - mlock_string += " " + params[4]; - - mlocks.push_back(mlock_string); - ci->Extend("db_mlock", new ExtensibleItemRegular >(mlocks)); - } - else if (key.equals_ci("MI")) - { - Memo *m = new Memo; - m->time = params[0].is_pos_number_only() ? convertTo(params[0]) : 0; - m->sender = params[1]; - for (unsigned j = 2; params[j].equals_ci("UNREAD") || params[j].equals_ci("RECEIPT"); ++j) - { - if (params[j].equals_ci("UNREAD")) - m->SetFlag(MF_UNREAD); - else if (params[j].equals_ci("RECEIPT")) - m->SetFlag(MF_RECEIPT); - } - m->text = params[params.size() - 1]; - ci->memos.memos.push_back(m); - } - else if (key.equals_ci("MIG")) - ci->memos.ignores.push_back(params[0].ci_str()); - else if (key.equals_ci("BI")) - { - if (params[0].equals_ci("NAME")) - ci->bi = findbot(params[1]); - else if (params[0].equals_ci("FLAGS")) - ci->botflags.FromString(params); - else if (params[0].equals_ci("TTB")) - { - for (unsigned j = 1, end = params.size(); j < end; j += 2) - { - if (params[j].equals_ci("BOLDS")) - ci->ttb[0] = params[j + 1].is_pos_number_only() ? convertTo(params[j + 1]) : 0; - else if (params[j].equals_ci("COLORS")) - ci->ttb[1] = params[j + 1].is_pos_number_only() ? convertTo(params[j + 1]) : 0; - else if (params[j].equals_ci("REVERSES")) - ci->ttb[2] = params[j + 1].is_pos_number_only() ? convertTo(params[j + 1]) : 0; - else if (params[j].equals_ci("UNDERLINES")) - ci->ttb[3] = params[j + 1].is_pos_number_only() ? convertTo(params[j + 1]) : 0; - else if (params[j].equals_ci("BADWORDS")) - ci->ttb[4] = params[j + 1].is_pos_number_only() ? convertTo(params[j + 1]) : 0; - else if (params[j].equals_ci("CAPS")) - ci->ttb[5] = params[j + 1].is_pos_number_only() ? convertTo(params[j + 1]) : 0; - else if (params[j].equals_ci("FLOOD")) - ci->ttb[6] = params[j + 1].is_pos_number_only() ? convertTo(params[j + 1]) : 0; - else if (params[j].equals_ci("REPEAT")) - ci->ttb[7] = params[j + 1].is_pos_number_only() ? convertTo(params[j + 1]) : 0; - else if (params[j].equals_ci("ITALICS")) - ci->ttb[8] = params[j + 1].is_pos_number_only() ? convertTo(params[j + 1]) : 0; - else if (params[j].equals_ci("AMSGS")) - ci->ttb[9] = params[j + 1].is_pos_number_only() ? convertTo(params[j + 1]) : 0; - } - } - else if (params[0].equals_ci("CAPSMIN")) - ci->capsmin = params[1].is_pos_number_only() ? convertTo(params[1]) : 0; - else if (params[0].equals_ci("CAPSPERCENT")) - ci->capspercent = params[1].is_pos_number_only() ? convertTo(params[1]) : 0; - else if (params[0].equals_ci("FLOODLINES")) - ci->floodlines = params[1].is_pos_number_only() ? convertTo(params[1]) : 0; - else if (params[0].equals_ci("FLOODSECS")) - ci->floodsecs = params[1].is_pos_number_only() ? convertTo(params[1]) : 0; - else if (params[0].equals_ci("REPEATTIMES")) - ci->repeattimes = params[1].is_pos_number_only() ? convertTo(params[1]) : 0; - else if (params[0].equals_ci("BADWORD")) - { - BadWordType Type; - if (params[1].equals_ci("SINGLE")) - Type = BW_SINGLE; - else if (params[1].equals_ci("START")) - Type = BW_START; - else if (params[1].equals_ci("END")) - Type = BW_END; - else - Type = BW_ANY; - ci->AddBadWord(params[2], Type); - } - } - } - catch (const ConvertException &ex) - { - throw DatabaseException(ex.GetReason()); - } - - return EVENT_CONTINUE; - } - - EventReturn OnSaveDatabase() - { - BackupDatabase(); - - db_buffer << "VER 2" << endl; - - for (nickcore_map::const_iterator nit = NickCoreList.begin(), nit_end = NickCoreList.end(); nit != nit_end; ++nit) - { - NickCore *nc = nit->second; - - db_buffer << "NC " << nc->display << " " << nc->pass << endl; - - db_buffer << "MD MEMOMAX " << nc->memos.memomax << endl; - - if (!nc->language.empty()) - db_buffer << "MD LANGUAGE " << nc->language << endl; - if (!nc->email.empty()) - db_buffer << "MD EMAIL " << nc->email << endl; - if (!nc->greet.empty()) - db_buffer << "MD GREET :" << nc->greet << endl; - - if (!nc->access.empty()) - { - for (std::vector::iterator it = nc->access.begin(), it_end = nc->access.end(); it != it_end; ++it) - db_buffer << "MD ACCESS " << *it << endl; - } - if (!nc->cert.empty()) - { - for (std::vector::iterator it = nc->cert.begin(), it_end = nc->cert.end(); it != it_end; ++it) - db_buffer << "MD CERT " << *it << endl; - } - if (nc->FlagCount()) - db_buffer << "MD FLAGS " << ToString(nc->ToString()) << endl; - MemoInfo *mi = &nc->memos; - for (unsigned k = 0, end = mi->memos.size(); k < end; ++k) - { - db_buffer << "MD MI " << mi->memos[k]->time << " " << mi->memos[k]->sender; - if (mi->memos[k]->HasFlag(MF_UNREAD)) - db_buffer << " UNREAD"; - if (mi->memos[k]->HasFlag(MF_RECEIPT)) - db_buffer << " RECEIPT"; - db_buffer << " :" << mi->memos[k]->text << endl; - } - for (unsigned k = 0, end = mi->ignores.size(); k < end; ++k) - db_buffer << "MD MIG " << Anope::string(mi->ignores[k]) << endl; - FOREACH_MOD(I_OnDatabaseWriteMetadata, OnDatabaseWriteMetadata(WriteMetadata, nc)); - } - - for (nickalias_map::const_iterator it = NickAliasList.begin(), it_end = NickAliasList.end(); it != it_end; ++it) - { - NickAlias *na = it->second; - - db_buffer << "NA " << na->nc->display << " " << na->nick << " " << na->time_registered << " " << na->last_seen << endl; - if (!na->last_usermask.empty()) - db_buffer << "MD LAST_USERMASK " << na->last_usermask << endl; - if (!na->last_realname.empty()) - db_buffer << "MD LAST_REALNAME :" << na->last_realname << endl; - if (!na->last_quit.empty()) - db_buffer << "MD LAST_QUIT :" << na->last_quit << endl; - if (na->FlagCount()) - db_buffer << "MD FLAGS " << ToString(na->ToString()) << endl; - if (na->hostinfo.HasVhost()) - db_buffer << "MD VHOST " << na->hostinfo.GetCreator() << " " << na->hostinfo.GetTime() << " " << na->hostinfo.GetHost() << " :" << na->hostinfo.GetIdent() << endl; - - FOREACH_MOD(I_OnDatabaseWriteMetadata, OnDatabaseWriteMetadata(WriteMetadata, na)); - } - - for (Anope::insensitive_map::const_iterator it = BotListByNick.begin(), it_end = BotListByNick.end(); it != it_end; ++it) - { - BotInfo *bi = it->second; - - if (bi->HasFlag(BI_CONF)) - continue; - - db_buffer << "BI " << bi->nick << " " << bi->GetIdent() << " " << bi->host << " " << bi->created << " " << bi->chancount << " :" << bi->realname << endl; - if (bi->FlagCount()) - db_buffer << "MD FLAGS " << ToString(bi->ToString()) << endl; - } - - for (registered_channel_map::const_iterator cit = RegisteredChannelList.begin(), cit_end = RegisteredChannelList.end(); cit != cit_end; ++cit) - { - ChannelInfo *ci = cit->second; - - db_buffer << "CH " << ci->name << " " << ci->time_registered << " " << ci->last_used << endl; - db_buffer << "MD BANTYPE " << ci->bantype << endl; - db_buffer << "MD MEMOMAX " << ci->memos.memomax << endl; - if (ci->GetFounder()) - db_buffer << "MD FOUNDER " << ci->GetFounder()->display << endl; - if (ci->successor) - db_buffer << "MD SUCCESSOR " << ci->successor->display << endl; - if (!ci->desc.empty()) - db_buffer << "MD DESC :" << ci->desc << endl; - if (!ci->last_topic.empty()) - db_buffer << "MD TOPIC " << ci->last_topic_setter << " " << ci->last_topic_time << " :" << ci->last_topic << endl; - db_buffer << "MD LEVELS"; - for (int j = 0; ChannelLevels[j].Level != CA_SIZE; ++j) - db_buffer << " " << ChannelLevels[j].Name << " " << ci->levels[ChannelLevels[j].Level]; - db_buffer << endl; - if (ci->FlagCount()) - db_buffer << "MD FLAGS " << ToString(ci->ToString()) << endl; - if (ci->HasFlag(CI_SUSPENDED)) - { - Anope::string by, reason; - ci->GetExtRegular("suspend_by", by); - ci->GetExtRegular("suspend_reason", reason); - db_buffer << "MD SUSPEND " << by << " :" << reason << endl; - } - for (unsigned k = 0, end = ci->GetAccessCount(); k < end; ++k) - { - ChanAccess *access = ci->GetAccess(k); - db_buffer << "MD ACCESS " << access->provider->name << " " << access->mask << " " << access->Serialize() << " " << access->last_seen << " " << access->creator << " " << access->created << endl; - } - for (unsigned k = 0, end = ci->GetAkickCount(); k < end; ++k) - { - db_buffer << "MD AKICK 0 " << (ci->GetAkick(k)->HasFlag(AK_ISNICK) ? "NICK " : "MASK ") << - (ci->GetAkick(k)->HasFlag(AK_ISNICK) ? ci->GetAkick(k)->nc->display : ci->GetAkick(k)->mask) << " " << ci->GetAkick(k)->creator << " " << ci->GetAkick(k)->addtime << " " << ci->last_used << " :"; - if (!ci->GetAkick(k)->reason.empty()) - db_buffer << ci->GetAkick(k)->reason; - db_buffer << endl; - } - { - std::vector mlocks; - if (ci->GetExtRegular("db_mlock", mlocks)) - for (unsigned i = 0; i < mlocks.size(); ++i) - db_buffer << mlocks[i] << endl; - else - { - for (std::multimap::const_iterator it = ci->GetMLock().begin(), it_end = ci->GetMLock().end(); it != it_end; ++it) - { - const ModeLock &ml = it->second; - ChannelMode *cm = ModeManager::FindChannelModeByName(ml.name); - if (cm != NULL) - db_buffer << "MD MLOCK " << (ml.set ? 1 : 0) << " " << cm->NameAsString() << " " << ml.setter << " " << ml.created << " " << ml.param << endl; - } - } - } - MemoInfo *memos = &ci->memos; - for (unsigned k = 0, end = memos->memos.size(); k < end; ++k) - { - db_buffer << "MD MI " << memos->memos[k]->time << " " << memos->memos[k]->sender; - if (memos->memos[k]->HasFlag(MF_UNREAD)) - db_buffer << " UNREAD"; - if (memos->memos[k]->HasFlag(MF_RECEIPT)) - db_buffer << " RECEIPT"; - db_buffer << " :" << memos->memos[k]->text << endl; - } - for (unsigned k = 0, end = memos->ignores.size(); k < end; ++k) - db_buffer << "MD MIG " << Anope::string(memos->ignores[k]) << endl; - if (ci->bi) - db_buffer << "MD BI NAME " << ci->bi->nick << endl; - if (ci->botflags.FlagCount()) - db_buffer << "MD BI FLAGS " << ToString(ci->botflags.ToString()) << endl; - db_buffer << "MD BI TTB BOLDS " << ci->ttb[0] << " COLORS " << ci->ttb[1] << " REVERSES " << ci->ttb[2] << " UNDERLINES " << ci->ttb[3] << " BADWORDS " << ci->ttb[4] << " CAPS " << ci->ttb[5] << " FLOOD " << ci->ttb[6] << " REPEAT " << ci->ttb[7] << " ITALICS " << ci->ttb[8] << " AMSGS " << ci->ttb[9] << endl; - if (ci->capsmin) - db_buffer << "MD BI CAPSMIN " << ci->capsmin << endl; - if (ci->capspercent) - db_buffer << "MD BI CAPSPERCENT " << ci->capspercent << endl; - if (ci->floodlines) - db_buffer << "MD BI FLOODLINES " << ci->floodlines << endl; - if (ci->floodsecs) - db_buffer << "MD BI FLOODSECS " << ci->floodsecs << endl; - if (ci->repeattimes) - db_buffer << "MD BI REPEATTIMES " << ci->repeattimes << endl; - for (unsigned k = 0, end = ci->GetBadWordCount(); k < end; ++k) - db_buffer << "MD BI BADWORD " << (ci->GetBadWord(k)->type == BW_ANY ? "ANY " : "") << (ci->GetBadWord(k)->type == BW_SINGLE ? "SINGLE " : "") << (ci->GetBadWord(k)->type == BW_START ? "START " : "") << - (ci->GetBadWord(k)->type == BW_END ? "END " : "") << ":" << ci->GetBadWord(k)->word << endl; - - FOREACH_MOD(I_OnDatabaseWriteMetadata, OnDatabaseWriteMetadata(WriteMetadata, ci)); - } - - db_buffer << "OS STATS " << maxusercnt << " " << maxusertime << endl; - - for (std::list::iterator it = XLineManager::XLineManagers.begin(), it_end = XLineManager::XLineManagers.end(); it != it_end; ++it) - { - XLineManager *xl = *it; - for (unsigned i = 0, end = xl->GetCount(); i < end; ++i) - { - XLine *x = xl->GetEntry(i); - db_buffer << "OS SXLINE " << xl->Type() << " " << x->GetUser() << " " << x->GetHost() << " " << x->By << " " << x->Created << " " << x->Expires << " :" << x->Reason << endl; - } - } - - if (SessionInterface) - for (SessionService::ExceptionVector::iterator it = SessionInterface->GetExceptions().begin(); it != SessionInterface->GetExceptions().end(); ++it) - { - Exception *e = *it; - db_buffer << "OS EXCEPTION " << e->mask << " " << e->limit << " " << e->who << " " << e->time << " " << e->expires << " " << e->reason << endl; - } - - FOREACH_MOD(I_OnDatabaseWrite, OnDatabaseWrite(Write)); - - std::fstream db; - db.open(DatabaseFile.c_str(), std::ios_base::out | std::ios_base::trunc); - - if (!db.is_open()) - { - ircdproto->SendGlobops(NULL, "Unable to open %s for writing!", DatabaseFile.c_str()); - return EVENT_CONTINUE; - } - - db << db_buffer.str(); - db_buffer.str(""); - - db.close(); - - return EVENT_CONTINUE; - } - - void OnModuleLoad(User *u, Module *m) - { - if (!u) - return; - - Implementation events[] = {I_OnDatabaseRead, I_OnDatabaseReadMetadata}; - for (int i = 0; i < 2; ++i) - { - std::vector::iterator it = std::find(ModuleManager::EventHandlers[events[i]].begin(), ModuleManager::EventHandlers[events[i]].end(), m); - /* This module wants to read from the database */ - if (it != ModuleManager::EventHandlers[events[i]].end()) - /* Loop over the database and call the events it would on a normal startup */ - ReadDatabase(m); - } - } -}; - -MODULE_INIT(DBPlain) diff --git a/modules/core/enc_md5.cpp b/modules/core/enc_md5.cpp deleted file mode 100644 index 109f31dfc..000000000 --- a/modules/core/enc_md5.cpp +++ /dev/null @@ -1,372 +0,0 @@ -/* Module for encryption using MD5. - * - * Modified for Anope. - * (C) 2003-2011 Anope Team - * Contact us at team@anope.org - * - * Taken from IRC Services and is copyright (c) 1996-2002 Andrew Church. - * E-mail: - * Parts written by Andrew Kempe and others. - * This program is free but copyrighted software; see the file COPYING for - * details. - */ - -#include "module.h" - -/*************************************************************************/ - -/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All -rights reserved. - -License to copy and use this software is granted provided that it -is identified as the "RSA Data Security, Inc. MD5 Message-Digest -Algorithm" in all material mentioning or referencing this software -or this function. - -License is also granted to make and use derivative works provided -that such works are identified as "derived from the RSA Data -Security, Inc. MD5 Message-Digest Algorithm" in all material -mentioning or referencing the derived work. - -RSA Data Security, Inc. makes no representations concerning either -the merchantability of this software or the suitability of this -software for any particular purpose. It is provided "as is" -without express or implied warranty of any kind. - -These notices must be retained in any copies of any part of this -documentation and/or software. - */ - -/* MD5 context. */ -struct MD5_CTX -{ - unsigned state[4]; /* state (ABCD) */ - unsigned count[2]; /* number of bits, modulo 2^64 (lsb first) */ - unsigned char buffer[64]; /* input buffer */ -}; - -/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm - */ - -/* Constants for MD5Transform routine. - */ -enum -{ - S11 = 7, - S12 = 12, - S13 = 17, - S14 = 22, - S21 = 5, - S22 = 9, - S23 = 14, - S24 = 20, - S31 = 4, - S32 = 11, - S33 = 16, - S34 = 23, - S41 = 6, - S42 = 10, - S43 = 15, - S44 = 21 -}; - -void MD5Transform(unsigned [4], const unsigned char [64]); -void Encode(unsigned char *, unsigned *, unsigned); -void Decode(unsigned *, const unsigned char *, unsigned); - -static unsigned char PADDING[64] = { - 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - -/* F, G, H and I are basic MD5 functions. - */ -inline static unsigned F(unsigned x, unsigned y, unsigned z) { return (x & y) | (~x & z); } -inline static unsigned G(unsigned x, unsigned y, unsigned z) { return (x & z) | (y & ~z); } -inline static unsigned H(unsigned x, unsigned y, unsigned z) { return x ^ y ^ z; } -inline static unsigned I(unsigned x, unsigned y, unsigned z) { return y ^ (x | ~z); } - -/* ROTATE_LEFT rotates x left n bits. - */ -inline static unsigned ROTATE_LEFT(unsigned x, unsigned n) { return (x << n) | (x >> (32 - n)); } - -/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. - * Rotation is separate from addition to prevent recomputation. - */ -inline static void FF(unsigned &a, unsigned b, unsigned c, unsigned d, unsigned x, unsigned s, unsigned ac) -{ - a += F(b, c, d) + x + ac; - a = ROTATE_LEFT(a, s); - a += b; -} -inline static void GG(unsigned &a, unsigned b, unsigned c, unsigned d, unsigned x, unsigned s, unsigned ac) -{ - a += G(b, c, d) + x + ac; - a = ROTATE_LEFT(a, s); - a += b; -} -inline static void HH(unsigned &a, unsigned b, unsigned c, unsigned d, unsigned x, unsigned s, unsigned ac) -{ - a += H(b, c, d) + x + ac; - a = ROTATE_LEFT(a, s); - a += b; -} -inline static void II(unsigned &a, unsigned b, unsigned c, unsigned d, unsigned x, unsigned s, unsigned ac) -{ - a += I(b, c, d) + x + ac; - a = ROTATE_LEFT(a, s); - a += b; -} - -/* MD5 initialization. Begins an MD5 operation, writing a new context. - */ -void MD5Init(MD5_CTX *context) -{ - context->count[0] = context->count[1] = 0; - /* Load magic initialization constants. */ - context->state[0] = 0x67452301; - context->state[1] = 0xefcdab89; - context->state[2] = 0x98badcfe; - context->state[3] = 0x10325476; -} - -/* MD5 block update operation. Continues an MD5 message-digest - * operation, processing another message block, and updating the - * context. - */ -void MD5Update(MD5_CTX *context, const unsigned char *input, unsigned inputLen) -{ - unsigned i, index, partLen; - - /* Compute number of bytes mod 64 */ - index = (context->count[0] >> 3) & 0x3F; - - /* Update number of bits */ - if ((context->count[0] += inputLen << 3) < (inputLen << 3)) - ++context->count[1]; - context->count[1] += inputLen >> 29; - - partLen = 64 - index; - - /* Transform as many times as possible. */ - if (inputLen >= partLen) - { - memcpy(&context->buffer[index], input, partLen); - MD5Transform(context->state, context->buffer); - - for (i = partLen; i + 63 < inputLen; i += 64) - MD5Transform(context->state, &input[i]); - - index = 0; - } - else - i = 0; - - /* Buffer remaining input */ - memcpy(&context->buffer[index], &input[i], inputLen - i); -} - -/* MD5 finalization. Ends an MD5 message-digest operation, writing the - * the message digest and zeroizing the context. - */ -void MD5Final(unsigned char digest[17], MD5_CTX *context) -{ - unsigned char bits[8]; - unsigned index, padLen; - - /* Save number of bits */ - Encode(bits, context->count, 8); - - /* Pad out to 56 mod 64. */ - index = (context->count[0] >> 3) & 0x3f; - padLen = index < 56 ? 56 - index : 120 - index; - MD5Update(context, PADDING, padLen); - - /* Append length (before padding) */ - MD5Update(context, bits, 8); - /* Store state in digest */ - Encode(digest, context->state, 16); - - /* Zeroize sensitive information. */ - memset(context, 0, sizeof(*context)); -} - -/* MD5 basic transformation. Transforms state based on block. - */ -void MD5Transform(unsigned state[4], const unsigned char block[64]) -{ - unsigned a = state[0], b = state[1], c = state[2], d = state[3], x[16]; - - Decode(x, block, 64); - - /* Round 1 */ - FF(a, b, c, d, x[0], S11, 0xd76aa478); /* 1 */ - FF(d, a, b, c, x[1], S12, 0xe8c7b756); /* 2 */ - FF(c, d, a, b, x[2], S13, 0x242070db); /* 3 */ - FF(b, c, d, a, x[3], S14, 0xc1bdceee); /* 4 */ - FF(a, b, c, d, x[4], S11, 0xf57c0faf); /* 5 */ - FF(d, a, b, c, x[5], S12, 0x4787c62a); /* 6 */ - FF(c, d, a, b, x[6], S13, 0xa8304613); /* 7 */ - FF(b, c, d, a, x[7], S14, 0xfd469501); /* 8 */ - FF(a, b, c, d, x[8], S11, 0x698098d8); /* 9 */ - FF(d, a, b, c, x[9], S12, 0x8b44f7af); /* 10 */ - FF(c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ - FF(b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ - FF(a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ - FF(d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ - FF(c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ - FF(b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ - - /* Round 2 */ - GG(a, b, c, d, x[1], S21, 0xf61e2562); /* 17 */ - GG(d, a, b, c, x[6], S22, 0xc040b340); /* 18 */ - GG(c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ - GG(b, c, d, a, x[0], S24, 0xe9b6c7aa); /* 20 */ - GG(a, b, c, d, x[5], S21, 0xd62f105d); /* 21 */ - GG(d, a, b, c, x[10], S22, 0x2441453); /* 22 */ - GG(c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ - GG(b, c, d, a, x[4], S24, 0xe7d3fbc8); /* 24 */ - GG(a, b, c, d, x[9], S21, 0x21e1cde6); /* 25 */ - GG(d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ - GG(c, d, a, b, x[3], S23, 0xf4d50d87); /* 27 */ - GG(b, c, d, a, x[8], S24, 0x455a14ed); /* 28 */ - GG(a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ - GG(d, a, b, c, x[2], S22, 0xfcefa3f8); /* 30 */ - GG(c, d, a, b, x[7], S23, 0x676f02d9); /* 31 */ - GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ - - /* Round 3 */ - HH(a, b, c, d, x[5], S31, 0xfffa3942); /* 33 */ - HH(d, a, b, c, x[8], S32, 0x8771f681); /* 34 */ - HH(c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ - HH(b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ - HH(a, b, c, d, x[1], S31, 0xa4beea44); /* 37 */ - HH(d, a, b, c, x[4], S32, 0x4bdecfa9); /* 38 */ - HH(c, d, a, b, x[7], S33, 0xf6bb4b60); /* 39 */ - HH(b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ - HH(a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ - HH(d, a, b, c, x[0], S32, 0xeaa127fa); /* 42 */ - HH(c, d, a, b, x[3], S33, 0xd4ef3085); /* 43 */ - HH(b, c, d, a, x[6], S34, 0x4881d05); /* 44 */ - HH(a, b, c, d, x[9], S31, 0xd9d4d039); /* 45 */ - HH(d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ - HH(c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ - HH(b, c, d, a, x[2], S34, 0xc4ac5665); /* 48 */ - - /* Round 4 */ - II(a, b, c, d, x[0], S41, 0xf4292244); /* 49 */ - II(d, a, b, c, x[7], S42, 0x432aff97); /* 50 */ - II(c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ - II(b, c, d, a, x[5], S44, 0xfc93a039); /* 52 */ - II(a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ - II(d, a, b, c, x[3], S42, 0x8f0ccc92); /* 54 */ - II(c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ - II(b, c, d, a, x[1], S44, 0x85845dd1); /* 56 */ - II(a, b, c, d, x[8], S41, 0x6fa87e4f); /* 57 */ - II(d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ - II(c, d, a, b, x[6], S43, 0xa3014314); /* 59 */ - II(b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ - II(a, b, c, d, x[4], S41, 0xf7537e82); /* 61 */ - II(d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ - II(c, d, a, b, x[2], S43, 0x2ad7d2bb); /* 63 */ - II(b, c, d, a, x[9], S44, 0xeb86d391); /* 64 */ - - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - - /* Zeroize sensitive information. */ - memset(x, 0, sizeof(x)); -} - -/* Encodes input (unsigned) into output (unsigned char). Assumes len is - * a multiple of 4. - */ -void Encode(unsigned char *output, unsigned *input, unsigned len) -{ - unsigned i, j; - - for (i = 0, j = 0; j < len; ++i, j += 4) - { - output[j] = static_cast(input[i] & 0xff); - output[j + 1] = static_cast((input[i] >> 8) & 0xff); - output[j + 2] = static_cast((input[i] >> 16) & 0xff); - output[j + 3] = static_cast((input[i] >> 24) & 0xff); - } -} - -/* Decodes input (unsigned char) into output (unsigned). Assumes len is - * a multiple of 4. - */ -void Decode(unsigned *output, const unsigned char *input, unsigned len) -{ - unsigned i, j; - - for (i = 0, j = 0; j < len; ++i, j += 4) - output[i] = static_cast(input[j]) | (static_cast(input[j + 1]) << 8) | (static_cast(input[j + 2]) << 16) | (static_cast(input[j + 3]) << 24); -} - -/*************************************************************************/ - -/* Module stuff. */ - -class EMD5 : public Module -{ - public: - EMD5(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, ENCRYPTION) - { - this->SetAuthor("Anope"); - - Implementation i[] = { I_OnEncrypt, I_OnCheckAuthentication }; - ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); - } - - EventReturn OnEncrypt(const Anope::string &src, Anope::string &dest) - { - MD5_CTX context; - char digest[17] = ""; - Anope::string buf = "md5:"; - - MD5Init(&context); - MD5Update(&context, reinterpret_cast(src.c_str()), src.length()); - MD5Final(reinterpret_cast(digest), &context); - - buf += Anope::Hex(digest, 16); - Log(LOG_DEBUG_2) << "(enc_md5) hashed password from [" << src << "] to [" << buf << "]"; - dest = buf; - return EVENT_ALLOW; - } - - EventReturn OnCheckAuthentication(Command *c, CommandSource *source, const std::vector ¶ms, const Anope::string &account, const Anope::string &password) - { - NickAlias *na = findnick(account); - NickCore *nc = na ? na->nc : NULL; - if (na == NULL) - return EVENT_CONTINUE; - - size_t pos = nc->pass.find(':'); - if (pos == Anope::string::npos) - return EVENT_CONTINUE; - Anope::string hash_method(nc->pass.begin(), nc->pass.begin() + pos); - if (!hash_method.equals_cs("md5")) - return EVENT_CONTINUE; - - Anope::string buf; - this->OnEncrypt(password, 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) - enc_encrypt(password, nc->pass); - return EVENT_ALLOW; - } - - return EVENT_CONTINUE; - } -}; - -MODULE_INIT(EMD5) diff --git a/modules/core/enc_none.cpp b/modules/core/enc_none.cpp deleted file mode 100644 index 180b431c2..000000000 --- a/modules/core/enc_none.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/* Module for plain text encryption. - * - * (C) 2003-2011 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) - { - this->SetAuthor("Anope"); - - Implementation i[] = { I_OnEncrypt, I_OnDecrypt, I_OnCheckAuthentication }; - ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); - } - - EventReturn OnEncrypt(const Anope::string &src, Anope::string &dest) - { - 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) - { - 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; - } - - EventReturn OnCheckAuthentication(Command *c, CommandSource *source, const std::vector ¶ms, const Anope::string &account, const Anope::string &password) - { - NickAlias *na = findnick(account); - NickCore *nc = na ? na->nc : NULL; - if (na == NULL) - return EVENT_CONTINUE; - - size_t pos = nc->pass.find(':'); - if (pos == Anope::string::npos) - return EVENT_CONTINUE; - Anope::string hash_method(nc->pass.begin(), nc->pass.begin() + pos); - if (!hash_method.equals_cs("plain")) - return EVENT_CONTINUE; - - Anope::string buf; - this->OnEncrypt(password, 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) - enc_encrypt(password, nc->pass); - return EVENT_ALLOW; - } - - return EVENT_CONTINUE; - } -}; - -MODULE_INIT(ENone) diff --git a/modules/core/enc_old.cpp b/modules/core/enc_old.cpp deleted file mode 100644 index e68887c49..000000000 --- a/modules/core/enc_old.cpp +++ /dev/null @@ -1,382 +0,0 @@ -/* Include file for high-level encryption routines. - * - * (C) 2003-2011 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" - -/******** Code specific to the type of encryption. ********/ - -/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All -rights reserved. - -License to copy and use this software is granted provided that it -is identified as the "RSA Data Security, Inc. MD5 Message-Digest -Algorithm" in all material mentioning or referencing this software -or this function. - -License is also granted to make and use derivative works provided -that such works are identified as "derived from the RSA Data -Security, Inc. MD5 Message-Digest Algorithm" in all material -mentioning or referencing the derived work. - -RSA Data Security, Inc. makes no representations concerning either -the merchantability of this software or the suitability of this -software for any particular purpose. It is provided "as is" -without express or implied warranty of any kind. - -These notices must be retained in any copies of any part of this -documentation and/or software. - */ - -/* MD5 context. */ -struct MD5_CTX -{ - unsigned state[4]; /* state (ABCD) */ - unsigned count[2]; /* number of bits, modulo 2^64 (lsb first) */ - unsigned char buffer[64]; /* input buffer */ -}; - -/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm - */ - -/* Constants for MD5Transform routine. - */ -enum -{ - S11 = 7, - S12 = 12, - S13 = 17, - S14 = 22, - S21 = 5, - S22 = 9, - S23 = 14, - S24 = 20, - S31 = 4, - S32 = 11, - S33 = 16, - S34 = 23, - S41 = 6, - S42 = 10, - S43 = 15, - S44 = 21 -}; - -static void MD5Transform(unsigned [4], const unsigned char[64]); -static void Encode(unsigned char *, unsigned *, unsigned); -static void Decode(unsigned *, const unsigned char *, unsigned); - -static unsigned char PADDING[64] = { - 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - -/* F, G, H and I are basic MD5 functions. - */ -inline static unsigned F(unsigned x, unsigned y, unsigned z) { return (x & y) | (~x & z); } -inline static unsigned G(unsigned x, unsigned y, unsigned z) { return (x & z) | (y & ~z); } -inline static unsigned H(unsigned x, unsigned y, unsigned z) { return x ^ y ^ z; } -inline static unsigned MD5_I(unsigned x, unsigned y, unsigned z) { return y ^ (x | ~z); } - -/* ROTATE_LEFT rotates x left n bits. - */ -inline static unsigned ROTATE_LEFT(unsigned x, unsigned n) { return (x << n) | (x >> (32 - n)); } - -/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. - * Rotation is separate from addition to prevent recomputation. - */ -inline static void FF(unsigned &a, unsigned b, unsigned c, unsigned d, unsigned x, unsigned s, unsigned ac) -{ - a += F(b, c, d) + x + ac; - a = ROTATE_LEFT(a, s); - a += b; -} -inline static void GG(unsigned &a, unsigned b, unsigned c, unsigned d, unsigned x, unsigned s, unsigned ac) -{ - a += G(b, c, d) + x + ac; - a = ROTATE_LEFT(a, s); - a += b; -} -inline static void HH(unsigned &a, unsigned b, unsigned c, unsigned d, unsigned x, unsigned s, unsigned ac) -{ - a += H(b, c, d) + x + ac; - a = ROTATE_LEFT(a, s); - a += b; -} -inline static void II(unsigned &a, unsigned b, unsigned c, unsigned d, unsigned x, unsigned s, unsigned ac) -{ - a += MD5_I(b, c, d) + x + ac; - a = ROTATE_LEFT(a, s); - a += b; -} - -/* MD5 initialization. Begins an MD5 operation, writing a new context. - */ -static void MD5Init(MD5_CTX *context) -{ - context->count[0] = context->count[1] = 0; - /* Load magic initialization constants. - */ - context->state[0] = 0x67452301; - context->state[1] = 0xefcdab89; - context->state[2] = 0x98badcfe; - context->state[3] = 0x10325476; -} - -/* MD5 block update operation. Continues an MD5 message-digest - * operation, processing another message block, and updating the - * context. - */ -static void MD5Update(MD5_CTX *context, const unsigned char *input, unsigned inputLen) -{ - unsigned i, index, partLen; - - /* Compute number of bytes mod 64 */ - index = (context->count[0] >> 3) & 0x3F; - - /* Update number of bits */ - if ((context->count[0] += inputLen << 3) < (inputLen << 3)) - ++context->count[1]; - context->count[1] += inputLen >> 29; - - partLen = 64 - index; - - /* Transform as many times as possible. - */ - if (inputLen >= partLen) - { - memcpy(&context->buffer[index], input, partLen); - MD5Transform(context->state, context->buffer); - - for (i = partLen; i + 63 < inputLen; i += 64) - MD5Transform(context->state, &input[i]); - - index = 0; - } - else - i = 0; - - /* Buffer remaining input */ - memcpy(&context->buffer[index], &input[i], inputLen - i); -} - -/* MD5 finalization. Ends an MD5 message-digest operation, writing the - * the message digest and zeroizing the context. - */ -static void MD5Final(unsigned char digest[17], MD5_CTX *context) -{ - unsigned char bits[8]; - unsigned index, padLen; - - /* Save number of bits */ - Encode(bits, context->count, 8); - - /* Pad out to 56 mod 64. - */ - index = (context->count[0] >> 3) & 0x3f; - padLen = index < 56 ? 56 - index : 120 - index; - MD5Update(context, PADDING, padLen); - - /* Append length (before padding) */ - MD5Update(context, bits, 8); - /* Store state in digest */ - Encode(digest, context->state, 16); - - /* Zeroize sensitive information. - */ - memset(context, 0, sizeof(*context)); -} - -/* MD5 basic transformation. Transforms state based on block. - */ -static void MD5Transform(unsigned state[4], const unsigned char block[64]) -{ - unsigned a = state[0], b = state[1], c = state[2], d = state[3], x[16]; - - Decode(x, block, 64); - - /* Round 1 */ - FF(a, b, c, d, x[0], S11, 0xd76aa478); /* 1 */ - FF(d, a, b, c, x[1], S12, 0xe8c7b756); /* 2 */ - FF(c, d, a, b, x[2], S13, 0x242070db); /* 3 */ - FF(b, c, d, a, x[3], S14, 0xc1bdceee); /* 4 */ - FF(a, b, c, d, x[4], S11, 0xf57c0faf); /* 5 */ - FF(d, a, b, c, x[5], S12, 0x4787c62a); /* 6 */ - FF(c, d, a, b, x[6], S13, 0xa8304613); /* 7 */ - FF(b, c, d, a, x[7], S14, 0xfd469501); /* 8 */ - FF(a, b, c, d, x[8], S11, 0x698098d8); /* 9 */ - FF(d, a, b, c, x[9], S12, 0x8b44f7af); /* 10 */ - FF(c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ - FF(b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ - FF(a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ - FF(d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ - FF(c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ - FF(b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ - - /* Round 2 */ - GG(a, b, c, d, x[1], S21, 0xf61e2562); /* 17 */ - GG(d, a, b, c, x[6], S22, 0xc040b340); /* 18 */ - GG(c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ - GG(b, c, d, a, x[0], S24, 0xe9b6c7aa); /* 20 */ - GG(a, b, c, d, x[5], S21, 0xd62f105d); /* 21 */ - GG(d, a, b, c, x[10], S22, 0x2441453); /* 22 */ - GG(c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ - GG(b, c, d, a, x[4], S24, 0xe7d3fbc8); /* 24 */ - GG(a, b, c, d, x[9], S21, 0x21e1cde6); /* 25 */ - GG(d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ - GG(c, d, a, b, x[3], S23, 0xf4d50d87); /* 27 */ - GG(b, c, d, a, x[8], S24, 0x455a14ed); /* 28 */ - GG(a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ - GG(d, a, b, c, x[2], S22, 0xfcefa3f8); /* 30 */ - GG(c, d, a, b, x[7], S23, 0x676f02d9); /* 31 */ - GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ - - /* Round 3 */ - HH(a, b, c, d, x[5], S31, 0xfffa3942); /* 33 */ - HH(d, a, b, c, x[8], S32, 0x8771f681); /* 34 */ - HH(c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ - HH(b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ - HH(a, b, c, d, x[1], S31, 0xa4beea44); /* 37 */ - HH(d, a, b, c, x[4], S32, 0x4bdecfa9); /* 38 */ - HH(c, d, a, b, x[7], S33, 0xf6bb4b60); /* 39 */ - HH(b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ - HH(a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ - HH(d, a, b, c, x[0], S32, 0xeaa127fa); /* 42 */ - HH(c, d, a, b, x[3], S33, 0xd4ef3085); /* 43 */ - HH(b, c, d, a, x[6], S34, 0x4881d05); /* 44 */ - HH(a, b, c, d, x[9], S31, 0xd9d4d039); /* 45 */ - HH(d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ - HH(c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ - HH(b, c, d, a, x[2], S34, 0xc4ac5665); /* 48 */ - - /* Round 4 */ - II(a, b, c, d, x[0], S41, 0xf4292244); /* 49 */ - II(d, a, b, c, x[7], S42, 0x432aff97); /* 50 */ - II(c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ - II(b, c, d, a, x[5], S44, 0xfc93a039); /* 52 */ - II(a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ - II(d, a, b, c, x[3], S42, 0x8f0ccc92); /* 54 */ - II(c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ - II(b, c, d, a, x[1], S44, 0x85845dd1); /* 56 */ - II(a, b, c, d, x[8], S41, 0x6fa87e4f); /* 57 */ - II(d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ - II(c, d, a, b, x[6], S43, 0xa3014314); /* 59 */ - II(b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ - II(a, b, c, d, x[4], S41, 0xf7537e82); /* 61 */ - II(d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ - II(c, d, a, b, x[2], S43, 0x2ad7d2bb); /* 63 */ - II(b, c, d, a, x[9], S44, 0xeb86d391); /* 64 */ - - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - - /* Zeroize sensitive information. - */ - memset(x, 0, sizeof(x)); -} - -/* Encodes input (unsigned) into output (unsigned char). Assumes len is - * a multiple of 4. - */ -static void Encode(unsigned char *output, unsigned *input, unsigned len) -{ - unsigned i, j; - - for (i = 0, j = 0; j < len; ++i, j += 4) - { - output[j] = static_cast(input[i] & 0xff); - output[j + 1] = static_cast((input[i] >> 8) & 0xff); - output[j + 2] = static_cast((input[i] >> 16) & 0xff); - output[j + 3] = static_cast((input[i] >> 24) & 0xff); - } -} - -/* Decodes input (unsigned char) into output (unsigned). Assumes len is - * a multiple of 4. - */ -static void Decode(unsigned *output, const unsigned char *input, unsigned len) -{ - unsigned i, j; - - for (i = 0, j = 0; j < len; ++i, j += 4) - output[i] = static_cast(input[j]) | (static_cast(input[j + 1]) << 8) | (static_cast(input[j + 2]) << 16) | (static_cast(input[j + 3]) << 24); -} - -/*************************************************************************/ - -/******** Our own high-level routines. ********/ - -inline static char XTOI(char c) { return c > 9 ? c - 'A' + 10 : c - '0'; } - -class EOld : public Module -{ - public: - EOld(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, ENCRYPTION) - { - this->SetAuthor("Anope"); - - Implementation i[] = { I_OnEncrypt, I_OnCheckAuthentication }; - ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); - } - - EventReturn OnEncrypt(const Anope::string &src, Anope::string &dest) - { - MD5_CTX context; - char digest[33] = "", digest2[17] = ""; - int i; - Anope::string buf = "oldmd5:"; - - memset(&context, 0, sizeof(context)); - - MD5Init(&context); - MD5Update(&context, reinterpret_cast(src.c_str()), src.length()); - MD5Final(reinterpret_cast(digest), &context); - for (i = 0; i < 32; i += 2) - digest2[i / 2] = XTOI(digest[i]) << 4 | XTOI(digest[i + 1]); - - buf += Anope::Hex(digest2, 16); - Log(LOG_DEBUG_2) << "(enc_old) hashed password from [" << src << "] to [" << buf << "]"; - dest = buf; - return EVENT_ALLOW; - } - - EventReturn OnCheckAuthentication(Command *c, CommandSource *source, const std::vector ¶ms, const Anope::string &account, const Anope::string &password) - { - NickAlias *na = findnick(account); - NickCore *nc = na ? na->nc : NULL; - if (na == NULL) - return EVENT_CONTINUE; - - size_t pos = nc->pass.find(':'); - if (pos == Anope::string::npos) - return EVENT_CONTINUE; - Anope::string hash_method(nc->pass.begin(), nc->pass.begin() + pos); - if (!hash_method.equals_cs("oldmd5")) - return EVENT_CONTINUE; - - Anope::string buf; - this->OnEncrypt(password, 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) - enc_encrypt(password, nc->pass); - return EVENT_ALLOW; - } - - return EVENT_CONTINUE; - } -}; - -MODULE_INIT(EOld) diff --git a/modules/core/enc_sha1.cpp b/modules/core/enc_sha1.cpp deleted file mode 100644 index 4fe321bdc..000000000 --- a/modules/core/enc_sha1.cpp +++ /dev/null @@ -1,222 +0,0 @@ -/* -SHA-1 in C -By Steve Reid -100% Public Domain - -Test Vectors (from FIPS PUB 180-1) -"abc" - A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D -"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" - 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 -A million repetitions of "a" - 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F -*/ - -/* #define LITTLE_ENDIAN * This should be #define'd if true. */ - -#include "module.h" - -struct SHA1_CTX -{ - uint32 state[5]; - uint32 count[2]; - unsigned char buffer[64]; -}; - -void SHA1Transform(uint32 state[5], const unsigned char buffer[64]); -void SHA1Init(SHA1_CTX *context); -void SHA1Update(SHA1_CTX *context, const unsigned char *data, uint32 len); -void SHA1Final(unsigned char digest[20], SHA1_CTX *context); - -inline static uint32 rol(uint32 value, uint32 bits) { return (value << bits) | (value >> (32 - bits)); } - -union CHAR64LONG16 -{ - unsigned char c[64]; - uint32 l[16]; -}; - -/* blk0() and blk() perform the initial expand. */ -/* I got the idea of expanding during the round function from SSLeay */ -inline static uint32 blk0(CHAR64LONG16 *block, uint32 i) -{ -#ifdef LITTLE_ENDIAN - return block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) | (rol(block->l[i], 8) & 0x00FF00FF); -#else - return block->l[i]; -#endif -} -inline static uint32 blk(CHAR64LONG16 *block, uint32 i) { return block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ block->l[(i + 8) & 15] ^ block->l[(i + 2) & 15] ^ block->l[i & 15],1); } - -/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ -inline static void R0(CHAR64LONG16 *block, uint32 v, uint32 &w, uint32 x, uint32 y, uint32 &z, uint32 i) { z += ((w & (x ^ y)) ^ y) + blk0(block, i) + 0x5A827999 + rol(v, 5); w = rol(w, 30); } -inline static void R1(CHAR64LONG16 *block, uint32 v, uint32 &w, uint32 x, uint32 y, uint32 &z, uint32 i) { z += ((w & (x ^ y)) ^ y) + blk(block, i) + 0x5A827999 + rol(v, 5); w = rol(w, 30); } -inline static void R2(CHAR64LONG16 *block, uint32 v, uint32 &w, uint32 x, uint32 y, uint32 &z, uint32 i) { z += (w ^ x ^ y) + blk(block, i) + 0x6ED9EBA1 + rol(v, 5); w = rol(w, 30); } -inline static void R3(CHAR64LONG16 *block, uint32 v, uint32 &w, uint32 x, uint32 y, uint32 &z, uint32 i) { z += (((w | x) & y) | (w & x)) + blk(block, i) + 0x8F1BBCDC + rol(v, 5); w = rol(w, 30); } -inline static void R4(CHAR64LONG16 *block, uint32 v, uint32 &w, uint32 x, uint32 y, uint32 &z, uint32 i) { z += (w ^ x ^ y) + blk(block, i) + 0xCA62C1D6 + rol(v, 5); w = rol(w, 30); } - -/* Hash a single 512-bit block. This is the core of the algorithm. */ - -void SHA1Transform(uint32 state[5], const unsigned char buffer[64]) -{ - uint32 a, b, c, d, e; - static unsigned char workspace[64]; - CHAR64LONG16 *block = reinterpret_cast(workspace); - memcpy(block, buffer, 64); - /* Copy context->state[] to working vars */ - a = state[0]; - b = state[1]; - c = state[2]; - d = state[3]; - e = state[4]; - /* 4 rounds of 20 operations each. Loop unrolled. */ - R0(block, a, b, c, d, e, 0); R0(block, e, a, b, c, d, 1); R0(block, d, e, a, b, c, 2); R0(block, c, d, e, a, b, 3); - R0(block, b, c, d, e, a, 4); R0(block, a, b, c, d, e, 5); R0(block, e, a, b, c, d, 6); R0(block, d, e, a, b, c, 7); - R0(block, c, d, e, a, b, 8); R0(block, b, c, d, e, a, 9); R0(block, a, b, c, d, e, 10); R0(block, e, a, b, c, d, 11); - R0(block, d, e, a, b, c, 12); R0(block, c, d, e, a, b, 13); R0(block, b, c, d, e, a, 14); R0(block, a, b, c, d, e, 15); - R1(block, e, a, b, c, d, 16); R1(block, d, e, a, b, c, 17); R1(block, c, d, e, a, b, 18); R1(block, b, c, d, e, a, 19); - R2(block, a, b, c, d, e, 20); R2(block, e, a, b, c, d, 21); R2(block, d, e, a, b, c, 22); R2(block, c, d, e, a, b, 23); - R2(block, b, c, d, e, a, 24); R2(block, a, b, c, d, e, 25); R2(block, e, a, b, c, d, 26); R2(block, d, e, a, b, c, 27); - R2(block, c, d, e, a, b, 28); R2(block, b, c, d, e, a, 29); R2(block, a, b, c, d, e, 30); R2(block, e, a, b, c, d, 31); - R2(block, d, e, a, b, c, 32); R2(block, c, d, e, a, b, 33); R2(block, b, c, d, e, a, 34); R2(block, a, b, c, d, e, 35); - R2(block, e, a, b, c, d, 36); R2(block, d, e, a, b, c, 37); R2(block, c, d, e, a, b, 38); R2(block, b, c, d, e, a, 39); - R3(block, a, b, c, d, e, 40); R3(block, e, a, b, c, d, 41); R3(block, d, e, a, b, c, 42); R3(block, c, d, e, a, b, 43); - R3(block, b, c, d, e, a, 44); R3(block, a, b, c, d, e, 45); R3(block, e, a, b, c, d, 46); R3(block, d, e, a, b, c, 47); - R3(block, c, d, e, a, b, 48); R3(block, b, c, d, e, a, 49); R3(block, a, b, c, d, e, 50); R3(block, e, a, b, c, d, 51); - R3(block, d, e, a, b, c, 52); R3(block, c, d, e, a, b, 53); R3(block, b, c, d, e, a, 54); R3(block, a, b, c, d, e, 55); - R3(block, e, a, b, c, d, 56); R3(block, d, e, a, b, c, 57); R3(block, c, d, e, a, b, 58); R3(block, b, c, d, e, a, 59); - R4(block, a, b, c, d, e, 60); R4(block, e, a, b, c, d, 61); R4(block, d, e, a, b, c, 62); R4(block, c, d, e, a, b, 63); - R4(block, b, c, d, e, a, 64); R4(block, a, b, c, d, e, 65); R4(block, e, a, b, c, d, 66); R4(block, d, e, a, b, c, 67); - R4(block, c, d, e, a, b, 68); R4(block, b, c, d, e, a, 69); R4(block, a, b, c, d, e, 70); R4(block, e, a, b, c, d, 71); - R4(block, d, e, a, b, c, 72); R4(block, c, d, e, a, b, 73); R4(block, b, c, d, e, a, 74); R4(block, a, b, c, d, e, 75); - R4(block, e, a, b, c, d, 76); R4(block, d, e, a, b, c, 77); R4(block, c, d, e, a, b, 78); R4(block, b, c, d, e, a, 79); - /* Add the working vars back into context.state[] */ - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - state[4] += e; - /* Wipe variables */ - a = b = c = d = e = 0; -} - -/* SHA1Init - Initialize new context */ - -void SHA1Init(SHA1_CTX *context) -{ - /* SHA1 initialization constants */ - context->state[0] = 0x67452301; - context->state[1] = 0xEFCDAB89; - context->state[2] = 0x98BADCFE; - context->state[3] = 0x10325476; - context->state[4] = 0xC3D2E1F0; - context->count[0] = context->count[1] = 0; -} - -/* Run your data through this. */ - -void SHA1Update(SHA1_CTX *context, const unsigned char *data, uint32 len) -{ - uint32 i, j; - - j = (context->count[0] >> 3) & 63; - if ((context->count[0] += len << 3) < (len << 3)) - ++context->count[1]; - context->count[1] += len >> 29; - if (j + len > 63) - { - memcpy(&context->buffer[j], data, (i = 64 - j)); - SHA1Transform(context->state, context->buffer); - for (; i + 63 < len; i += 64) - SHA1Transform(context->state, &data[i]); - j = 0; - } - else - i = 0; - memcpy(&context->buffer[j], &data[i], len - i); -} - -/* Add padding and return the message digest. */ - -void SHA1Final(unsigned char digest[21], SHA1_CTX *context) -{ - uint32 i; - unsigned char finalcount[8]; - - for (i = 0; i < 8; ++i) - finalcount[i] = static_cast((context->count[i >= 4 ? 0 : 1] >> ((3 - (i & 3)) * 8)) & 255); /* Endian independent */ - SHA1Update(context, reinterpret_cast("\200"), 1); - while ((context->count[0] & 504) != 448) - SHA1Update(context, reinterpret_cast("\0"), 1); - SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ - for (i = 0; i < 20; ++i) - digest[i] = static_cast((context->state[i>>2] >> ((3 - (i & 3)) * 8)) & 255); - /* Wipe variables */ - i = 0; - memset(context->buffer, 0, 64); - memset(context->state, 0, 20); - memset(context->count, 0, 8); - memset(&finalcount, 0, 8); - SHA1Transform(context->state, context->buffer); -} - -/*****************************************************************************/ -/*****************************************************************************/ - -/* Module stuff. */ - -class ESHA1 : public Module -{ - public: - ESHA1(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, ENCRYPTION) - { - this->SetAuthor("Anope"); - - Implementation i[] = { I_OnEncrypt, I_OnCheckAuthentication }; - ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); - } - - EventReturn OnEncrypt(const Anope::string &src, Anope::string &dest) - { - SHA1_CTX context; - char digest[21] = ""; - Anope::string buf = "sha1:"; - - SHA1Init(&context); - SHA1Update(&context, reinterpret_cast(src.c_str()), src.length()); - SHA1Final(reinterpret_cast(digest), &context); - - buf += Anope::Hex(digest, 20); - Log(LOG_DEBUG_2) << "(enc_sha1) hashed password from [" << src << "] to [" << buf << "]"; - dest = buf; - return EVENT_ALLOW; - } - - EventReturn OnCheckAuthentication(Command *c, CommandSource *source, const std::vector ¶ms, const Anope::string &account, const Anope::string &password) - { - NickAlias *na = findnick(account); - NickCore *nc = na ? na->nc : NULL; - if (na == NULL) - return EVENT_CONTINUE; - - size_t pos = nc->pass.find(':'); - if (pos == Anope::string::npos) - return EVENT_CONTINUE; - Anope::string hash_method(nc->pass.begin(), nc->pass.begin() + pos); - if (!hash_method.equals_cs("sha1")) - return EVENT_CONTINUE; - - Anope::string buf; - this->OnEncrypt(password, buf); - if (nc->pass.equals_cs(buf)) - { - if (ModuleManager::FindFirstOf(ENCRYPTION) != this) - enc_encrypt(password, nc->pass); - return EVENT_ALLOW; - } - - return EVENT_CONTINUE; - } -}; - -MODULE_INIT(ESHA1) diff --git a/modules/core/enc_sha256.cpp b/modules/core/enc_sha256.cpp deleted file mode 100644 index 3034f3731..000000000 --- a/modules/core/enc_sha256.cpp +++ /dev/null @@ -1,314 +0,0 @@ -/* This module generates and compares password hashes using SHA256 algorithms. - * To help reduce the risk of dictionary attacks, the code appends random bytes - * (so-called "salt") to the original plain text before generating hashes and - * stores this salt appended to the result. To verify another plain text value - * against the given hash, this module will retrieve the salt value from the - * password string and use it when computing a new hash of the plain text. - * - * 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. - * - * Modified for Anope. - * (C) 2003-2011 Anope Team - * Contact us at team@anope.org - * - * Taken from InspIRCd ( www.inspircd.org ) - * see http://wiki.inspircd.org/Credits - * - * This program is free but copyrighted software; see - * the file COPYING for details. - */ - -/* FIPS 180-2 SHA-224/256/384/512 implementation - * Last update: 05/23/2005 - * Issue date: 04/30/2005 - * - * Copyright (C) 2005 Olivier Gay - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the project nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include "module.h" - -static const unsigned SHA256_DIGEST_SIZE = 256 / 8; -static const unsigned SHA256_BLOCK_SIZE = 512 / 8; - -/** An sha256 context - */ -class SHA256Context -{ - public: - unsigned tot_len; - unsigned len; - unsigned char block[2 * SHA256_BLOCK_SIZE]; - uint32 h[8]; -}; - -inline static uint32 SHFR(uint32 x, uint32 n) { return x >> n; } -inline static uint32 ROTR(uint32 x, uint32 n) { return (x >> n) | (x << ((sizeof(x) << 3) - n)); } -inline static uint32 ROTL(uint32 x, uint32 n) { return (x << n) | (x >> ((sizeof(x) << 3) - n)); } -inline static uint32 CH(uint32 x, uint32 y, uint32 z) { return (x & y) ^ (~x & z); } -inline static uint32 MAJ(uint32 x, uint32 y, uint32 z) { return (x & y) ^ (x & z) ^ (y & z); } - -inline static uint32 SHA256_F1(uint32 x) { return ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22); } -inline static uint32 SHA256_F2(uint32 x) { return ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25); } -inline static uint32 SHA256_F3(uint32 x) { return ROTR(x, 7) ^ ROTR(x, 18) ^ SHFR(x, 3); } -inline static uint32 SHA256_F4(uint32 x) { return ROTR(x, 17) ^ ROTR(x, 19) ^ SHFR(x, 10); } - -inline static void UNPACK32(unsigned x, unsigned char *str) -{ - str[3] = static_cast(x); - str[2] = static_cast(x >> 8); - str[1] = static_cast(x >> 16); - str[0] = static_cast(x >> 24); -} - -inline static void PACK32(unsigned char *str, uint32 &x) -{ - x = static_cast(str[3]) | static_cast(str[2]) << 8 | static_cast(str[1]) << 16 | static_cast(str[0]) << 24; -} - -/* Macros used for loops unrolling */ - -inline static void SHA256_SCR(uint32 w[64], int i) -{ - w[i] = SHA256_F4(w[i - 2]) + w[i - 7] + SHA256_F3(w[i - 15]) + w[i - 16]; -} - -uint32 sha256_k[64] = -{ - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, - 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, - 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, - 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, - 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, - 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, - 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, - 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, - 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, - 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, - 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, - 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, - 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, - 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 -}; - -class ESHA256 : public Module -{ - unsigned iv[8]; - bool use_iv; - - /* initializes the IV with a new random value */ - void NewRandomIV() - { - for (int i = 0; i < 8; ++i) - iv[i] = getrandom32(); - } - - /* returns the IV as base64-encrypted string */ - Anope::string GetIVString() - { - char buf[33]; - for (int i = 0; i < 8; ++i) - UNPACK32(iv[i], reinterpret_cast(&buf[i << 2])); - buf[32] = '\0'; - return Anope::Hex(buf, 32); - } - - /* splits the appended IV from the password string so it can be used for the next encryption */ - /* password format: :: */ - void GetIVFromPass(const Anope::string &password) - { - size_t pos = password.find(':'); - Anope::string buf = password.substr(password.find(':', pos + 1) + 1, password.length()); - char buf2[33]; - Anope::Unhex(buf, buf2); - for (int i = 0 ; i < 8; ++i) - PACK32(reinterpret_cast(&buf2[i << 2]), iv[i]); - } - - void SHA256Init(SHA256Context *ctx) - { - for (int i = 0; i < 8; ++i) - ctx->h[i] = iv[i]; - ctx->len = 0; - ctx->tot_len = 0; - } - - void SHA256Transform(SHA256Context *ctx, unsigned char *message, unsigned block_nb) - { - uint32 w[64], wv[8]; - unsigned char *sub_block; - for (unsigned i = 1; i <= block_nb; ++i) - { - int j; - sub_block = message + ((i - 1) << 6); - - for (j = 0; j < 16; ++j) - PACK32(&sub_block[j << 2], w[j]); - for (j = 16; j < 64; ++j) - SHA256_SCR(w, j); - for (j = 0; j < 8; ++j) - wv[j] = ctx->h[j]; - for (j = 0; j < 64; ++j) - { - uint32 t1 = wv[7] + SHA256_F2(wv[4]) + CH(wv[4], wv[5], wv[6]) + sha256_k[j] + w[j]; - uint32 t2 = SHA256_F1(wv[0]) + MAJ(wv[0], wv[1], wv[2]); - wv[7] = wv[6]; - wv[6] = wv[5]; - wv[5] = wv[4]; - wv[4] = wv[3] + t1; - wv[3] = wv[2]; - wv[2] = wv[1]; - wv[1] = wv[0]; - wv[0] = t1 + t2; - } - for (j = 0; j < 8; ++j) - ctx->h[j] += wv[j]; - } - } - - void SHA256Update(SHA256Context *ctx, const unsigned char *message, unsigned len) - { - /* - * XXX here be dragons! - * After many hours of pouring over this, I think I've found the problem. - * When Special created our module from the reference one, he used: - * - * unsigned rem_len = SHA256_BLOCK_SIZE - ctx->len; - * - * instead of the reference's version of: - * - * unsigned tmp_len = SHA256_BLOCK_SIZE - ctx->len; - * unsigned rem_len = len < tmp_len ? len : tmp_len; - * - * I've changed back to the reference version of this code, and it seems to work with no errors. - * So I'm inclined to believe this was the problem.. - * -- w00t (January 06, 2008) - */ - unsigned tmp_len = SHA256_BLOCK_SIZE - ctx->len, rem_len = len < tmp_len ? len : tmp_len; - - memcpy(&ctx->block[ctx->len], message, rem_len); - if (ctx->len + len < SHA256_BLOCK_SIZE) - { - ctx->len += len; - return; - } - unsigned new_len = len - rem_len, block_nb = new_len / SHA256_BLOCK_SIZE; - unsigned char *shifted_message = new unsigned char[len - rem_len]; - memcpy(shifted_message, message + rem_len, len - rem_len); - SHA256Transform(ctx, ctx->block, 1); - SHA256Transform(ctx, shifted_message, block_nb); - rem_len = new_len % SHA256_BLOCK_SIZE; - memcpy(ctx->block, &shifted_message[block_nb << 6], rem_len); - delete [] shifted_message; - ctx->len = rem_len; - ctx->tot_len += (block_nb + 1) << 6; - } - - void SHA256Final(SHA256Context *ctx, unsigned char *digest) - { - unsigned block_nb = 1 + ((SHA256_BLOCK_SIZE - 9) < (ctx->len % SHA256_BLOCK_SIZE)); - unsigned len_b = (ctx->tot_len + ctx->len) << 3; - unsigned pm_len = block_nb << 6; - memset(ctx->block + ctx->len, 0, pm_len - ctx->len); - ctx->block[ctx->len] = 0x80; - UNPACK32(len_b, ctx->block + pm_len - 4); - SHA256Transform(ctx, ctx->block, block_nb); - for (int i = 0 ; i < 8; ++i) - UNPACK32(ctx->h[i], &digest[i << 2]); - } - -/********** ANOPE ******/ - public: - ESHA256(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, ENCRYPTION) - { - this->SetAuthor("Anope"); - - Implementation i[] = { I_OnEncrypt, I_OnCheckAuthentication }; - ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); - - use_iv = false; - } - - EventReturn OnEncrypt(const Anope::string &src, Anope::string &dest) - { - char digest[SHA256_DIGEST_SIZE + 1]; - SHA256Context ctx; - std::stringstream buf; - - if (!use_iv) - NewRandomIV(); - else - use_iv = false; - - SHA256Init(&ctx); - SHA256Update(&ctx, reinterpret_cast(src.c_str()), src.length()); - SHA256Final(&ctx, reinterpret_cast(digest)); - digest[SHA256_DIGEST_SIZE] = '\0'; - buf << "sha256:" << Anope::Hex(digest, SHA256_DIGEST_SIZE) << ":" << GetIVString(); - Log(LOG_DEBUG_2) << "(enc_sha256) hashed password from [" << src << "] to [" << buf.str() << " ]"; - dest = buf.str(); - return EVENT_ALLOW; - } - - EventReturn OnCheckAuthentication(Command *c, CommandSource *source, const std::vector ¶ms, const Anope::string &account, const Anope::string &password) - { - NickAlias *na = findnick(account); - NickCore *nc = na ? na->nc : NULL; - if (na == NULL) - return EVENT_CONTINUE; - - size_t pos = nc->pass.find(':'); - if (pos == Anope::string::npos) - return EVENT_CONTINUE; - Anope::string hash_method(nc->pass.begin(), nc->pass.begin() + pos); - if (!hash_method.equals_cs("sha256")) - return EVENT_CONTINUE; - - GetIVFromPass(nc->pass); - use_iv = true; - Anope::string buf; - this->OnEncrypt(password, 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) - enc_encrypt(password, nc->pass); - return EVENT_ALLOW; - } - - return EVENT_CONTINUE; - } -}; - -MODULE_INIT(ESHA256) diff --git a/modules/core/gl_global.cpp b/modules/core/gl_global.cpp deleted file mode 100644 index 80c42e017..000000000 --- a/modules/core/gl_global.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/* Global core functions - * - * (C) 2003-2011 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 "global.h" - -class CommandGLGlobal : public Command -{ - 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 ¶ms) - { - User *u = source.u; - const Anope::string &msg = params[0]; - - if (!global) - source.Reply("No global reference, is gl_main loaded?"); - else - { - Log(LOG_ADMIN, u, this); - global->SendGlobal(findbot(Config->Global), u->nick, msg); - } - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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.owner->nick.c_str()); - return true; - } -}; - -class GLGlobal : public Module -{ - CommandGLGlobal commandglglobal; - - public: - GLGlobal(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandglglobal(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandglglobal); - } -}; - -MODULE_INIT(GLGlobal) diff --git a/modules/core/gl_main.cpp b/modules/core/gl_main.cpp deleted file mode 100644 index c7ccf9209..000000000 --- a/modules/core/gl_main.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/* Global core functions - * - * (C) 2003-2011 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 "global.h" - -class MyGlobalService : public GlobalService -{ - void ServerGlobal(Server *s, const Anope::string &message) - { - if (s != Me && !s->HasFlag(SERVER_JUPED)) - notice_server(Config->Global, s, "%s", message.c_str()); - for (unsigned i = 0, j = s->GetLinks().size(); i < j; ++i) - this->ServerGlobal(s->GetLinks()[i], message); - } - - public: - MyGlobalService(Module *m) : GlobalService(m) { } - - void SendGlobal(BotInfo *sender, const Anope::string &source, const Anope::string &message) - { - if (Me->GetLinks().empty()) - return; - - Anope::string rmessage; - - if (!source.empty() && !Config->AnonymousGlobal) - rmessage = "[" + source + "] " + message; - else - rmessage = message; - - this->ServerGlobal(Me->GetLinks().front(), rmessage); - } -}; - -class GlobalCore : public Module -{ - MyGlobalService myglobalservice; - - public: - GlobalCore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - myglobalservice(this) - { - this->SetAuthor("Anope"); - - BotInfo *Global = findbot(Config->Global); - if (Global == NULL) - throw ModuleException("No bot named " + Config->Global); - - ModuleManager::RegisterService(&this->myglobalservice); - - Implementation i[] = { I_OnRestart, I_OnShutdown, I_OnNewServer, I_OnPreHelp }; - ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); - } - - void OnRestart() - { - if (Config->GlobalOnCycle) - global->SendGlobal(findbot(Config->Global), "", Config->GlobalOnCycleMessage); - } - - void OnShutdown() - { - if (Config->GlobalOnCycle) - global->SendGlobal(findbot(Config->Global), "", Config->GlobalOnCycleMessage); - } - - void OnNewServer(Server *s) - { - if (Config->GlobalOnCycle && !Config->GlobalOnCycleUP.empty()) - notice_server(Config->Global, s, "%s", Config->GlobalOnCycleUP.c_str()); - } - - void OnPreHelp(CommandSource &source, const std::vector ¶ms) - { - if (!params.empty() || source.owner->nick != Config->Global) - return; - source.Reply(_("%s commands:\n"), Config->Global.c_str()); - } -}; - -MODULE_INIT(GlobalCore) - diff --git a/modules/core/global.h b/modules/core/global.h deleted file mode 100644 index 454e879c2..000000000 --- a/modules/core/global.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef GLOBAL_H -#define GLOBAL_H - -class GlobalService : public Service -{ - public: - GlobalService(Module *m) : Service(m, "Global") { } - - /** Send out a global message to all users - * @param sender Our client which should send the global - * @param source The sender of the global - * @param message The message - */ - virtual void SendGlobal(BotInfo *sender, const Anope::string &source, const Anope::string &message) = 0; -}; - -static service_reference global("Global"); - -#endif // GLOBAL_H - diff --git a/modules/core/help.cpp b/modules/core/help.cpp deleted file mode 100644 index 2a4f25b6c..000000000 --- a/modules/core/help.cpp +++ /dev/null @@ -1,130 +0,0 @@ -/* Core functions - * - * (C) 2003-2011 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 CommandHelp : public Command -{ - public: - CommandHelp(Module *creator) : Command(creator, "generic/help", 0) - { - this->SetDesc(_("Displays this list and give information about commands")); - this->SetFlag(CFLAG_STRIP_CHANNEL); - this->SetFlag(CFLAG_ALLOW_UNREGISTERED); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - FOREACH_MOD(I_OnPreHelp, OnPreHelp(source, params)); - - User *u = source.u; - BotInfo *bi = source.owner; - - if (params.empty()) - { - for (BotInfo::command_map::iterator it = bi->commands.begin(), it_end = bi->commands.end(); it != it_end; ++it) - { - const Anope::string &c_name = it->first; - CommandInfo &info = it->second; - - // Smaller command exists - Anope::string cmd = myStrGetToken(c_name, ' ', 0); - if (cmd != it->first && bi->commands.count(cmd)) - continue; - - service_reference c(info.name); - if (!c) - continue; - if (!Config->HidePrivilegedCommands || info.permission.empty() || u->HasCommand(info.permission)) - { - source.command = c_name; - c->OnServHelp(source); - } - } - } - else - { - bool helped = false; - for (unsigned max = params.size(); max > 0; --max) - { - Anope::string full_command; - for (unsigned i = 0; i < max; ++i) - full_command += " " + params[i]; - full_command.erase(full_command.begin()); - - BotInfo::command_map::iterator it = bi->commands.find(full_command); - if (it == bi->commands.end()) - continue; - - CommandInfo &info = it->second; - - service_reference c(info.name); - if (!c) - continue; - - if (Config->HidePrivilegedCommands && !info.permission.empty() && !u->HasCommand(info.permission)) - continue; - - const Anope::string &subcommand = params.size() > max ? params[max] : ""; - source.command = full_command; - if (!c->OnHelp(source, subcommand)) - continue; - - helped = true; - - /* Inform the user what permission is required to use the 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()); - } - if (!c->HasFlag(CFLAG_ALLOW_UNREGISTERED) && !u->IsIdentified()) - { - if (info.permission.empty()) - source.Reply(" "); - source.Reply( _("You need to be identified to use this command.")); - } - /* User doesn't have the proper permission to use this command */ - else if (!info.permission.empty() && !u->HasCommand(info.permission)) - { - source.Reply(_("You cannot use this command.")); - } - - break; - } - - if (helped == false) - source.Reply(_("No help available for \002%s\002."), params[0].c_str()); - } - - FOREACH_MOD(I_OnPostHelp, OnPostHelp(source, params)); - - return; - } -}; - -class Help : public Module -{ - CommandHelp commandhelp; - - public: - Help(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandhelp(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandhelp); - } -}; - -MODULE_INIT(Help) diff --git a/modules/core/hs_del.cpp b/modules/core/hs_del.cpp deleted file mode 100644 index 541986aff..000000000 --- a/modules/core/hs_del.cpp +++ /dev/null @@ -1,107 +0,0 @@ -/* HostServ core functions - * - * (C) 2003-2011 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 ¶ms) - { - User *u = source.u; - const Anope::string &nick = params[0]; - NickAlias *na = findnick(nick); - if (na) - { - Log(LOG_ADMIN, u, this) << "for user " << na->nick; - FOREACH_MOD(I_OnDeleteVhost, OnDeleteVhost(na)); - na->hostinfo.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) - { - 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(_("Delete the vhost for all nicks in a group")); - this->SetSyntax(_("\037nick\037")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - const Anope::string &nick = params[0]; - User *u = source.u; - NickAlias *na = findnick(nick); - if (na) - { - FOREACH_MOD(I_OnDeleteVhost, OnDeleteVhost(na)); - NickCore *nc = na->nc; - for (std::list::iterator it = nc->aliases.begin(), it_end = nc->aliases.end(); it != it_end; ++it) - { - na = *it; - na->hostinfo.RemoveVhost(); - } - Log(LOG_ADMIN, u, 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) - { - 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, CORE), - commandhsdel(this), commandhsdelall(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandhsdel); - ModuleManager::RegisterService(&commandhsdelall); - } -}; - -MODULE_INIT(HSDel) diff --git a/modules/core/hs_group.cpp b/modules/core/hs_group.cpp deleted file mode 100644 index 8d6fbd630..000000000 --- a/modules/core/hs_group.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/* HostServ core functions - * - * (C) 2003-2011 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 -{ - void Sync(NickAlias *na) - { - if (!na || !na->hostinfo.HasVhost()) - return; - - for (std::list::iterator it = na->nc->aliases.begin(), it_end = na->nc->aliases.end(); it != it_end; ++it) - { - NickAlias *nick = *it; - nick->hostinfo.SetVhost(na->hostinfo.GetIdent(), na->hostinfo.GetHost(), na->hostinfo.GetCreator()); - } - } - - public: - CommandHSGroup(Module *creator) : Command(creator, "hostserv/group", 0, 0) - { - this->SetDesc(_("Syncs the vhost for all nicks in a group")); - this->SetSyntax(""); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - NickAlias *na = findnick(u->nick); - if (na && u->Account() == na->nc && na->hostinfo.HasVhost()) - { - this->Sync(na); - if (!na->hostinfo.GetIdent().empty()) - source.Reply(_("All vhost's in the group \002%s\002 have been set to \002%s\002@\002%s\002"), u->Account()->display.c_str(), na->hostinfo.GetIdent().c_str(), na->hostinfo.GetHost().c_str()); - else - source.Reply(_("All vhost's in the group \002%s\002 have been set to \002%s\002"), u->Account()->display.c_str(), na->hostinfo.GetHost().c_str()); - } - else - source.Reply(HOST_NOT_ASSIGNED); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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; - - public: - HSGroup(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandhsgroup(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandhsgroup); - } -}; - -MODULE_INIT(HSGroup) diff --git a/modules/core/hs_list.cpp b/modules/core/hs_list.cpp deleted file mode 100644 index 589eef3e1..000000000 --- a/modules/core/hs_list.cpp +++ /dev/null @@ -1,137 +0,0 @@ -/* HostServ core functions - * - * (C) 2003-2011 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 CommandHSList : public Command -{ - public: - CommandHSList(Module *creator) : Command(creator, "hostserv/list", 0, 1) - { - this->SetDesc(_("Displays one or more vhost entries.")); - this->SetSyntax(_("\002[|<#X-Y>]")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - const Anope::string &key = !params.empty() ? params[0] : ""; - int from = 0, to = 0, counter = 1; - unsigned display_counter = 0; - - /** - * Do a check for a range here, then in the next loop - * we'll only display what has been requested.. - **/ - if (!key.empty() && key[0] == '#') - { - size_t tmp = key.find('-'); - if (tmp == Anope::string::npos || tmp == key.length() || tmp == 1) - { - source.Reply(LIST_INCORRECT_RANGE); - return; - } - for (unsigned i = 1, end = key.length(); i < end; ++i) - { - if (!isdigit(key[i]) && i != tmp) - { - source.Reply(LIST_INCORRECT_RANGE); - return; - } - try - { - from = convertTo(key.substr(1, tmp - 1)); - to = convertTo(key.substr(tmp + 1)); - } - catch (const ConvertException &) { } - } - } - - for (nickalias_map::const_iterator it = NickAliasList.begin(), it_end = NickAliasList.end(); it != it_end; ++it) - { - NickAlias *na = it->second; - - if (!na->hostinfo.HasVhost()) - continue; - - if (!key.empty() && key[0] != '#') - { - if ((Anope::Match(na->nick, key) || Anope::Match(na->hostinfo.GetHost(), key)) && display_counter < Config->NSListMax) - { - ++display_counter; - if (!na->hostinfo.GetIdent().empty()) - source.Reply(_("#%d Nick:\002%s\002, vhost:\002%s\002@\002%s\002 (%s - %s)"), counter, na->nick.c_str(), na->hostinfo.GetIdent().c_str(), na->hostinfo.GetHost().c_str(), na->hostinfo.GetCreator().c_str(), do_strftime(na->hostinfo.GetTime()).c_str()); - else - source.Reply(_("#%d Nick:\002%s\002, vhost:\002%s\002 (%s - %s)"), counter, na->nick.c_str(), na->hostinfo.GetHost().c_str(), na->hostinfo.GetCreator().c_str(), do_strftime(na->hostinfo.GetTime()).c_str()); - } - } - else - { - /** - * List the host if its in the display range, and not more - * than NSListMax records have been displayed... - **/ - if (((counter >= from && counter <= to) || (!from && !to)) && display_counter < Config->NSListMax) - { - ++display_counter; - if (!na->hostinfo.GetIdent().empty()) - source.Reply(_("#%d Nick:\002%s\002, vhost:\002%s\002@\002%s\002 (%s - %s)"), counter, na->nick.c_str(), na->hostinfo.GetIdent().c_str(), na->hostinfo.GetHost().c_str(), na->hostinfo.GetCreator().c_str(), do_strftime(na->hostinfo.GetTime()).c_str()); - else - source.Reply(_("#%d Nick:\002%s\002, vhost:\002%s\002 (%s - %s)"), counter, na->nick.c_str(), na->hostinfo.GetHost().c_str(), na->hostinfo.GetCreator().c_str(), do_strftime(na->hostinfo.GetTime()).c_str()); - } - } - ++counter; - } - if (!key.empty()) - source.Reply(_("Displayed records matching key \002%s\002 (Count: \002%d\002)"), key.c_str(), display_counter); - else - { - if (from) - source.Reply(_("Displayed records from \002%d\002 to \002%d\002"), from, to); - else - source.Reply(_("Displayed all records (Count: \002%d\002)"), display_counter); - } - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("This command lists registered vhosts to the operator\n" - "if a Key is specified, only entries whos nick or vhost match\n" - "the pattern given in are displayed e.g. Rob* for all\n" - "entries beginning with \"Rob\"\n" - "If a #X-Y style is used, only entries between the range of X\n" - "and Y will be displayed, e.g. #1-3 will display the first 3\n" - "nick/vhost entries.\n" - "The list uses the value of NSListMax as a hard limit for the\n" - "number of items to display to a operator at any 1 time.")); - return true; - } -}; - -class HSList : public Module -{ - CommandHSList commandhslist; - - public: - HSList(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandhslist(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandhslist); - } -}; - -MODULE_INIT(HSList) diff --git a/modules/core/hs_main.cpp b/modules/core/hs_main.cpp deleted file mode 100644 index a6cc6bbb7..000000000 --- a/modules/core/hs_main.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/* HostServ core functions - * - * (C) 2003-2011 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 -{ - public: - HostServCore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE) - { - this->SetAuthor("Anope"); - - if (!ircd || !ircd->vhost) - throw ModuleException("Your IRCd does not suppor vhosts"); - - BotInfo *HostServ = findbot(Config->HostServ); - if (HostServ == NULL) - throw ModuleException("No bot named " + Config->HostServ); - - Implementation i[] = { I_OnNickIdentify, I_OnNickUpdate, I_OnPreHelp }; - ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); - } - - void OnNickIdentify(User *u) - { - HostInfo *ho = NULL; - NickAlias *na = findnick(u->nick); - if (na && na->hostinfo.HasVhost()) - ho = &na->hostinfo; - else - { - na = findnick(u->Account()->display); - if (na && na->hostinfo.HasVhost()) - ho = &na->hostinfo; - } - if (ho == NULL) - return; - - if (u->vhost.empty() || !u->vhost.equals_cs(na->hostinfo.GetHost()) || (!na->hostinfo.GetIdent().empty() && !u->GetVIdent().equals_cs(na->hostinfo.GetIdent()))) - { - ircdproto->SendVhost(u, na->hostinfo.GetIdent(), na->hostinfo.GetHost()); - if (ircd->vhost) - { - u->vhost = na->hostinfo.GetHost(); - u->UpdateHost(); - } - if (ircd->vident && !na->hostinfo.GetIdent().empty()) - u->SetVIdent(na->hostinfo.GetIdent()); - - BotInfo *bi = findbot(Config->HostServ); - if (bi) - { - if (!na->hostinfo.GetIdent().empty()) - u->SendMessage(bi, _("Your vhost of \002%s\002@\002%s\002 is now activated."), na->hostinfo.GetIdent().c_str(), na->hostinfo.GetHost().c_str()); - else - u->SendMessage(bi, _("Your vhost of \002%s\002 is now activated."), na->hostinfo.GetHost().c_str()); - } - } - } - - void OnNickUpdate(User *u) - { - this->OnNickIdentify(u); - } - - void OnPreHelp(CommandSource &source, const std::vector ¶ms) - { - if (!params.empty() || source.owner->nick != Config->HostServ) - return; - source.Reply(_("%s commands:\n"), Config->HostServ.c_str()); - } -}; - -MODULE_INIT(HostServCore) - diff --git a/modules/core/hs_off.cpp b/modules/core/hs_off.cpp deleted file mode 100644 index a3f11c2bd..000000000 --- a/modules/core/hs_off.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/* HostServ core functions - * - * (C) 2003-2011 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->SetSyntax(""); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - NickAlias *na = findnick(u->nick); - - if (!na || !na->hostinfo.HasVhost()) - source.Reply(HOST_NOT_ASSIGNED); - else - { - ircdproto->SendVhostDel(u); - Log(LOG_COMMAND, u, 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) - { - 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 IP address.")); - return true; - } -}; - -class HSOff : public Module -{ - CommandHSOff commandhsoff; - - public: - HSOff(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandhsoff(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandhsoff); - } -}; - -MODULE_INIT(HSOff) diff --git a/modules/core/hs_on.cpp b/modules/core/hs_on.cpp deleted file mode 100644 index 0ba124ba1..000000000 --- a/modules/core/hs_on.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/* HostServ core functions - * - * (C) 2003-2011 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->SetSyntax(""); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - NickAlias *na = findnick(u->nick); - if (na && u->Account() == na->nc && na->hostinfo.HasVhost()) - { - if (!na->hostinfo.GetIdent().empty()) - source.Reply(_("Your vhost of \002%s\002@\002%s\002 is now activated."), na->hostinfo.GetIdent().c_str(), na->hostinfo.GetHost().c_str()); - else - source.Reply(_("Your vhost of \002%s\002 is now activated."), na->hostinfo.GetHost().c_str()); - Log(LOG_COMMAND, u, this) << "to enable their vhost of " << (!na->hostinfo.GetIdent().empty() ? na->hostinfo.GetIdent() + "@" : "") << na->hostinfo.GetHost(); - ircdproto->SendVhost(u, na->hostinfo.GetIdent(), na->hostinfo.GetHost()); - if (ircd->vhost) - u->vhost = na->hostinfo.GetHost(); - if (ircd->vident) - { - if (!na->hostinfo.GetIdent().empty()) - u->SetVIdent(na->hostinfo.GetIdent()); - } - u->UpdateHost(); - } - else - source.Reply(HOST_NOT_ASSIGNED); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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 IP address.")); - return true; - } -}; - -class HSOn : public Module -{ - CommandHSOn commandhson; - - public: - HSOn(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandhson(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandhson); - } -}; - -MODULE_INIT(HSOn) diff --git a/modules/core/hs_set.cpp b/modules/core/hs_set.cpp deleted file mode 100644 index 4711022db..000000000 --- a/modules/core/hs_set.cpp +++ /dev/null @@ -1,236 +0,0 @@ -/* HostServ core functions - * - * (C) 2003-2011 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 ¶ms) - { - User *u = source.u; - - const Anope::string &nick = params[0]; - - NickAlias *na = findnick(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 (user.length() > Config->UserLen) - { - source.Reply(HOST_SET_IDENTTOOLONG, Config->UserLen); - return; - } - else if (!ircd->vident) - { - 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->HostLen) - { - source.Reply(HOST_SET_TOOLONG, Config->HostLen); - return; - } - - if (!isValidHost(host, 3)) - { - source.Reply(HOST_SET_ERROR); - return; - } - - Log(LOG_ADMIN, u, this) << "to set the vhost of " << na->nick << " to " << (!user.empty() ? user + "@" : "") << host; - - na->hostinfo.SetVhost(user, host, u->nick); - FOREACH_MOD(I_OnSetVhost, 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) - { - 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 @ set idents for users as \n" - "well as vhosts.")); - return true; - } -}; - -class CommandHSSetAll : public Command -{ - void Sync(NickAlias *na) - { - if (!na || !na->hostinfo.HasVhost()) - return; - - for (std::list::iterator it = na->nc->aliases.begin(), it_end = na->nc->aliases.end(); it != it_end; ++it) - { - NickAlias *nick = *it; - nick->hostinfo.SetVhost(na->hostinfo.GetIdent(), na->hostinfo.GetHost(), na->hostinfo.GetCreator()); - } - } - - 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 ¶ms) - { - User *u = source.u; - - Anope::string nick = params[0]; - - NickAlias *na = findnick(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 (user.length() > Config->UserLen) - { - source.Reply(HOST_SET_IDENTTOOLONG, Config->UserLen); - return; - } - else if (!ircd->vident) - { - 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->HostLen) - { - source.Reply(HOST_SET_TOOLONG, Config->HostLen); - return; - } - - if (!isValidHost(host, 3)) - { - source.Reply(HOST_SET_ERROR); - return; - } - - Log(LOG_ADMIN, u, this) << "to set the vhost of " << na->nick << " to " << (!user.empty() ? user + "@" : "") << host; - - na->hostinfo.SetVhost(user, host, u->nick); - this->Sync(na); - FOREACH_MOD(I_OnSetVhost, 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) - { - 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 @ 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, CORE), commandhsset(this), commandhssetall(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandhsset); - ModuleManager::RegisterService(&commandhssetall); - } -}; - -MODULE_INIT(HSSet) diff --git a/modules/core/memoserv.h b/modules/core/memoserv.h deleted file mode 100644 index 0bf0c79ca..000000000 --- a/modules/core/memoserv.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef MEMOSERV_H -#define MEMOSERV_H - -class MemoServService : public Service -{ - public: - enum MemoResult - { - MEMO_SUCCESS, - MEMO_INVALID_TARGET, - MEMO_TOO_FAST, - MEMO_TARGET_FULL - }; - - MemoServService(Module *m) : Service(m, "MemoServ") { } - - /** Retrieve the memo info for a nick or channel - * @param target Target - * @param ischan Set to true if target is a channel - * @return A memoinfo structure or NULL - */ - virtual MemoInfo *GetMemoInfo(const Anope::string &target, bool &ischan) = 0; - - /** Sends a memo. - * @param source The source of the memo, can be anythin. - * @param target The target of the memo, nick or channel. - * @param message Memo text - * @param force true to force the memo, restrictions/delays etc are not checked - */ - virtual MemoResult Send(const Anope::string &source, const Anope::string &target, const Anope::string &message, bool force = false) = 0; - - /** Check for new memos and notify the user if there are any - * @param u The user - */ - virtual void Check(User *u) = 0; -}; - -static service_reference memoserv("MemoServ"); - -#endif // MEMOSERV_H - diff --git a/modules/core/ms_cancel.cpp b/modules/core/ms_cancel.cpp deleted file mode 100644 index e5beffa10..000000000 --- a/modules/core/ms_cancel.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/* MemoServ core functions - * - * (C) 2003-2011 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 "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(_("{\037nick\037 | \037channel\037}")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - - const Anope::string &nname = params[0]; - - bool ischan; - MemoInfo *mi = memoserv->GetMemoInfo(nname, ischan); - - if (mi == NULL) - source.Reply(ischan ? CHAN_X_NOT_REGISTERED : _(NICK_X_NOT_REGISTERED), nname.c_str()); - else - { - for (int i = mi->memos.size() - 1; i >= 0; --i) - if (mi->memos[i]->HasFlag(MF_UNREAD) && u->Account()->display.equals_ci(mi->memos[i]->sender)) - { - FOREACH_MOD(I_OnMemoDel, OnMemoDel(findnick(nname)->nc, mi, mi->memos[i])); - mi->Del(mi->memos[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) - { - 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, CORE), - commandmscancel(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandmscancel); - } -}; - -MODULE_INIT(MSCancel) diff --git a/modules/core/ms_check.cpp b/modules/core/ms_check.cpp deleted file mode 100644 index 3019e270f..000000000 --- a/modules/core/ms_check.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/* MemoServ core functions - * - * (C) 2003-2011 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 ¶ms) - { - User *u = source.u; - - const Anope::string &recipient = params[0]; - - bool found = false; - - NickAlias *na = findnick(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 wanna get the LAST memo, so we - have to loop backwards */ - - for (int i = mi->memos.size() - 1; i >= 0; --i) - { - if (u->Account()->display.equals_ci(mi->memos[i]->sender)) - { - found = true; /* Yes, we've found the memo */ - - if (mi->memos[i]->HasFlag(MF_UNREAD)) - source.Reply(_("The last memo you sent to %s (sent on %s) has not yet been read."), na->nick.c_str(), do_strftime(mi->memos[i]->time).c_str()); - else - source.Reply(_("The last memo you sent to %s (sent on %s) has been read."), na->nick.c_str(), do_strftime(mi->memos[i]->time).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) - { - 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 does only work with nicks, not with chans.")); - return true; - } -}; - -class MSCheck : public Module -{ - CommandMSCheck commandmscheck; - - public: - MSCheck(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandmscheck(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandmscheck); - } -}; - -MODULE_INIT(MSCheck) diff --git a/modules/core/ms_del.cpp b/modules/core/ms_del.cpp deleted file mode 100644 index 991adff2b..000000000 --- a/modules/core/ms_del.cpp +++ /dev/null @@ -1,163 +0,0 @@ -/* MemoServ core functions - * - * (C) 2003-2011 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) - { - if (!Number || Number > mi->memos.size()) - return; - - if (ci) - FOREACH_MOD(I_OnMemoDel, OnMemoDel(ci, mi, mi->memos[Number - 1])); - else - FOREACH_MOD(I_OnMemoDel, OnMemoDel(source.u->Account(), mi, mi->memos[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 ¶ms) - { - User *u = source.u; - - 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] : ""; - - if (!(ci = cs_findchan(chan))) - { - source.Reply(CHAN_X_NOT_REGISTERED, chan.c_str()); - return; - } - else if (readonly) - { - source.Reply(READ_ONLY_MODE); - return; - } - else if (!ci->HasPriv(u, CA_MEMO)) - { - source.Reply(ACCESS_DENIED); - return; - } - mi = &ci->memos; - } - else - mi = &u->Account()->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. */ - if (ci) - FOREACH_MOD(I_OnMemoDel, OnMemoDel(ci, mi, mi->memos[mi->memos.size() - 1])); - else - FOREACH_MOD(I_OnMemoDel, OnMemoDel(u->Account(), mi, mi->memos[mi->memos.size() - 1])); - mi->Del(mi->memos[mi->memos.size() - 1]); - source.Reply(_("Memo %d has been deleted."), mi->memos.size() + 1); - } - else - { - if (ci) - FOREACH_MOD(I_OnMemoDel, OnMemoDel(ci, mi, NULL)); - else - FOREACH_MOD(I_OnMemoDel, OnMemoDel(u->Account(), mi, NULL)); - /* Delete all memos. */ - for (unsigned i = 0, end = mi->memos.size(); i < end; ++i) - delete mi->memos[i]; - mi->memos.clear(); - 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) - { - 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, CORE), - commandmsdel(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandmsdel); - } -}; - -MODULE_INIT(MSDel) diff --git a/modules/core/ms_ignore.cpp b/modules/core/ms_ignore.cpp deleted file mode 100644 index c8b9c19e6..000000000 --- a/modules/core/ms_ignore.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/* MemoServ core functions - * - * (C) 2003-2011 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 "memoserv.h" - -class CommandMSIgnore : public Command -{ - public: - CommandMSIgnore(Module *creator) : Command(creator, "memoserv/ignore", 1, 3) - { - this->SetDesc(_("Manage your memo ignore list")); - this->SetSyntax(_("[\037channel\037] {\002ADD|DEL|LIST\002} [\037entry\037]")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - - 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 = u->nick; - } - - bool ischan; - MemoInfo *mi = memoserv->GetMemoInfo(channel, ischan); - if (!mi) - source.Reply(ischan ? CHAN_X_NOT_REGISTERED : _(NICK_X_NOT_REGISTERED), channel.c_str()); - else if (ischan && !cs_findchan(channel)->HasPriv(u, CA_MEMO)) - source.Reply(ACCESS_DENIED); - else if (command.equals_ci("ADD") && !param.empty()) - { - 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 your ignore list."), param.c_str()); - } - else - source.Reply(_("\002%s\002 is already on your ignore list."), param.c_str()); - } - else if (command.equals_ci("DEL") && !param.empty()) - { - std::vector::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 your ignore list."), param.c_str()); - } - else - source.Reply(_("\002%s\002 is not on your ignore list."), param.c_str()); - } - else if (command.equals_ci("LIST")) - { - if (mi->ignores.empty()) - source.Reply(_("Your memo ignore list is empty.")); - else - { - source.Reply(_("Ignore list:")); - for (unsigned i = 0; i < mi->ignores.size(); ++i) - source.Reply(" %s", mi->ignores[i].c_str()); - } - } - else - this->OnSyntaxError(source, ""); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Allows you to ignore users by nick or host from memoing you. If someone on your\n" - "memo ignore list tries to memo you, 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, CORE), - commandmsignore(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandmsignore); - } -}; - -MODULE_INIT(MSIgnore) diff --git a/modules/core/ms_info.cpp b/modules/core/ms_info.cpp deleted file mode 100644 index 9fdb467c9..000000000 --- a/modules/core/ms_info.cpp +++ /dev/null @@ -1,219 +0,0 @@ -/* MemoServ core functions - * - * (C) 2003-2011 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 ¶ms) - { - User *u = source.u; - - const MemoInfo *mi; - NickAlias *na = NULL; - ChannelInfo *ci = NULL; - const Anope::string &nname = !params.empty() ? params[0] : ""; - int hardmax = 0; - - if (!nname.empty() && nname[0] != '#' && u->HasPriv("memoserv/info")) - { - na = findnick(nname); - if (!na) - { - source.Reply(NICK_X_NOT_REGISTERED, nname.c_str()); - return; - } - mi = &na->nc->memos; - hardmax = na->nc->HasFlag(NI_MEMO_HARDMAX) ? 1 : 0; - } - else if (!nname.empty() && nname[0] == '#') - { - if (!(ci = cs_findchan(nname))) - { - source.Reply(CHAN_X_NOT_REGISTERED, nname.c_str()); - return; - } - else if (!ci->HasPriv(u, CA_MEMO)) - { - source.Reply(ACCESS_DENIED); - return; - } - mi = &ci->memos; - hardmax = ci->HasFlag(CI_MEMO_HARDMAX) ? 1 : 0; - } - else if (!nname.empty()) /* It's not a chan and we aren't services admin */ - { - source.Reply(ACCESS_DENIED); - return; - } - else - { - mi = &u->Account()->memos; - hardmax = u->Account()->HasFlag(NI_MEMO_HARDMAX) ? 1 : 0; - } - - if (!nname.empty() && (ci || na->nc != u->Account())) - { - if (mi->memos.empty()) - source.Reply(_("%s currently has no memos."), nname.c_str()); - else if (mi->memos.size() == 1) - { - if (mi->memos[0]->HasFlag(MF_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->memos[i]->HasFlag(MF_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->HasFlag(NI_MEMO_RECEIVE) && na->nc->HasFlag(NI_MEMO_SIGNON)) - source.Reply(_("%s is notified of new memos at logon and when they arrive."), nname.c_str()); - else if (na->nc->HasFlag(NI_MEMO_RECEIVE)) - source.Reply(_("%s is notified when new memos arrive."), nname.c_str()); - else if (na->nc->HasFlag(NI_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 == u->Account()) */ - { - if (mi->memos.empty()) - source.Reply(_("You currently have no memos.")); - else if (mi->memos.size() == 1) - { - if (mi->memos[0]->HasFlag(MF_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->memos[i]->HasFlag(MF_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 (!u->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 (!u->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.")); - - /* Ripped too. But differently because of a seg fault (loughs) */ - if (u->Account()->HasFlag(NI_MEMO_RECEIVE) && u->Account()->HasFlag(NI_MEMO_SIGNON)) - source.Reply(_("You will be notified of new memos at logon and when they arrive.")); - else if (u->Account()->HasFlag(NI_MEMO_RECEIVE)) - source.Reply(_("You will be notified when new memos arrive.")); - else if (u->Account()->HasFlag(NI_MEMO_SIGNON)) - source.Reply(_("You will be notified of new memos at logon.")); - else - source.Reply(_("You will not be notified of new memos.")); - } - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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 use limited to \002Services\n" - "Operators\002.")); - - return true; - } -}; - -class MSInfo : public Module -{ - CommandMSInfo commandmsinfo; - - public: - MSInfo(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandmsinfo(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandmsinfo); - } -}; - -MODULE_INIT(MSInfo) diff --git a/modules/core/ms_list.cpp b/modules/core/ms_list.cpp deleted file mode 100644 index 26821a413..000000000 --- a/modules/core/ms_list.cpp +++ /dev/null @@ -1,168 +0,0 @@ -/* MemoServ core functions - * - * (C) 2003-2011 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 MemoListCallback : public NumberList -{ - CommandSource &source; - ChannelInfo *ci; - const MemoInfo *mi; - bool SentHeader; - public: - MemoListCallback(CommandSource &_source, ChannelInfo *_ci, const MemoInfo *_mi, const Anope::string &list) : NumberList(list, false), source(_source), ci(_ci), mi(_mi), SentHeader(false) - { - } - - void HandleNumber(unsigned Number) - { - if (!Number || Number > mi->memos.size()) - return; - - if (!SentHeader) - { - SentHeader = true; - source.Reply(_("Memos for %s:"), ci ? ci->name.c_str() : source.u->nick.c_str()); - source.Reply(_(" Num Sender Date/Time")); - } - - DoList(source, mi, Number - 1); - } - - static void DoList(CommandSource &source, const MemoInfo *mi, unsigned index) - { - Memo *m = mi->memos[index]; - source.Reply(_("%c%3d %-16s %s"), (m->HasFlag(MF_UNREAD)) ? '*' : ' ', index + 1, m->sender.c_str(), do_strftime(m->time).c_str()); - } -}; - -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 ¶ms) - { - User *u = source.u; - - Anope::string param = !params.empty() ? params[0] : "", chan; - ChannelInfo *ci = NULL; - const MemoInfo *mi; - int i, end; - - if (!param.empty() && param[0] == '#') - { - chan = param; - param = params.size() > 1 ? params[1] : ""; - - if (!(ci = cs_findchan(chan))) - { - source.Reply(CHAN_X_NOT_REGISTERED, chan.c_str()); - return; - } - else if (!ci->HasPriv(u, CA_MEMO)) - { - source.Reply(ACCESS_DENIED); - return; - } - mi = &ci->memos; - } - else - mi = &u->Account()->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 - { - if (!param.empty() && isdigit(param[0])) - { - MemoListCallback list(source, ci, mi, param); - list.Process(); - } - else - { - if (!param.empty()) - { - for (i = 0, end = mi->memos.size(); i < end; ++i) - if (mi->memos[i]->HasFlag(MF_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; - } - } - - bool SentHeader = false; - - for (i = 0, end = mi->memos.size(); i < end; ++i) - { - if (!param.empty() && !mi->memos[i]->HasFlag(MF_UNREAD)) - continue; - - if (!SentHeader) - { - SentHeader = true; - source.Reply(_("New memo for %s."), ci ? ci->name.c_str() : u->nick.c_str()); - source.Reply(_(" Num Sender Date/Time")); - } - - MemoListCallback::DoList(source, mi, i); - } - } - } - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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, CORE), - commandmslist(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandmslist); - } -}; - -MODULE_INIT(MSList) diff --git a/modules/core/ms_main.cpp b/modules/core/ms_main.cpp deleted file mode 100644 index bb6d7847e..000000000 --- a/modules/core/ms_main.cpp +++ /dev/null @@ -1,228 +0,0 @@ -/* MemoServ core functions - * - * (C) 2003-2011 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 "memoserv.h" - -static BotInfo *MemoServ; - -static bool SendMemoMail(NickCore *nc, MemoInfo *mi, Memo *m) -{ - Anope::string message = Anope::printf(translate(nc, _( - "Hi %s\n" - " \n" - "You've just received a new memo from %s. This is memo number %d.\n" - " \n" - "Memo text:\n" - " \n" - "%s")), nc->display.c_str(), m->sender.c_str(), mi->GetIndex(m), m->text.c_str()); - - return Mail(nc, translate(nc, _("New memo")), message); -} - -class MyMemoServService : public MemoServService -{ - public: - MyMemoServService(Module *m) : MemoServService(m) { } - - MemoInfo *GetMemoInfo(const Anope::string &target, bool &ischan) - { - if (!target.empty() && target[0] == '#') - { - ischan = true; - ChannelInfo *ci = cs_findchan(target); - if (ci != NULL) - return &ci->memos; - } - else - { - ischan = false; - NickAlias *na = findnick(target); - if (na != NULL) - return &na->nc->memos; - } - - return NULL; - } - - MemoResult Send(const Anope::string &source, const Anope::string &target, const Anope::string &message, bool force) - { - bool ischan; - MemoInfo *mi = this->GetMemoInfo(target, ischan); - - if (mi == NULL) - return MEMO_INVALID_TARGET; - - User *sender = finduser(source); - if (sender != NULL && !sender->HasPriv("memoserv/no-limit") && !force) - { - if (Config->MSSendDelay > 0 && sender->lastmemosend + Config->MSSendDelay > Anope::CurTime) - return MEMO_TOO_FAST; - else if (!mi->memomax) - return MEMO_TARGET_FULL; - else if (mi->memomax > 0 && mi->memos.size() >= 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(); - mi->memos.push_back(m); - m->sender = source; - m->time = Anope::CurTime; - m->text = message; - m->SetFlag(MF_UNREAD); - - FOREACH_MOD(I_OnMemoSend, OnMemoSend(source, target, mi, m)); - - if (ischan) - { - ChannelInfo *ci = cs_findchan(target); - - if (ci->c) - { - for (CUserList::iterator it = ci->c->users.begin(), it_end = ci->c->users.end(); it != it_end; ++it) - { - UserContainer *cu = *it; - - if (ci->HasPriv(cu->user, CA_MEMO)) - { - if (cu->user->Account() && cu->user->Account()->HasFlag(NI_MEMO_RECEIVE)) - cu->user->SendMessage(MemoServ, MEMO_NEW_X_MEMO_ARRIVED, ci->name.c_str(), Config->UseStrictPrivMsgString.c_str(), Config->MemoServ.c_str(), ci->name.c_str(), mi->memos.size()); - } - } - } - } - else - { - NickCore *nc = findnick(target)->nc; - - if (nc->HasFlag(NI_MEMO_RECEIVE)) - { - for (std::list::iterator it = nc->aliases.begin(), it_end = nc->aliases.end(); it != it_end; ++it) - { - NickAlias *na = *it; - User *user = finduser(na->nick); - if (user && user->IsIdentified()) - user->SendMessage(MemoServ, MEMO_NEW_MEMO_ARRIVED, source.c_str(), Config->UseStrictPrivMsgString.c_str(), Config->MemoServ.c_str(), mi->memos.size()); - } - } - - /* let's get out the mail if set in the nickcore - certus */ - if (nc->HasFlag(NI_MEMO_MAIL)) - SendMemoMail(nc, mi, m); - } - - return MEMO_SUCCESS; - } - - void Check(User *u) - { - NickCore *nc = u->Account(); - if (!nc) - return; - - unsigned i = 0, end = nc->memos.memos.size(), newcnt = 0; - for (; i < end; ++i) - { - if (nc->memos.memos[i]->HasFlag(MF_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() >= nc->memos.memomax) - { - if (nc->memos.memos.size() > 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); - } - } -}; - -class MemoServCore : public Module -{ - MyMemoServService mymemoserv; - public: - MemoServCore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - mymemoserv(this) - { - this->SetAuthor("Anope"); - - MemoServ = findbot(Config->MemoServ); - if (MemoServ == NULL) - throw ModuleException("No bot named " + Config->MemoServ); - - Implementation i[] = { I_OnNickIdentify, I_OnJoinChannel, I_OnUserAway, I_OnNickUpdate, I_OnPreHelp, I_OnPostHelp }; - ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); - - ModuleManager::RegisterService(&this->mymemoserv); - } - - void OnNickIdentify(User *u) - { - this->mymemoserv.Check(u); - } - - void OnJoinChannel(User *u, Channel *c) - { - if (c->ci && c->ci->HasPriv(u, CA_MEMO) && c->ci->memos.memos.size() > 0) - { - 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) - { - if (message.empty()) - this->mymemoserv.Check(u); - } - - void OnNickUpdate(User *u) - { - this->mymemoserv.Check(u); - } - - void OnPreHelp(CommandSource &source, const std::vector ¶ms) - { - if (!params.empty() || source.owner->nick != Config->MemoServ) - return; - 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:"), Config->MemoServ.c_str(), Config->MemoServ.c_str()); - } - - void OnPostHelp(CommandSource &source, const std::vector ¶ms) - { - if (!params.empty() || source.owner->nick != Config->MemoServ) - return; - source.Reply(_(" \n" - "Type \002%s%s HELP \037command\037\002 for help on any of the\n" - "above commands.\n" - "(*) By default, any user with at least level 10 access on a\n" - " channel can read that channel's memos. This can be\n" - " changed with the %s \002LEVELS\002 command."), Config->UseStrictPrivMsgString.c_str(), Config->MemoServ.c_str(), Config->ChanServ.c_str()); - } -}; - -MODULE_INIT(MemoServCore) - diff --git a/modules/core/ms_read.cpp b/modules/core/ms_read.cpp deleted file mode 100644 index f275f8d7d..000000000 --- a/modules/core/ms_read.cpp +++ /dev/null @@ -1,195 +0,0 @@ -/* MemoServ core functions - * - * (C) 2003-2011 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 "memoserv.h" - -void rsend_notify(CommandSource &source, MemoInfo *mi, Memo *m, const Anope::string &targ) -{ - /* Only send receipt if memos are allowed */ - if (memoserv && !readonly) - { - /* Get nick alias for sender */ - NickAlias *na = findnick(m->sender); - - if (!na) - return; - - /* Get nick core for sender */ - NickCore *nc = na->nc; - - if (!nc) - return; - - /* Text of the memo varies if the recepient was a - nick or channel */ - Anope::string text = Anope::printf(translate(na->nc, _("\002[auto-memo]\002 The memo you sent to %s has been viewed.")), targ.c_str()); - - /* Send notification */ - memoserv->Send(source.u->nick, m->sender, text, true); - - /* Notify recepient 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->UnsetFlag(MF_RECEIPT); -} - -class MemoListCallback : public NumberList -{ - CommandSource &source; - MemoInfo *mi; - public: - MemoListCallback(CommandSource &_source, MemoInfo *_mi, const Anope::string &numlist) : NumberList(numlist, false), source(_source), mi(_mi) - { - } - - void HandleNumber(unsigned Number) - { - if (!Number || Number > mi->memos.size()) - return; - - MemoListCallback::DoRead(source, mi, NULL, Number - 1); - } - - static void DoRead(CommandSource &source, MemoInfo *mi, ChannelInfo *ci, unsigned index) - { - Memo *m = mi->memos[index]; - if (ci) - source.Reply(_("Memo %d from %s (%s). To delete, type: \002%s%s DEL %s %d\002"), index + 1, m->sender.c_str(), do_strftime(m->time).c_str(), Config->UseStrictPrivMsgString.c_str(), Config->MemoServ.c_str(), ci->name.c_str(), index + 1); - else - source.Reply(_("Memo %d from %s (%s). To delete, type: \002%s%s DEL %d\002"), index + 1, m->sender.c_str(), do_strftime(m->time).c_str(), Config->UseStrictPrivMsgString.c_str(), Config->MemoServ.c_str(), index + 1); - source.Reply("%s", m->text.c_str()); - m->UnsetFlag(MF_UNREAD); - - /* Check if a receipt notification was requested */ - if (m->HasFlag(MF_RECEIPT)) - rsend_notify(source, mi, m, ci ? ci->name : source.u->nick); - } -}; - -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 ¶ms) - { - User *u = source.u; - - MemoInfo *mi; - ChannelInfo *ci = NULL; - Anope::string numstr = params[0], chan; - - if (!numstr.empty() && numstr[0] == '#') - { - chan = numstr; - numstr = params.size() > 1 ? params[1] : ""; - - if (!(ci = cs_findchan(chan))) - { - source.Reply(CHAN_X_NOT_REGISTERED, chan.c_str()); - return; - } - else if (!ci->HasPriv(u, CA_MEMO)) - { - source.Reply(ACCESS_DENIED); - return; - } - mi = &ci->memos; - } - else - mi = &u->Account()->memos; - - if (numstr.empty() || (!numstr.equals_ci("LAST") && !numstr.equals_ci("NEW") && !numstr.is_number_only())) - 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->memos[i]->HasFlag(MF_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, numstr); - list.Process(); - } - } - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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, CORE), - commandmsread(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandmsread); - } -}; - -MODULE_INIT(MSRead) diff --git a/modules/core/ms_rsend.cpp b/modules/core/ms_rsend.cpp deleted file mode 100644 index 37c633e02..000000000 --- a/modules/core/ms_rsend.cpp +++ /dev/null @@ -1,106 +0,0 @@ -/* MemoServ core functions - * - * (C) 2003-2011 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 "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 ¶ms) - { - User *u = source.u; - - const Anope::string &nick = params[0]; - const Anope::string &text = params[1]; - NickAlias *na = NULL; - - /* prevent user from rsend to themselves */ - if ((na = findnick(nick)) && na->nc == u->Account()) - { - source.Reply(_("You can not request a receipt when sending a memo to yourself.")); - return; - } - - if (Config->MSMemoReceipt == 1 && !u->IsServicesOper()) - source.Reply(ACCESS_DENIED); - else if (Config->MSMemoReceipt > 2 || Config->MSMemoReceipt == 0) - { - Log() << "MSMemoReceipt is set misconfigured to " << Config->MSMemoReceipt; - source.Reply(_("Sorry, RSEND has been disabled on this network.")); - } - else - { - MemoServService::MemoResult result = memoserv->Send(u->nick, 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 SEND command again."), Config->MSSendDelay); - else if (result == MemoServService::MEMO_TARGET_FULL) - source.Reply(_("%s currently has too many memos and cannot receive more."), nick.c_str()); - else - { - source.Reply(_("Memo sent to \002%s\002."), name.c_str()); - - bool ischan; - MemoInfo *mi = memoserv->GetMemoInfo(nick, ischan); - if (mi == NULL) - throw CoreException("NULL mi in ms_rsend"); - Memo *m = (mi->memos.size() ? mi->memos[mi->memos.size() - 1] : NULL); - if (m != NULL) - m->SetFlag(MF_RECEIPT); - } - } - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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, CORE), - commandmsrsend(this) - { - this->SetAuthor("Anope"); - - if (!Config->MSMemoReceipt) - throw ModuleException("Invalid value for memoreceipt"); - - ModuleManager::RegisterService(&commandmsrsend); - } -}; - -MODULE_INIT(MSRSend) diff --git a/modules/core/ms_send.cpp b/modules/core/ms_send.cpp deleted file mode 100644 index 9397e7ad7..000000000 --- a/modules/core/ms_send.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* MemoServ core functions - * - * (C) 2003-2011 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 "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(_("{\037nick\037 | \037channel\037} \037memo-text\037")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - const Anope::string &nick = params[0]; - const Anope::string &text = params[1]; - - MemoServService::MemoResult result = memoserv->Send(source.u->nick, nick, text); - if (result == MemoServService::MEMO_SUCCESS) - source.Reply(_("Memo sent to \002%s\002."), nick.c_str()); - 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 SEND command again."), Config->MSSendDelay); - else if (result == MemoServService::MEMO_TARGET_FULL) - source.Reply(_("%s currently has too many memos and cannot receive more."), nick.c_str()); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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, CORE), - commandmssend(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandmssend); - } -}; - -MODULE_INIT(MSSend) diff --git a/modules/core/ms_sendall.cpp b/modules/core/ms_sendall.cpp deleted file mode 100644 index 7af5ac7af..000000000 --- a/modules/core/ms_sendall.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/* MemoServ core functions - * - * (C) 2003-2011 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 "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 ¶ms) - { - User *u = source.u; - const Anope::string &text = params[0]; - - if (readonly) - { - source.Reply(MEMO_SEND_DISABLED); - return; - } - - NickAlias *na = findnick(u->nick); - - for (nickcore_map::const_iterator it = NickCoreList.begin(), it_end = NickCoreList.end(); it != it_end; ++it) - { - NickCore *nc = it->second; - - if ((na && na->nc == nc) || !nc->display.equals_ci(u->nick)) - memoserv->Send(u->nick, nc->display, text); - } - - source.Reply(_("A massmemo has been sent to all registered users.")); - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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, CORE), - commandmssendall(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandmssendall); - } -}; - -MODULE_INIT(MSSendAll) diff --git a/modules/core/ms_set.cpp b/modules/core/ms_set.cpp deleted file mode 100644 index 1aa3f469d..000000000 --- a/modules/core/ms_set.cpp +++ /dev/null @@ -1,313 +0,0 @@ -/* MemoServ core functions - * - * (C) 2003-2011 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 ¶ms, MemoInfo *mi) - { - User *u = source.u; - const Anope::string ¶m = params[1]; - - if (param.equals_ci("ON")) - { - u->Account()->SetFlag(NI_MEMO_SIGNON); - u->Account()->SetFlag(NI_MEMO_RECEIVE); - source.Reply(_("%s will now notify you of memos when you log on and when they are sent to you."), Config->MemoServ.c_str()); - } - else if (param.equals_ci("LOGON")) - { - u->Account()->SetFlag(NI_MEMO_SIGNON); - u->Account()->UnsetFlag(NI_MEMO_RECEIVE); - source.Reply(_("%s will now notify you of memos when you log on or unset /AWAY."), Config->MemoServ.c_str()); - } - else if (param.equals_ci("NEW")) - { - u->Account()->UnsetFlag(NI_MEMO_SIGNON); - u->Account()->SetFlag(NI_MEMO_RECEIVE); - source.Reply(_("%s will now notify you of memos when they are sent to you."), Config->MemoServ.c_str()); - } - else if (param.equals_ci("MAIL")) - { - if (!u->Account()->email.empty()) - { - u->Account()->SetFlag(NI_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")) - { - u->Account()->UnsetFlag(NI_MEMO_MAIL); - source.Reply(_("You will no longer be informed via email.")); - } - else if (param.equals_ci("OFF")) - { - u->Account()->UnsetFlag(NI_MEMO_SIGNON); - u->Account()->UnsetFlag(NI_MEMO_RECEIVE); - u->Account()->UnsetFlag(NI_MEMO_MAIL); - source.Reply(_("%s will not send you any notification of memos."), Config->MemoServ.c_str()); - } - else - this->OnSyntaxError(source, param); - - return; - } - - void DoLimit(CommandSource &source, const std::vector ¶ms, MemoInfo *mi) - { - User *u = source.u; - - 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 limit; - NickCore *nc = u->Account(); - ChannelInfo *ci = NULL; - bool is_servadmin = u->HasPriv("memoserv/set-limit"); - - if (p1[0] == '#') - { - chan = p1; - p1 = p2; - p2 = p3; - p3 = params.size() > 4 ? params[4] : ""; - if (!(ci = cs_findchan(chan))) - { - source.Reply(CHAN_X_NOT_REGISTERED, chan.c_str()); - return; - } - else if (!is_servadmin && !ci->HasPriv(u, CA_MEMO)) - { - source.Reply(ACCESS_DENIED); - return; - } - mi = &ci->memos; - } - if (is_servadmin) - { - if (!p2.empty() && !p2.equals_ci("HARD") && chan.empty()) - { - NickAlias *na; - if (!(na = findnick(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->SetFlag(CI_MEMO_HARDMAX); - else - ci->UnsetFlag(CI_MEMO_HARDMAX); - } - else - { - if (!p2.empty()) - nc->SetFlag(NI_MEMO_HARDMAX); - else - nc->UnsetFlag(NI_MEMO_HARDMAX); - } - limit = -1; - try - { - limit = convertTo(p1); - } - catch (const ConvertException &) { } - } - else - { - if (p1.empty() || !p2.empty() || !isdigit(p1[0])) - { - this->OnSyntaxError(source, ""); - return; - } - if (!chan.empty() && ci->HasFlag(CI_MEMO_HARDMAX)) - { - source.Reply(_("The memo limit for %s may not be changed."), chan.c_str()); - return; - } - else if (chan.empty() && nc->HasFlag(NI_MEMO_HARDMAX)) - { - source.Reply(_("You are not permitted to change your memo limit.")); - return; - } - limit = -1; - try - { - limit = convertTo(p1); - } - catch (const ConvertException &) { } - /* The first character is a digit, but we could still go negative - * from overflow... watch out! */ - if (limit < 0 || (Config->MSMaxMemos > 0 && static_cast(limit) > Config->MSMaxMemos)) - { - if (!chan.empty()) - source.Reply(_("You cannot set the memo limit for %s higher than %d."), chan.c_str(), Config->MSMaxMemos); - else - source.Reply(_("You cannot set your memo limit higher than %d."), Config->MSMaxMemos); - return; - } - } - mi->memomax = limit; - if (limit > 0) - { - if (chan.empty() && nc == u->Account()) - 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 == u->Account()) - 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 == u->Account()) - 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 ¶ms) - { - User *u = source.u; - const Anope::string &cmd = params[0]; - MemoInfo *mi = &u->Account()->memos; - - if (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 - { - source.Reply(NICK_SET_UNKNOWN_OPTION, Config->UseStrictPrivMsgString.c_str(), cmd.c_str()); - source.Reply(MORE_INFO, Config->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str(), "SET"); - } - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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->UseStrictPrivMsgString.c_str(), source.owner->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" - "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 aswell 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")) - { - User *u = source.u; - if (u->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" - "This use of the \002SET LIMIT\002 command is limited to \002Services\002\n" - "\002admins\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."), Config->MSMaxMemos); - 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."), Config->MSMaxMemos); - } - else - return false; - - return true; - } -}; - -class MSSet : public Module -{ - CommandMSSet commandmsset; - - public: - MSSet(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandmsset(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandmsset); - } -}; - -MODULE_INIT(MSSet) diff --git a/modules/core/ms_staff.cpp b/modules/core/ms_staff.cpp deleted file mode 100644 index 6264dbea4..000000000 --- a/modules/core/ms_staff.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* MemoServ core functions - * - * (C) 2003-2011 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 "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 ¶ms) - { - const Anope::string &text = params[0]; - - if (readonly) - { - source.Reply(MEMO_SEND_DISABLED); - return; - } - - for (nickcore_map::const_iterator it = NickCoreList.begin(), it_end = NickCoreList.end(); it != it_end; ++it) - { - NickCore *nc = it->second; - - if (nc->IsServicesOper()) - memoserv->Send(source.u->nick, nc->display, text, true); - } - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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, CORE), - commandmsstaff(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandmsstaff); - } -}; - -MODULE_INIT(MSStaff) diff --git a/modules/core/nickserv.h b/modules/core/nickserv.h deleted file mode 100644 index b58dbd65f..000000000 --- a/modules/core/nickserv.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef NICKSERV_H -#define NICKSERV_H - -class NickServService : public Service -{ - public: - NickServService(Module *m) : Service(m, "NickServ") { } - - virtual void Validate(User *u) = 0; -}; - -static service_reference nickserv("NickServ"); - -#endif // NICKSERV_H - diff --git a/modules/core/ns_access.cpp b/modules/core/ns_access.cpp deleted file mode 100644 index df9ae6eed..000000000 --- a/modules/core/ns_access.cpp +++ /dev/null @@ -1,195 +0,0 @@ -/* NickServ core functions - * - * (C) 2003-2011 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 DoServAdminList(CommandSource &source, const std::vector ¶ms, NickCore *nc) - { - Anope::string mask = params.size() > 2 ? params[2] : ""; - unsigned i, end; - - if (nc->access.empty()) - { - source.Reply(_("Access list for \002%s\002 is empty."), nc->display.c_str()); - return; - } - - if (nc->HasFlag(NI_SUSPENDED)) - { - source.Reply(NICK_X_SUSPENDED, nc->display.c_str()); - return; - } - - source.Reply(_("Access list for \002%s\002:"), params[1].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; - } - - void DoAdd(CommandSource &source, NickCore *nc, const Anope::string &mask) - { - - if (mask.empty()) - { - this->OnSyntaxError(source, "ADD"); - return; - } - - if (nc->access.size() >= Config->NSAccessMax) - { - source.Reply(_("Sorry, you can only have %d access entries for a nickname."), Config->NSAccessMax); - return; - } - - if (nc->FindAccess(mask)) - { - source.Reply(_("Mask \002%s\002 already present on your access list."), mask.c_str()); - return; - } - - nc->AddAccess(mask); - source.Reply(_("\002%s\002 added to your access list."), mask.c_str()); - - return; - } - - void DoDel(CommandSource &source, NickCore *nc, const Anope::string &mask) - { - if (mask.empty()) - { - this->OnSyntaxError(source, "DEL"); - return; - } - - if (!nc->FindAccess(mask)) - { - source.Reply(_("\002%s\002 not found on your access list."), mask.c_str()); - return; - } - - source.Reply(_("\002%s\002 deleted from your access list."), mask.c_str()); - nc->EraseAccess(mask); - - return; - } - - void DoList(CommandSource &source, NickCore *nc, const Anope::string &mask) - { - User *u = source.u; - unsigned i, end; - - if (nc->access.empty()) - { - source.Reply(_("Your access list is empty."), u->nick.c_str()); - return; - } - - source.Reply(_("Access list:")); - 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 \037mask\037")); - this->SetSyntax(_("DEL \037mask\037")); - this->SetSyntax(_("LIST")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - const Anope::string &cmd = params[0]; - const Anope::string &mask = params.size() > 1 ? params[1] : ""; - - NickAlias *na; - if (cmd.equals_ci("LIST") && u->IsServicesOper() && !mask.empty() && (na = findnick(params[1]))) - return this->DoServAdminList(source, params, na->nc); - - if (!mask.empty() && mask.find('@') == Anope::string::npos) - { - source.Reply(BAD_USERHOST_MASK); - source.Reply(MORE_INFO, Config->UseStrictPrivMsgString.c_str(), Config->NickServ.c_str(), this->name.c_str()); - } - else if (u->Account()->HasFlag(NI_SUSPENDED)) - source.Reply(NICK_X_SUSPENDED, u->Account()->display.c_str()); - else if (cmd.equals_ci("ADD")) - return this->DoAdd(source, u->Account(), mask); - else if (cmd.equals_ci("DEL")) - return this->DoDel(source, u->Account(), mask); - else if (cmd.equals_ci("LIST")) - return this->DoList(source, u->Account(), mask); - else - this->OnSyntaxError(source, ""); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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.\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."), Config->NickServ.c_str(), Config->NickServ.c_str()); - return true; - } -}; - -class NSAccess : public Module -{ - CommandNSAccess commandnsaccess; - - public: - NSAccess(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandnsaccess(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandnsaccess); - } -}; - -MODULE_INIT(NSAccess) diff --git a/modules/core/ns_ajoin.cpp b/modules/core/ns_ajoin.cpp deleted file mode 100644 index 4f35f28b8..000000000 --- a/modules/core/ns_ajoin.cpp +++ /dev/null @@ -1,239 +0,0 @@ -/* NickServ core functions - * - * (C) 2003-2011 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 CommandNSAJoin : public Command -{ - void DoList(CommandSource &source, const std::vector ¶ms) - { - std::vector > channels; - source.u->Account()->GetExtRegular("ns_ajoin_channels", channels); - - if (channels.empty()) - source.Reply(_("Your auto join list is empty.")); - else - { - source.Reply(_("Your auto join list:\n" - " Num Channel Key")); - for (unsigned i = 0; i < channels.size(); ++i) - source.Reply(" %3d %-12s %s", i + 1, channels[i].first.c_str(), channels[i].second.c_str()); - } - } - - void DoAdd(CommandSource &source, const std::vector ¶ms) - { - std::vector > channels; - source.u->Account()->GetExtRegular("ns_ajoin_channels", channels); - - if (channels.size() >= Config->AJoinMax) - source.Reply(_("Your auto join list is full.")); - else if (ircdproto->IsChannelValid(params[1]) == false) - source.Reply(CHAN_X_INVALID, params[1].c_str()); - else - { - channels.push_back(std::make_pair(params[1], params.size() > 2 ? params[2] : "")); - source.Reply(_("Added %s to your auto join list."), params[1].c_str()); - source.u->Account()->Extend("ns_ajoin_channels", new ExtensibleItemRegular > >(channels)); - } - } - - void DoDel(CommandSource &source, const std::vector ¶ms) - { - std::vector > channels; - source.u->Account()->GetExtRegular("ns_ajoin_channels", channels); - - unsigned i; - for (i = 0; i < channels.size(); ++i) - if (channels[i].first.equals_ci(params[1])) - break; - - if (i == channels.size()) - source.Reply(_("%s was not found on your auto join list."), params[1].c_str()); - else - { - channels.erase(channels.begin() + i); - source.u->Account()->Extend("ns_ajoin_channels", new ExtensibleItemRegular > >(channels)); - source.Reply(_("%s was removed from your auto join list."), params[1].c_str()); - } - } - - public: - CommandNSAJoin(Module *creator) : Command(creator, "nickserv/ajoin", 1, 3) - { - this->SetDesc(_("Manage your auto join list")); - this->SetSyntax(_("{ADD | DEL | LIST} [\037channel\037] [\037key\037]")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - if (params[0].equals_ci("LIST")) - this->DoList(source, params); - else if (params.size() < 2) - this->OnSyntaxError(source, ""); - else if (params[0].equals_ci("ADD")) - this->DoAdd(source, params); - else if (params[0].equals_ci("DEL")) - this->DoDel(source, params); - else - this->OnSyntaxError(source, ""); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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")); - return true; - } -}; - -class NSAJoin : public Module -{ - CommandNSAJoin commandnsajoin; - - public: - NSAJoin(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandnsajoin(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandnsajoin); - - Implementation i[] = { I_OnNickIdentify, I_OnDatabaseWriteMetadata, I_OnDatabaseReadMetadata }; - ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); - } - - void OnNickIdentify(User *u) - { - std::vector > channels; - u->Account()->GetExtRegular("ns_ajoin_channels", channels); - - for (unsigned i = 0; i < channels.size(); ++i) - { - Channel *c = findchan(channels[i].first); - ChannelInfo *ci = c != NULL ? c->ci : cs_findchan(channels[i].first); - if (c == NULL && ci != NULL) - c = ci->c; - - bool need_invite = false; - Anope::string key = channels[i].second; - - if (ci != NULL) - { - if (ci->HasFlag(CI_SUSPENDED)) - continue; - } - if (c != NULL) - { - if (c->FindUser(u) != NULL) - continue; - else if (c->HasMode(CMODE_OPERONLY) && !u->HasMode(UMODE_OPER)) - continue; - else if (c->HasMode(CMODE_ADMINONLY) && !u->HasMode(UMODE_ADMIN)) - continue; - else if (c->HasMode(CMODE_SSL) && !u->HasMode(UMODE_SSL)) - continue; - else if (matches_list(c, u, CMODE_BAN) == true && matches_list(c, u, CMODE_EXCEPT) == false) - need_invite = true; - else if (c->HasMode(CMODE_INVITE) && matches_list(c, u, CMODE_INVITEOVERRIDE) == false) - need_invite = true; - - if (c->HasMode(CMODE_KEY)) - { - Anope::string k; - if (c->GetParam(CMODE_KEY, k)) - { - if (ci->HasPriv(u, CA_GETKEY)) - key = k; - else if (key != k) - need_invite = true; - } - } - if (c->HasMode(CMODE_LIMIT)) - { - Anope::string l; - if (c->GetParam(CMODE_LIMIT, l)) - { - try - { - unsigned limit = convertTo(l); - if (c->users.size() >= limit) - need_invite = true; - } - catch (const ConvertException &) { } - } - } - } - - if (need_invite) - { - BotInfo *bi = findbot(Config->NickServ); - if (!bi || !ci->HasPriv(u, CA_INVITE)) - continue; - ircdproto->SendInvite(bi, channels[i].first, u->nick); - } - - ircdproto->SendSVSJoin(Config->NickServ, u->nick, channels[i].first, key); - } - } - - void OnDatabaseWriteMetadata(void (*WriteMetadata)(const Anope::string &, const Anope::string &), NickCore *nc) - { - std::vector > channels; - nc->GetExtRegular("ns_ajoin_channels", channels); - - Anope::string chans; - for (unsigned i = 0; i < channels.size(); ++i) - chans += " " + channels[i].first + "," + channels[i].second; - - if (!chans.empty()) - { - chans.erase(chans.begin()); - WriteMetadata("NS_AJOIN", chans); - } - } - - EventReturn OnDatabaseReadMetadata(NickCore *nc, const Anope::string &key, const std::vector ¶ms) - { - if (key == "NS_AJOIN") - { - std::vector > channels; - nc->GetExtRegular("ns_ajoin_channels", channels); - - for (unsigned i = 0; i < params.size(); ++i) - { - Anope::string chan, chankey; - commasepstream sep(params[i]); - sep.GetToken(chan); - sep.GetToken(chankey); - - channels.push_back(std::make_pair(chan, chankey)); - } - - nc->Extend("ns_ajoin_channels", new ExtensibleItemRegular > >(channels)); - - return EVENT_STOP; - } - - return EVENT_CONTINUE; - } -}; - -MODULE_INIT(NSAJoin) diff --git a/modules/core/ns_alist.cpp b/modules/core/ns_alist.cpp deleted file mode 100644 index f502b4344..000000000 --- a/modules/core/ns_alist.cpp +++ /dev/null @@ -1,96 +0,0 @@ -/* NickServ core functions - * - * (C) 2003-2011 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 -{ - 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 ¶ms) - { - User *u = source.u; - Anope::string nick = u->Account()->display; - - if (params.size() && u->IsServicesOper()) - nick = params[0]; - - NickAlias *na = findnick(nick); - - if (!na) - source.Reply(NICK_X_NOT_REGISTERED, nick.c_str()); - else - { - int chan_count = 0; - - source.Reply(_("Channels that \002%s\002 has access on:\n" - " Num Channel Level"), na->nick.c_str()); - - for (registered_channel_map::const_iterator it = RegisteredChannelList.begin(), it_end = RegisteredChannelList.end(); it != it_end; ++it) - { - ChannelInfo *ci = it->second; - - AccessGroup access = ci->AccessFor(na->nc); - if (access.empty()) - continue; - - ++chan_count; - - if (access.size() > 1) - { - source.Reply(_(" %3d You match %d access entries on %c%s, they are"), chan_count, access.size(), ci->HasFlag(CI_NO_EXPIRE) ? '!' : ' ', ci->name.c_str()); - for (unsigned i = 0; i < access.size(); ++i) - source.Reply(_(" %3d %-8s"), i + 1, access[i]->Serialize().c_str()); - } - else - source.Reply(_(" %3d %c%-20s %-8s"), chan_count, ci->HasFlag(CI_NO_EXPIRE) ? '!' : ' ', ci->name.c_str(), access[0]->Serialize().c_str()); - } - - source.Reply(_("End of list - %d channels shown."), chan_count); - } - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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, CORE), - commandnsalist(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandnsalist); - } -}; - -MODULE_INIT(NSAList) diff --git a/modules/core/ns_cert.cpp b/modules/core/ns_cert.cpp deleted file mode 100644 index fbd75d597..000000000 --- a/modules/core/ns_cert.cpp +++ /dev/null @@ -1,231 +0,0 @@ -/* NickServ core functions - * - * (C) 2003-2011 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 CommandNSCert : public Command -{ - private: - void DoServAdminList(CommandSource &source, NickCore *nc) - { - if (nc->cert.empty()) - { - source.Reply(_("Certificate list for \002%s\002 is empty."), nc->display.c_str()); - return; - } - - if (nc->HasFlag(NI_SUSPENDED)) - { - source.Reply(NICK_X_SUSPENDED, nc->display.c_str()); - return; - } - - source.Reply(_("Certificate list for \002%s\002:"), nc->display.c_str()); - for (unsigned i = 0, end = nc->cert.size(); i < end; ++i) - { - Anope::string fingerprint = nc->GetCert(i); - source.Reply(" %s", fingerprint.c_str()); - } - - return; - } - - void DoAdd(CommandSource &source, NickCore *nc, const Anope::string &mask) - { - - if (nc->cert.size() >= Config->NSAccessMax) - { - source.Reply(_("Sorry, you can only have %d certificate entries for a nickname."), Config->NSAccessMax); - return; - } - - if (!source.u->fingerprint.empty() && !nc->FindCert(source.u->fingerprint)) - { - nc->AddCert(source.u->fingerprint); - source.Reply(_("\002%s\002 added to your certificate list."), source.u->fingerprint.c_str()); - return; - } - - if (mask.empty()) - { - this->OnSyntaxError(source, "ADD"); - return; - } - - if (nc->FindCert(mask)) - { - source.Reply(_("Fingerprint \002%s\002 already present on your certificate list."), mask.c_str()); - return; - } - - nc->AddCert(mask); - source.Reply(_("\002%s\002 added to your certificate list."), mask.c_str()); - return; - } - - void DoDel(CommandSource &source, NickCore *nc, const Anope::string &mask) - { - - if (!source.u->fingerprint.empty() && nc->FindCert(source.u->fingerprint)) - { - nc->EraseCert(source.u->fingerprint); - source.Reply(_("\002%s\002 deleted from your certificate list."), source.u->fingerprint.c_str()); - return; - } - - if (mask.empty()) - { - this->OnSyntaxError(source, "DEL"); - return; - } - - if (!nc->FindCert(mask)) - { - source.Reply(_("\002%s\002 not found on your certificate list."), mask.c_str()); - return; - } - - source.Reply(_("\002%s\002 deleted from your certificate list."), mask.c_str()); - nc->EraseCert(mask); - - return; - } - - void DoList(CommandSource &source, NickCore *nc) - { - User *u = source.u; - - if (nc->cert.empty()) - { - source.Reply(_("Your certificate list is empty."), u->nick.c_str()); - return; - } - - source.Reply(_("Cert list:")); - for (unsigned i = 0, end = nc->cert.size(); i < end; ++i) - { - Anope::string fingerprint = nc->GetCert(i); - source.Reply(" %s", fingerprint.c_str()); - } - - return; - } - - public: - CommandNSCert(Module *creator) : Command(creator, "nickserv/cert", 1, 2) - { - this->SetDesc("Modify the nickname client certificate list"); - this->SetSyntax("ADD \037fingerprint\037"); - this->SetSyntax("DEL \037fingerprint\037"); - this->SetSyntax("LIST"); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - const Anope::string &cmd = params[0]; - const Anope::string &mask = params.size() > 1 ? params[1] : ""; - - NickAlias *na; - if (cmd.equals_ci("LIST") && u->IsServicesOper() && !mask.empty() && (na = findnick(mask))) - return this->DoServAdminList(source, na->nc); - - if (u->Account()->HasFlag(NI_SUSPENDED)) - source.Reply(NICK_X_SUSPENDED, u->Account()->display.c_str()); - else if (cmd.equals_ci("ADD")) - return this->DoAdd(source, u->Account(), mask); - else if (cmd.equals_ci("DEL")) - return this->DoDel(source, u->Account(), mask); - else if (cmd.equals_ci("LIST")) - return this->DoList(source, u->Account()); - else - this->OnSyntaxError(source, cmd); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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, your nick will be\n" - "automatically identified to %s.\n" - " \n"), Config->NickServ.c_str(), Config->NickServ.c_str()); - source.Reply(_("Examples:\n" - " \n" - " \002CERT ADD \002\n" - " Adds this fingerprint to the certificate list and\n" - " automatically identifies you when you connect to IRC\n" - " using this certificate.\n" - " \n" - " \002CERT DEL \002\n" - " Reverses the previous command.\n" - " \n" - " \002CERT LIST\002\n" - " Displays the current certificate list."), Config->NickServ.c_str()); - return true; - } -}; - -class NSCert : public Module -{ - CommandNSCert commandnscert; - - void DoAutoIdentify(User *u) - { - BotInfo *bi = findbot(Config->NickServ); - NickAlias *na = findnick(u->nick); - if (!bi || !na) - return; - if (u->IsIdentified() && u->Account() == na->nc) - return; - if (na->nc->HasFlag(NI_SUSPENDED)) - return; - if (!na->nc->FindCert(u->fingerprint)) - return; - - u->Identify(na); - u->SendMessage(bi, _("SSL Fingerprint accepted. You are now identified.")); - return; - } - - public: - NSCert(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandnscert(this) - { - this->SetAuthor("Anope"); - - if (!ircd || !ircd->certfp) - throw ModuleException("Your IRCd does not support ssl client certificates"); - - Implementation i[] = { I_OnUserNickChange, I_OnFingerprint }; - ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); - - ModuleManager::RegisterService(&commandnscert); - } - - void OnFingerprint(User *u) - { - DoAutoIdentify(u); - } - - void OnUserNickChange(User *u, const Anope::string &oldnick) - { - if (!u->fingerprint.empty()) - DoAutoIdentify(u); - } -}; - -MODULE_INIT(NSCert) diff --git a/modules/core/ns_drop.cpp b/modules/core/ns_drop.cpp deleted file mode 100644 index 7960652ff..000000000 --- a/modules/core/ns_drop.cpp +++ /dev/null @@ -1,130 +0,0 @@ -/* NickServ core functions - * - * (C) 2003-2011 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", 0, 1) - { - this->SetFlag(CFLAG_ALLOW_UNREGISTERED); - this->SetDesc(_("Cancel the registration of a nickname")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - Anope::string nick = !params.empty() ? params[0] : ""; - - if (readonly) - { - source.Reply(_("Sorry, nickname de-registration is temporarily disabled.")); - return; - } - - NickAlias *na = findnick((u->Account() && !nick.empty() ? nick : u->nick)); - if (!na) - { - source.Reply(NICK_NOT_REGISTERED); - return; - } - - if (!u->Account()) - { - source.Reply(NICK_IDENTIFY_REQUIRED, Config->UseStrictPrivMsgString.c_str(), Config->NickServ.c_str()); - return; - } - - bool is_mine = u->Account() == na->nc; - Anope::string my_nick; - if (is_mine && nick.empty()) - my_nick = na->nick; - - if (!is_mine && !u->HasPriv("nickserv/drop")) - source.Reply(ACCESS_DENIED); - else if (Config->NSSecureAdmins && !is_mine && na->nc->IsServicesOper()) - source.Reply(ACCESS_DENIED); - else - { - if (readonly) - source.Reply(READ_ONLY_MODE); - - FOREACH_MOD(I_OnNickDrop, OnNickDrop(u, na)); - - Log(!is_mine ? LOG_OVERRIDE : LOG_COMMAND, u, this) << "to drop nickname " << na->nick << " (group: " << na->nc->display << ") (email: " << (!na->nc->email.empty() ? na->nc->email : "none") << ")"; - delete na; - - if (!is_mine) - { - source.Reply(_("Nickname \002%s\002 has been dropped."), nick.c_str()); - } - else - { - if (!nick.empty()) - source.Reply(_("Nickname \002%s\002 has been dropped."), nick.c_str()); - else - source.Reply(_("Your nickname has been dropped.")); - } - } - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - User *u = source.u; - if (u->Account() && u->HasPriv("nickserv/drop")) - source.Reply(_("Syntax: \002%s [\037nickname\037]\002\n" - " \n" - "Without a parameter, deletes your nickname.\n" - " \n" - "With a parameter, drops the named nick from the database.\n" - "You may drop any nick within your group without any \n" - "special privileges. Dropping any nick is limited to \n" - "\002Services Operators\002."), source.command.c_str()); - else - source.Reply(_("Syntax: \002%s [\037nickname\037 | \037password\037]\002\n" - " \n" - "Deltes your nickname. A nick\n" - "that has been dropped is free for anyone to re-register.\n" - " \n" - "You may drop a nick within your group by passing it\n" - "as the \002nick\002 parameter.\n" - " \n" - "If you have a nickname registration pending but can not confirm\n" - "it for any reason, you can cancel your registration by passing\n" - "your password as the \002password\002 parameter.\n" - " \n" - "In order to use this command, you must first identify\n" - "with your password."), source.command.c_str()); - - return true; - } -}; - -class NSDrop : public Module -{ - CommandNSDrop commandnsdrop; - - public: - NSDrop(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandnsdrop(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandnsdrop); - } -}; - -MODULE_INIT(NSDrop) diff --git a/modules/core/ns_getemail.cpp b/modules/core/ns_getemail.cpp deleted file mode 100644 index fa27e5174..000000000 --- a/modules/core/ns_getemail.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/* NickServ core functions - * - * (C) 2003-2011 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(_("\037user@email-host\037")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - const Anope::string &email = params[0]; - int j = 0; - - Log(LOG_ADMIN, u, this) << "on " << email; - - for (nickcore_map::const_iterator it = NickCoreList.begin(), it_end = NickCoreList.end(); it != it_end; ++it) - { - NickCore *nc = it->second; - - if (!nc->email.empty() && nc->email.equals_ci(email)) - { - ++j; - source.Reply(_("Emails Match \002%s\002 to \002%s\002."), nc->display.c_str(), email.c_str()); - } - } - - if (j <= 0) - { - source.Reply(_("No Emails listed for \002%s\002."), email.c_str()); - return; - } - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Returns the matching nicks that used given email. \002Note\002 that\n" - "you can not use wildcards for either user or emailhost. Whenever\n" - "this command is used, a message including the person who issued\n" - "the command and the email it was used on will be logged.")); - return true; - } -}; - -class NSGetEMail : public Module -{ - CommandNSGetEMail commandnsgetemail; - public: - NSGetEMail(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandnsgetemail(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandnsgetemail); - } -}; - -MODULE_INIT(NSGetEMail) diff --git a/modules/core/ns_getpass.cpp b/modules/core/ns_getpass.cpp deleted file mode 100644 index 9ebea5263..000000000 --- a/modules/core/ns_getpass.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/* NickServ core functions - * - * (C) 2003-2011 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 ¶ms) - { - User *u = source.u; - const Anope::string &nick = params[0]; - Anope::string tmp_pass; - NickAlias *na; - - if (!(na = findnick(nick))) - source.Reply(NICK_X_NOT_REGISTERED, nick.c_str()); - else if (Config->NSSecureAdmins && na->nc->IsServicesOper()) - source.Reply(ACCESS_DENIED); - else - { - if (enc_decrypt(na->nc->pass, tmp_pass) == 1) - { - Log(LOG_ADMIN, u, 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) - { - 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, CORE), - commandnsgetpass(this) - { - this->SetAuthor("Anope"); - - Anope::string tmp_pass = "plain:tmp"; - if (enc_decrypt(tmp_pass, tmp_pass) == -1) - throw ModuleException("Incompatible with the encryption module being used"); - - ModuleManager::RegisterService(&commandnsgetpass); - } -}; - -MODULE_INIT(NSGetPass) diff --git a/modules/core/ns_ghost.cpp b/modules/core/ns_ghost.cpp deleted file mode 100644 index 37dcef9c7..000000000 --- a/modules/core/ns_ghost.cpp +++ /dev/null @@ -1,122 +0,0 @@ -/* NickServ core functions - * - * (C) 2003-2011 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 CommandNSGhost : public Command -{ - public: - CommandNSGhost(Module *creator) : Command(creator, "nickserv/ghost", 1, 2) - { - this->SetFlag(CFLAG_ALLOW_UNREGISTERED); - this->SetDesc(_("Disconnects a \"ghost\" IRC session using your nick")); - this->SetSyntax("\037nickname\037 [\037password\037]"); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - const Anope::string &nick = params[0]; - const Anope::string &pass = params.size() > 1 ? params[1] : ""; - - User *u = source.u; - User *user = finduser(nick); - NickAlias *na = findnick(nick); - - if (!user) - source.Reply(NICK_X_NOT_IN_USE, nick.c_str()); - else if (!na) - source.Reply(NICK_X_NOT_REGISTERED, nick.c_str()); - else if (na->nc->HasFlag(NI_SUSPENDED)) - source.Reply(NICK_X_SUSPENDED, na->nick.c_str()); - else if (nick.equals_ci(u->nick)) - source.Reply(_("You can't ghost yourself!")); - else - { - bool ok = false; - if (u->Account() == na->nc) - ok = true; - else if (!na->nc->HasFlag(NI_SECURE) && is_on_access(u, na->nc)) - ok = true; - else if (!u->fingerprint.empty() && na->nc->FindCert(u->fingerprint)) - ok = true; - else if (!pass.empty()) - { - EventReturn MOD_RESULT; - FOREACH_RESULT(I_OnCheckAuthentication, OnCheckAuthentication(this, &source, params, na->nc->display, pass)); - if (MOD_RESULT == EVENT_STOP) - return; - else if (MOD_RESULT == EVENT_ALLOW) - ok = true; - } - - if (ok) - { - if (!user->IsIdentified()) - source.Reply(_("You may not ghost an unidentified user, use RECOVER instead.")); - else - { - Log(LOG_COMMAND, u, this) << "for " << nick; - Anope::string buf = "GHOST command used by " + u->nick; - user->Kill(Config->NickServ, buf); - source.Reply(_("Ghost with your nick has been killed."), nick.c_str()); - } - } - else - { - source.Reply(ACCESS_DENIED); - if (!pass.empty()) - { - Log(LOG_COMMAND, u, this) << "with an invalid password for " << nick; - bad_password(u); - } - } - } - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("itermminates a \"ghost\" IRC session using your nick. A\n" - "ghost\" session is one which is not actually connected,\n" - "but which the IRC server believes is still online for one\n" - "reason or another. Typically, this happens if your\n" - "computer crashes or your Internet or modem connection\n" - "goes down while you're on IRC.\n" - " \n" - "In order to use the \002GHOST\002 command for a nick, your\n" - "current address as shown in /WHOIS must be on that nick's\n" - "access list, you must be identified and in the group of\n" - "that nick, or you must supply the correct password for\n" - "the nickname.")); - return true; - } -}; - -class NSGhost : public Module -{ - CommandNSGhost commandnsghost; - - public: - NSGhost(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandnsghost(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandnsghost); - } -}; - -MODULE_INIT(NSGhost) diff --git a/modules/core/ns_group.cpp b/modules/core/ns_group.cpp deleted file mode 100644 index 2e23e1f51..000000000 --- a/modules/core/ns_group.cpp +++ /dev/null @@ -1,305 +0,0 @@ -/* NickServ core functions - * - * (C) 2003-2011 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 CommandNSGroup : public Command -{ - public: - CommandNSGroup(Module *creator) : Command(creator, "nickserv/group", 1, 2) - { - this->SetFlag(CFLAG_ALLOW_UNREGISTERED); - this->SetDesc(_("Join a group")); - this->SetSyntax(_("\037target\037 \037password\037")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - - const Anope::string &nick = params[0]; - const Anope::string &pass = params.size() > 1 ? params[1] : ""; - - if (readonly) - { - source.Reply(_("Sorry, nickname grouping is temporarily disabled.")); - return; - } - - if (Config->RestrictOperNicks) - for (unsigned i = 0; i < Config->Opers.size(); ++i) - { - Oper *o = Config->Opers[i]; - - if (!u->HasMode(UMODE_OPER) && u->nick.find_ci(o->name) != Anope::string::npos) - { - source.Reply(NICK_CANNOT_BE_REGISTERED, u->nick.c_str()); - return; - } - } - - NickAlias *target, *na = findnick(u->nick); - if (!(target = findnick(nick))) - source.Reply(NICK_X_NOT_REGISTERED, nick.c_str()); - else if (Anope::CurTime < u->lastnickreg + Config->NSRegDelay) - source.Reply(_("Please wait %d seconds before using the GROUP command again."), (Config->NSRegDelay + u->lastnickreg) - Anope::CurTime); - else if (target && target->nc->HasFlag(NI_SUSPENDED)) - { - Log(LOG_COMMAND, u, this) << "tried to use GROUP for SUSPENDED nick " << target->nick; - source.Reply(NICK_X_SUSPENDED, target->nick.c_str()); - } - 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, Config->UseStrictPrivMsgString.c_str(), Config->NickServ.c_str()); - else if (na && Config->NSNoGroupChange) - source.Reply(_("Your nick is already registered.")); - else if (Config->NSMaxAliases && (target->nc->aliases.size() >= Config->NSMaxAliases) && !target->nc->IsServicesOper()) - source.Reply(_("There are too many nicks in %s's group.")); - else - { - bool ok = false; - if (!na && u->Account()) - ok = true; - else if (!u->fingerprint.empty() && target->nc->FindCert(u->fingerprint)) - ok = true; - else if (!pass.empty()) - { - EventReturn MOD_RESULT; - FOREACH_RESULT(I_OnCheckAuthentication, OnCheckAuthentication(this, &source, params, target->nc->display, pass)); - if (MOD_RESULT == EVENT_STOP) - return; - else if (MOD_RESULT == EVENT_ALLOW) - ok = true; - } - if (ok) - { - /* If the nick is already registered, drop it. - * If not, check that it is valid. - */ - if (na) - delete na; - else - { - size_t prefixlen = Config->NSGuestNickPrefix.length(); - size_t nicklen = u->nick.length(); - - if (nicklen <= prefixlen + 7 && nicklen >= prefixlen + 1 && !u->nick.find_ci(Config->NSGuestNickPrefix) && !u->nick.substr(prefixlen).find_first_not_of("1234567890")) - { - source.Reply(NICK_CANNOT_BE_REGISTERED, u->nick.c_str()); - return; - } - } - - na = new NickAlias(u->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(na->nc); - FOREACH_MOD(I_OnNickGroup, OnNickGroup(u, target)); - if (target->nc->HasFlag(NI_UNCONFIRMED) == false) - ircdproto->SendAccountLogin(u, u->Account()); - ircdproto->SetAutoIdentificationToken(u); - - Log(LOG_COMMAND, u, this) << "makes " << u->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; - } - else - { - Log(LOG_COMMAND, u, this) << "failed group for " << target->nick; - source.Reply(PASSWORD_INCORRECT); - bad_password(u); - } - } - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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]")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - Anope::string nick = !params.empty() ? params[0] : ""; - NickAlias *na = findnick(!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(_("The nick %s is not in your group."), na->nick.c_str()); - else - { - NickCore *oldcore = na->nc; - - std::list::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)) - change_core_display(oldcore); - - na->nc = new NickCore(na->nick); - na->nc->aliases.push_back(na); - - na->nc->pass = oldcore->pass; - if (!oldcore->email.empty()) - na->nc->email = oldcore->email; - if (!oldcore->greet.empty()) - na->nc->greet = oldcore->greet; - na->nc->language = oldcore->language; - - source.Reply(_("Nick %s has been ungrouped from %s."), na->nick.c_str(), oldcore->display.c_str()); - - User *user = finduser(na->nick); - if (user) - /* The user on the nick who was ungrouped may be identified to the old group, set -r */ - user->RemoveMode(findbot(Config->NickServ), UMODE_REGISTERED); - } - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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, url, and icq. Everything\n" - "else is reset. You may not ungroup yourself if there is only one\n" - "nick 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 ¶ms) - { - User *u = source.u; - Anope::string nick = !params.empty() ? params[0] : ""; - - const NickCore *nc = u->Account(); - - if (!nick.empty() && (!nick.equals_ci(u->nick) && !u->IsServicesOper())) - source.Reply(ACCESS_DENIED, Config->NickServ.c_str()); - else if (!nick.empty() && (!findnick(nick) || !(nc = findnick(nick)->nc))) - source.Reply(nick.empty() ? NICK_NOT_REGISTERED : _(NICK_X_NOT_REGISTERED), nick.c_str()); - else - { - source.Reply(!nick.empty() ? _("List of nicknames in the group of \002%s\002:") : _("List of nicknames in your group:"), nc->display.c_str()); - for (std::list::const_iterator it = nc->aliases.begin(), it_end = nc->aliases.end(); it != it_end; ++it) - { - NickAlias *na2 = *it; - - source.Reply(na2->HasFlag(NS_NO_EXPIRE) || !Config->NSExpire ? _(" %s (does not expire)") : _(" %s (expires in %s)"), na2->nick.c_str(), do_strftime(na2->last_seen + Config->NSExpire).c_str()); - } - source.Reply(_("%d nicknames in the group."), nc->aliases.size()); - } - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - User *u = source.u; - if (u->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" - "This use 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, CORE), - commandnsgroup(this), commandnsungroup(this), commandnsglist(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandnsgroup); - ModuleManager::RegisterService(&commandnsungroup); - ModuleManager::RegisterService(&commandnsglist); - } -}; - -MODULE_INIT(NSGroup) diff --git a/modules/core/ns_identify.cpp b/modules/core/ns_identify.cpp deleted file mode 100644 index b0a375cd2..000000000 --- a/modules/core/ns_identify.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/* NickServ core functions - * - * (C) 2003-2011 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 CommandNSIdentify : public Command -{ - public: - CommandNSIdentify(Module *creator) : Command(creator, "nickserv/identify", 1, 2) - { - this->SetFlag(CFLAG_ALLOW_UNREGISTERED); - this->SetDesc(_("Identify yourself with your password")); - this->SetSyntax(_("[\037account\037] \037password\037")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - - const Anope::string &nick = params.size() == 2 ? params[0] : u->nick; - Anope::string pass = params[params.size() - 1]; - - NickAlias *na = findnick(nick); - if (na && na->nc->HasFlag(NI_SUSPENDED)) - source.Reply(NICK_X_SUSPENDED, na->nick.c_str()); - else if (u->Account() && na && u->Account() == na->nc) - source.Reply(_("You are already identified.")); - else - { - EventReturn MOD_RESULT; - FOREACH_RESULT(I_OnCheckAuthentication, OnCheckAuthentication(this, &source, params, na ? na->nc->display : nick, pass)); - if (MOD_RESULT == EVENT_STOP) - return; - - if (!na) - source.Reply(NICK_X_NOT_REGISTERED, nick.c_str()); - else if (MOD_RESULT != EVENT_ALLOW) - { - Log(LOG_COMMAND, u, this) << "and failed to identify"; - source.Reply(PASSWORD_INCORRECT); - bad_password(u); - } - else - { - if (u->IsIdentified()) - Log(LOG_COMMAND, u, this) << "to log out of account " << u->Account()->display; - - Log(LOG_COMMAND, u, this) << "and identified for account " << na->nc->display; - source.Reply(_("Password accepted - you are now recognized.")); - u->Identify(na); - } - } - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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.owner->nick.c_str()); - return true; - } -}; - -class NSIdentify : public Module -{ - CommandNSIdentify commandnsidentify; - - public: - NSIdentify(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandnsidentify(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandnsidentify); - } -}; - -MODULE_INIT(NSIdentify) diff --git a/modules/core/ns_info.cpp b/modules/core/ns_info.cpp deleted file mode 100644 index 5a6d9dc0c..000000000 --- a/modules/core/ns_info.cpp +++ /dev/null @@ -1,173 +0,0 @@ -/* NickServ core functions - * - * (C) 2003-2011 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 -{ - private: - // cannot be const, as it is modified - void CheckOptStr(Anope::string &buf, NickCoreFlag opt, const Anope::string &str, NickCore *nc, bool reverse_logic = false) - { - if (reverse_logic ? !nc->HasFlag(opt) : nc->HasFlag(opt)) - { - if (!buf.empty()) - buf += ", "; - - buf += str; - } - } - public: - CommandNSInfo(Module *creator) : Command(creator, "nickserv/info", 0, 2) - { - this->SetFlag(CFLAG_ALLOW_UNREGISTERED); - this->SetDesc(_("Displays information about a given nickname")); - this->SetSyntax(_("[\037nickname\037]")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - - const Anope::string &nick = params.size() ? params[0] : (u->Account() ? u->Account()->display : u->nick); - NickAlias *na = findnick(nick); - bool has_auspex = u->IsIdentified() && u->HasPriv("nickserv/auspex"); - - if (!na) - { - if (nickIsServices(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 = finduser(na->nick); - if (u2 && u2->Account() == na->nc) - nick_online = true; - - if (has_auspex || (u->Account() && na->nc == u->Account())) - show_hidden = true; - - source.Reply(_("%s is %s"), na->nick.c_str(), na->last_realname.c_str()); - - if (na->nc->IsServicesOper() && (show_hidden || !na->nc->HasFlag(NI_HIDE_STATUS))) - source.Reply(_("%s is a services operator of type %s."), na->nick.c_str(), na->nc->o->ot->GetName().c_str()); - - if (nick_online) - { - if (show_hidden || !na->nc->HasFlag(NI_HIDE_MASK)) - source.Reply(_(" Is online from: %s"), na->last_usermask.c_str()); - else - source.Reply(_("%s is currently online."), na->nick.c_str()); - } - else - { - if (show_hidden || !na->nc->HasFlag(NI_HIDE_MASK)) - source.Reply(_("Last seen address: %s"), na->last_usermask.c_str()); - } - - source.Reply(_(" Time registered: %s"), do_strftime(na->time_registered).c_str()); - - if (!nick_online) - { - source.Reply(_(" Last seen time: %s"), do_strftime(na->last_seen).c_str()); - } - - if (!na->last_quit.empty() && (show_hidden || !na->nc->HasFlag(NI_HIDE_QUIT))) - source.Reply(_("Last quit message: %s"), na->last_quit.c_str()); - - if (!na->nc->email.empty() && (show_hidden || !na->nc->HasFlag(NI_HIDE_EMAIL))) - source.Reply(_(" E-mail address: %s"), na->nc->email.c_str()); - - if (show_hidden) - { - if (na->hostinfo.HasVhost()) - { - if (ircd->vident && !na->hostinfo.GetIdent().empty()) - source.Reply(_(" vhost: %s@%s"), na->hostinfo.GetIdent().c_str(), na->hostinfo.GetHost().c_str()); - else - source.Reply(_(" vhost: %s"), na->hostinfo.GetHost().c_str()); - } - if (!na->nc->greet.empty()) - source.Reply(_(" Greet message: %s"), na->nc->greet.c_str()); - - Anope::string optbuf; - - CheckOptStr(optbuf, NI_KILLPROTECT, _("Protection"), na->nc); - CheckOptStr(optbuf, NI_SECURE, _("Security"), na->nc); - CheckOptStr(optbuf, NI_PRIVATE, _("Private"), na->nc); - CheckOptStr(optbuf, NI_MSG, _("Message mode"), na->nc); - CheckOptStr(optbuf, NI_AUTOOP, _("Auto-op"), na->nc); - - source.Reply(NICK_INFO_OPTIONS, optbuf.empty() ? _("None") : optbuf.c_str()); - - if (na->nc->HasFlag(NI_SUSPENDED)) - { - if (!na->last_quit.empty()) - source.Reply(_("This nickname is currently suspended, reason: %s"), na->last_quit.c_str()); - else - source.Reply(_("This nickname is currently suspended")); - } - - if (na->nc->HasFlag(NI_UNCONFIRMED) == false) - { - if (na->HasFlag(NS_NO_EXPIRE) || !Config->NSExpire) - source.Reply(_("This nickname will not expire.")); - else - source.Reply(_("Expires on: %s"), do_strftime(na->last_seen + Config->NSExpire).c_str()); - } - else - source.Reply(_("Expires on: %s"), do_strftime(na->time_registered + Config->NSUnconfirmedExpire).c_str()); - } - - FOREACH_MOD(I_OnNickInfo, OnNickInfo(source, na, show_hidden)); - - if (na->nc->HasFlag(NI_UNCONFIRMED)) - source.Reply(_("This nickname is unconfirmed.")); - } - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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 NSInfo : public Module -{ - CommandNSInfo commandnsinfo; - - public: - NSInfo(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandnsinfo(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandnsinfo); - } -}; - -MODULE_INIT(NSInfo) diff --git a/modules/core/ns_list.cpp b/modules/core/ns_list.cpp deleted file mode 100644 index 96049ba02..000000000 --- a/modules/core/ns_list.cpp +++ /dev/null @@ -1,169 +0,0 @@ -/* NickServ core functions - * - * (C) 2003-2011 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 ¶ms) - { - User *u = source.u; - - Anope::string pattern = params[0]; - const NickCore *mync; - unsigned nnicks; - bool is_servadmin = u->IsServicesOper(); - char noexpire_char = ' '; - int count = 0, from = 0, to = 0; - bool suspended, nsnoexpire, unconfirmed; - - suspended = nsnoexpire = unconfirmed = false; - - if (pattern[0] == '#') - { - Anope::string n1 = myStrGetToken(pattern.substr(1), '-', 0), /* Read FROM out */ - n2 = myStrGetToken(pattern, '-', 1); - - try - { - from = convertTo(n1); - to = convertTo(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 = u->Account(); - - source.Reply(LIST_HEADER, pattern.c_str()); - for (nickalias_map::const_iterator it = NickAliasList.begin(), it_end = NickAliasList.end(); it != it_end; ++it) - { - NickAlias *na = it->second; - - /* Don't show private nicks to non-services admins. */ - if (na->nc->HasFlag(NI_PRIVATE) && !is_servadmin && na->nc != mync) - continue; - else if (nsnoexpire && !na->HasFlag(NS_NO_EXPIRE)) - continue; - else if (suspended && !na->nc->HasFlag(NI_SUSPENDED)) - continue; - else if (unconfirmed && !na->nc->HasFlag(NI_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)) - { - if (((count + 1 >= from && count + 1 <= to) || (!from && !to)) && ++nnicks <= Config->NSListMax) - { - if (is_servadmin && na->HasFlag(NS_NO_EXPIRE)) - noexpire_char = '!'; - else - noexpire_char = ' '; - if (na->nc->HasFlag(NI_HIDE_MASK) && !is_servadmin && na->nc != mync) - buf = Anope::printf("%-20s [Hostname Hidden]", na->nick.c_str()); - else if (na->nc->HasFlag(NI_SUSPENDED)) - buf = Anope::printf("%-20s [Suspended]", na->nick.c_str()); - else if (na->nc->HasFlag(NI_UNCONFIRMED)) - buf = Anope::printf("%-20s [Unconfirmed]", na->nick.c_str()); - else - buf = Anope::printf("%-20s %s", na->nick.c_str(), na->last_usermask.c_str()); - source.Reply(" %c%s", noexpire_char, buf.c_str()); - } - ++count; - } - } - - - source.Reply(_("End of list - %d/%d matches shown."), nnicks > Config->NSListMax ? Config->NSListMax : nnicks, nnicks); - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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. Nicks\n" - "with the \002NOEXPIRE\002 option set will have a \002!\002 appended to\n" - "the nickname for Services Operators.\n" - " \n" - "If the SUSPENDED, NOEXPIRE or UNCONFIRMED 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" - "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")); - - return true; - } -}; - -class NSList : public Module -{ - CommandNSList commandnslist; - - public: - NSList(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandnslist(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandnslist); - } -}; - -MODULE_INIT(NSList) diff --git a/modules/core/ns_logout.cpp b/modules/core/ns_logout.cpp deleted file mode 100644 index 476330a11..000000000 --- a/modules/core/ns_logout.cpp +++ /dev/null @@ -1,97 +0,0 @@ -/* NickServ core functions - * - * (C) 2003-2011 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 "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 ¶ms) - { - User *u = source.u; - - const Anope::string &nick = !params.empty() ? params[0] : ""; - const Anope::string ¶m = params.size() > 1 ? params[1] : ""; - - User *u2; - if (!u->IsServicesOper() && !nick.empty()) - this->OnSyntaxError(source, ""); - else if (!(u2 = (!nick.empty() ? finduser(nick) : u))) - source.Reply(NICK_X_NOT_IN_USE, nick.c_str()); - else if (!nick.empty() && !u2->IsServicesOper()) - source.Reply(_("You can't logout %s because they are a Services Operator."), nick.c_str()); - else - { - if (!nick.empty() && !param.empty() && param.equals_ci("REVALIDATE") && nickserv) - nickserv->Validate(u2); - - u2->isSuperAdmin = 0; /* Dont let people logout and remain a SuperAdmin */ - Log(LOG_COMMAND, u, 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.")); - - ircdproto->SendAccountLogout(u2, u2->Account()); - u2->RemoveMode(source.owner, UMODE_REGISTERED); - ircdproto->SendUnregisteredNick(u2); - - u2->Logout(); - - /* Send out an event */ - FOREACH_MOD(I_OnNickLogout, OnNickLogout(u2)); - } - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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 REVALIDATE as well, Services will ask the given nick\n" - "to re-identify. This use 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, CORE), - commandnslogout(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandnslogout); - } -}; - -MODULE_INIT(NSLogout) diff --git a/modules/core/ns_main.cpp b/modules/core/ns_main.cpp deleted file mode 100644 index 57323d0a5..000000000 --- a/modules/core/ns_main.cpp +++ /dev/null @@ -1,307 +0,0 @@ -/* NickServ core functions - * - * (C) 2003-2011 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 "nickserv.h" - -static BotInfo *NickServ; - -class MyNickServService : public NickServService -{ - public: - MyNickServService(Module *m) : NickServService(m) { } - - void Validate(User *u) - { - NickAlias *na = findnick(u->nick); - if (!na) - return; - - if (na->nc->HasFlag(NI_SUSPENDED)) - { - u->SendMessage(NickServ, NICK_X_SUSPENDED, u->nick.c_str()); - u->Collide(na); - return; - } - - if (!u->IsIdentified() && !u->fingerprint.empty() && na->nc->FindCert(u->fingerprint)) - { - u->SendMessage(NickServ, _("SSL Fingerprint accepted, you are now identified.")); - u->Identify(na); - return; - } - - if (!na->nc->HasFlag(NI_SECURE) && u->IsRecognized()) - { - na->last_seen = Anope::CurTime; - Anope::string last_usermask = u->GetIdent() + "@" + u->GetDisplayedHost(); - na->last_usermask = last_usermask; - na->last_realname = u->realname; - return; - } - - if (u->IsRecognized() || !na->nc->HasFlag(NI_KILL_IMMED)) - { - if (na->nc->HasFlag(NI_SECURE)) - u->SendMessage(NickServ, NICK_IS_SECURE, Config->UseStrictPrivMsgString.c_str(), Config->NickServ.c_str()); - else - u->SendMessage(NickServ, NICK_IS_REGISTERED, Config->UseStrictPrivMsgString.c_str(), Config->NickServ.c_str()); - } - - if (na->nc->HasFlag(NI_KILLPROTECT) && !u->IsRecognized()) - { - if (na->nc->HasFlag(NI_KILL_IMMED)) - { - u->SendMessage(NickServ, FORCENICKCHANGE_NOW); - u->Collide(na); - } - else if (na->nc->HasFlag(NI_KILL_QUICK)) - { - u->SendMessage(NickServ, _("If you do not change within 20 seconds, I will change your nick.")); - new NickServCollide(u, 20); - } - else - { - u->SendMessage(NickServ, _("If you do not change within one minute, I will change your nick.")); - new NickServCollide(u, 60); - } - } - - } -}; - -class ExpireCallback : public CallBack -{ - public: - ExpireCallback(Module *owner) : CallBack(owner, Config->ExpireTimeout, Anope::CurTime, true) { } - - void Tick(time_t) - { - if (noexpire || readonly) - return; - - for (nickalias_map::const_iterator it = NickAliasList.begin(), it_end = NickAliasList.end(); it != it_end; ) - { - NickAlias *na = it->second; - ++it; - - User *u = finduser(na->nick); - if (u && (na->nc->HasFlag(NI_SECURE) ? u->IsIdentified(true) : u->IsRecognized(true))) - na->last_seen = Anope::CurTime; - - bool expire = false; - - if (na->nc->HasFlag(NI_UNCONFIRMED)) - if (Config->NSUnconfirmedExpire && Anope::CurTime - na->time_registered >= Config->NSUnconfirmedExpire) - expire = true; - if (na->nc->HasFlag(NI_SUSPENDED)) - { - if (Config->NSSuspendExpire && Anope::CurTime - na->last_seen >= Config->NSSuspendExpire) - expire = true; - } - else if (Config->NSExpire && Anope::CurTime - na->last_seen >= Config->NSExpire) - expire = true; - if (na->HasFlag(NS_NO_EXPIRE)) - expire = false; - - if (expire) - { - EventReturn MOD_RESULT; - FOREACH_RESULT(I_OnPreNickExpire, OnPreNickExpire(na)); - if (MOD_RESULT == EVENT_STOP) - continue; - Anope::string extra; - if (na->nc->HasFlag(NI_SUSPENDED)) - extra = "suspended "; - Log(LOG_NORMAL, "expire") << "Expiring " << extra << "nickname " << na->nick << " (group: " << na->nc->display << ") (e-mail: " << (na->nc->email.empty() ? "none" : na->nc->email) << ")"; - FOREACH_MOD(I_OnNickExpire, OnNickExpire(na)); - delete na; - } - } - } -}; - -class NickServCore : public Module -{ - MyNickServService mynickserv; - ExpireCallback expires; - - public: - NickServCore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), mynickserv(this), expires(this) - { - this->SetAuthor("Anope"); - - NickServ = findbot(Config->NickServ); - if (NickServ == NULL) - throw ModuleException("No bot named " + Config->NickServ); - - Implementation i[] = { I_OnDelNick, I_OnDelCore, I_OnChangeCoreDisplay, I_OnNickIdentify, I_OnNickGroup, - I_OnNickUpdate, I_OnUserNickChange, I_OnPreHelp, I_OnPostHelp }; - ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); - - ModuleManager::RegisterService(&this->mynickserv); - } - - void OnDelNick(NickAlias *na) - { - User *u = finduser(na->nick); - if (u && u->Account() == na->nc) - { - u->RemoveMode(NickServ, UMODE_REGISTERED); - ircdproto->SendAccountLogout(u, u->Account()); - ircdproto->SendUnregisteredNick(u); - u->Logout(); - } - } - - void OnDelCore(NickCore *nc) - { - Log(NickServ, "nick") << "deleting nickname group " << nc->display; - - /* Clean up this nick core from any users online using it - * (ones that /nick but remain unidentified) - */ - for (std::list::iterator it = nc->Users.begin(); it != nc->Users.end();) - { - User *user = *it++; - ircdproto->SendAccountLogout(user, user->Account()); - user->RemoveMode(NickServ, UMODE_REGISTERED); - ircdproto->SendUnregisteredNick(user); - user->Logout(); - FOREACH_MOD(I_OnNickLogout, OnNickLogout(user)); - } - nc->Users.clear(); - } - - void OnChangeCoreDisplay(NickCore *nc, const Anope::string &newdisplay) - { - Log(LOG_NORMAL, "nick", NickServ) << "Changing " << nc->display << " nickname group display to " << newdisplay; - } - - void OnNickIdentify(User *u) - { - NickAlias *this_na = findnick(u->nick); - if (this_na && this_na->nc == u->Account() && u->Account()->HasFlag(NI_UNCONFIRMED) == false) - u->SetMode(NickServ, UMODE_REGISTERED); - - if (Config->NSModeOnID) - for (UChannelList::iterator it = u->chans.begin(), it_end = u->chans.end(); it != it_end; ++it) - { - ChannelContainer *cc = *it; - Channel *c = cc->chan; - if (c) - chan_set_correct_modes(u, c, 1); - } - - if (Config->NSForceEmail && 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->UseStrictPrivMsgString.c_str(), Config->NickServ.c_str()); - } - - if (u->Account()->HasFlag(NI_UNCONFIRMED)) - { - u->SendMessage(NickServ, _("Your email address is not confirmed. To confirm it, follow the instructions that were emailed to you when you registered.")); - this_na = findnick(u->Account()->display); - time_t time_registered = Anope::CurTime - this_na->time_registered; - if (Config->NSUnconfirmedExpire > time_registered) - u->SendMessage(NickServ, _("Your account will expire, if not confirmed, in %s"), duration(Config->NSUnconfirmedExpire - time_registered).c_str()); - } - } - - void OnNickGroup(User *u, NickAlias *target) - { - if (target->nc->HasFlag(NI_UNCONFIRMED) == false) - u->SetMode(NickServ, UMODE_REGISTERED); - } - - void OnNickUpdate(User *u) - { - for (UChannelList::iterator it = u->chans.begin(), it_end = u->chans.end(); it != it_end; ++it) - { - ChannelContainer *cc = *it; - Channel *c = cc->chan; - if (c) - chan_set_correct_modes(u, c, 1); - } - } - - void OnUserNickChange(User *u, const Anope::string &oldnick) - { - NickAlias *na = findnick(u->nick); - /* If the new nick isnt registerd or its registerd and not yours */ - if (!na || na->nc != u->Account()) - { - u->RemoveMode(NickServ, UMODE_REGISTERED); - ircdproto->SendUnregisteredNick(u); - - this->mynickserv.Validate(u); - } - else - { - if (na->nc->HasFlag(NI_UNCONFIRMED) == false) - { - u->SetMode(NickServ, UMODE_REGISTERED); - ircdproto->SetAutoIdentificationToken(u); - } - Log(NickServ) << u->GetMask() << " automatically identified for group " << u->Account()->display; - } - } - - void OnUserModeSet(User *u, UserModeName Name) - { - if (Name == UMODE_REGISTERED && !u->IsIdentified()) - u->RemoveMode(NickServ, Name); - } - - void OnPreHelp(CommandSource &source, const std::vector ¶ms) - { - if (!params.empty() || source.owner->nick != Config->NickServ) - return; - 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 "), Config->NickServ.c_str(), Config->UseStrictPrivMsgString.c_str(), Config->NickServ.c_str(), Config->UseStrictPrivMsgString.c_str(), Config->NickServ.c_str(), source.command.c_str()); - } - - void OnPostHelp(CommandSource &source, const std::vector ¶ms) - { - if (!params.empty() || source.owner->nick != Config->NickServ) - return; - if (source.u->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.")); - if (Config->NSExpire >= 86400) - source.Reply(_("Nicknames 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."), Config->NSExpire / 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)."), Config->NickServ.c_str()); - } -}; - -MODULE_INIT(NickServCore) - diff --git a/modules/core/ns_recover.cpp b/modules/core/ns_recover.cpp deleted file mode 100644 index 5e45aab9a..000000000 --- a/modules/core/ns_recover.cpp +++ /dev/null @@ -1,132 +0,0 @@ -/* NickServ core functions - * - * (C) 2003-2011 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 CommandNSRecover : public Command -{ - public: - CommandNSRecover(Module *creator) : Command(creator, "nickserv/recover", 1, 2) - { - this->SetFlag(CFLAG_ALLOW_UNREGISTERED); - this->SetDesc(_("Kill another user who has taken your nick")); - this->SetSyntax(_("\037nickname\037 [\037password\037]")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - - const Anope::string &nick = params[0]; - const Anope::string &pass = params.size() > 1 ? params[1] : ""; - - NickAlias *na; - User *u2; - if (!(u2 = finduser(nick))) - source.Reply(NICK_X_NOT_IN_USE, nick.c_str()); - else if (!(na = findnick(u2->nick))) - source.Reply(NICK_X_NOT_REGISTERED, nick.c_str()); - else if (na->nc->HasFlag(NI_SUSPENDED)) - source.Reply(NICK_X_SUSPENDED, na->nick.c_str()); - else if (nick.equals_ci(u->nick)) - source.Reply(_("You can't recover yourself!")); - else if (!pass.empty()) - { - EventReturn MOD_RESULT; - FOREACH_RESULT(I_OnCheckAuthentication, OnCheckAuthentication(this, &source, params, na->nc->display, pass)); - if (MOD_RESULT == EVENT_STOP) - return; - - if (MOD_RESULT == EVENT_ALLOW) - { - u2->SendMessage(source.owner, FORCENICKCHANGE_NOW); - u2->Collide(na); - - /* Convert Config->NSReleaseTimeout seconds to string format */ - Anope::string relstr = duration(Config->NSReleaseTimeout); - - source.Reply(NICK_RECOVERED, Config->UseStrictPrivMsgString.c_str(), Config->NickServ.c_str(), nick.c_str(), relstr.c_str()); - } - else - { - source.Reply(ACCESS_DENIED); - Log(LOG_COMMAND, u, this) << "with invalid password for " << nick; - bad_password(u); - } - } - else - { - if (u->Account() == na->nc || (!na->nc->HasFlag(NI_SECURE) && is_on_access(u, na->nc)) || - (!u->fingerprint.empty() && na->nc->FindCert(u->fingerprint))) - { - u2->SendMessage(source.owner, FORCENICKCHANGE_NOW); - u2->Collide(na); - - /* Convert Config->NSReleaseTimeout seconds to string format */ - Anope::string relstr = duration(Config->NSReleaseTimeout); - - source.Reply(NICK_RECOVERED, Config->UseStrictPrivMsgString.c_str(), Config->NickServ.c_str(), nick.c_str(), relstr.c_str()); - } - else - source.Reply(ACCESS_DENIED); - } - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - /* Convert Config->NSReleaseTimeout seconds to string format */ - Anope::string relstr = duration(Config->NSReleaseTimeout); - - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Allows you to recover your nickname if someone else has\n" - "taken it; this does the same thing that %s does\n" - "automatically if someone tries to use a kill-protected\n" - "nick.\n" - " \n" - "When you give this command, %s will bring a fake\n" - "user online with the same nickname as the user you're\n" - "trying to recover your nick from. This causes the IRC\n" - "servers to disconnect the other user. This fake user will\n" - "remain online for %s to ensure that the other\n" - "user does not immediately reconnect; after that time, you\n" - "can reclaim your nick. Alternatively, use the \002RELEASE\002\n" - "command (\002%s%s HELP RELEASE\002) to get the nick\n" - "back sooner.\n" - " \n" - "In order to use the \002RECOVER\002 command for a nick, your\n" - "current address as shown in /WHOIS must be on that nick's\n" - "access list, you must be identified and in the group of\n" - "that nick, or you must supply the correct password for\n" - "the nickname."), Config->NickServ.c_str(), Config->NickServ.c_str(), relstr.c_str(), Config->UseStrictPrivMsgString.c_str(), Config->NickServ.c_str()); - - return true; - } -}; - -class NSRecover : public Module -{ - CommandNSRecover commandnsrecover; - - public: - NSRecover(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandnsrecover(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandnsrecover); - } -}; - -MODULE_INIT(NSRecover) diff --git a/modules/core/ns_register.cpp b/modules/core/ns_register.cpp deleted file mode 100644 index 552551994..000000000 --- a/modules/core/ns_register.cpp +++ /dev/null @@ -1,355 +0,0 @@ -/* NickServ core functions - * - * (C) 2003-2011 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, NickAlias *na, BotInfo *bi); - -class CommandNSConfirm : public Command -{ - public: - CommandNSConfirm(Module *creator) : Command(creator, "nickserv/confirm", 1, 2) - { - this->SetFlag(CFLAG_ALLOW_UNREGISTERED); - this->SetDesc(_("Confirm an auth code")); - this->SetSyntax(_("\037passcode\037")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - const Anope::string &passcode = params[0]; - - if (u->Account() && u->HasPriv("nickserv/confirm")) - { - NickAlias *na = findnick(passcode); - if (na == NULL) - source.Reply(NICK_X_NOT_REGISTERED, passcode.c_str()); - else if (na->nc->HasFlag(NI_UNCONFIRMED) == false) - source.Reply(_("Nick \002%s\002 is already confirmed."), na->nick.c_str()); - else - { - na->nc->UnsetFlag(NI_UNCONFIRMED); - Log(LOG_ADMIN, u, this) << "to confirm nick " << na->nick << " (" << na->nc->display << ")"; - source.Reply(_("Nick \002%s\002 has been confirmed."), na->nick.c_str()); - } - } - else if (u->Account()) - { - Anope::string code; - if (u->Account()->GetExtRegular("ns_register_passcode", code) && code == passcode) - { - u->Account()->Shrink("ns_register_passcode"); - Log(LOG_COMMAND, u, this) << "to confirm their email"; - source.Reply(_("Your email address of \002%s\002 has been confirmed."), u->Account()->email.c_str()); - u->Account()->UnsetFlag(NI_UNCONFIRMED); - ircdproto->SendAccountLogin(u, u->Account()); - NickAlias *na = findnick(u->nick); - if (na && na->nc == u->Account()) - u->SetMode(findbot(Config->NickServ), UMODE_REGISTERED); - } - else - source.Reply(_("Invalid passcode.")); - } - else - source.Reply(_("Invalid passcode.")); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - User *u = source.u; - 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 (u->Account() && u->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) - { - source.Reply(NICK_CONFIRM_INVALID); - } -}; - -class CommandNSRegister : public Command -{ - public: - CommandNSRegister(Module *creator) : Command(creator, "nickserv/register", 1, 2) - { - this->SetFlag(CFLAG_ALLOW_UNREGISTERED); - this->SetDesc(_("Register a nickname")); - if (Config->NSForceEmail) - this->SetSyntax(_("\037password\037 \037email\037")); - else - this->SetSyntax(_("\037password\037 \037[email]\037")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - NickAlias *na; - size_t prefixlen = Config->NSGuestNickPrefix.length(); - size_t nicklen = u->nick.length(); - Anope::string pass = params[0]; - Anope::string email = params.size() > 1 ? params[1] : ""; - - if (readonly) - { - source.Reply(_("Sorry, nickname registration is temporarily disabled.")); - return; - } - - if (!u->HasMode(UMODE_OPER) && Config->NickRegDelay && Anope::CurTime - u->my_signon < Config->NickRegDelay) - { - source.Reply(_("You must have been using this nick for at least %d seconds to register."), Config->NickRegDelay); - return; - } - - /* Prevent "Guest" nicks from being registered. -TheShadow */ - - /* Guest nick can now have a series of between 1 and 7 digits. - * --lara - */ - if (nicklen <= prefixlen + 7 && nicklen >= prefixlen + 1 && !u->nick.find_ci(Config->NSGuestNickPrefix) && u->nick.substr(prefixlen).find_first_not_of("1234567890") == Anope::string::npos) - { - source.Reply(NICK_CANNOT_BE_REGISTERED, u->nick.c_str()); - return; - } - - if (Config->RestrictOperNicks) - for (unsigned i = 0; i < Config->Opers.size(); ++i) - { - Oper *o = Config->Opers[i]; - - if (!u->HasMode(UMODE_OPER) && u->nick.find_ci(o->name) != Anope::string::npos) - { - source.Reply(NICK_CANNOT_BE_REGISTERED, u->nick.c_str()); - return; - } - } - - if (Config->NSForceEmail && email.empty()) - this->OnSyntaxError(source, ""); - else if (Anope::CurTime < u->lastnickreg + Config->NSRegDelay) - source.Reply(_("Please wait %d seconds before using the REGISTER command again."), (u->lastnickreg + Config->NSRegDelay) - Anope::CurTime); - else if ((na = findnick(u->nick))) - source.Reply(NICK_ALREADY_REGISTERED, u->nick.c_str()); - else if (pass.equals_ci(u->nick) || (Config->StrictPasswords && pass.length() < 5)) - source.Reply(MORE_OBSCURE_PASSWORD); - else if (pass.length() > Config->PassLen) - source.Reply(PASSWORD_TOO_LONG); - else if (!email.empty() && !MailValidate(email)) - source.Reply(MAIL_X_INVALID, email.c_str()); - else - { - na = new NickAlias(u->nick, new NickCore(u->nick)); - enc_encrypt(pass, na->nc->pass); - if (!email.empty()) - na->nc->email = email; - - Anope::string last_usermask = u->GetIdent() + "@" + u->GetDisplayedHost(); - na->last_usermask = last_usermask; - na->last_realname = u->realname; - if (Config->NSAddAccessOnReg) - na->nc->AddAccess(create_mask(u)); - - u->Login(na->nc); - - Log(LOG_COMMAND, u, this) << "to register " << na->nick << " (email: " << (!na->nc->email.empty() ? na->nc->email : "none") << ")"; - - FOREACH_MOD(I_OnNickRegister, OnNickRegister(na)); - - if (Config->NSAddAccessOnReg) - source.Reply(_("Nickname \002%s\002 registered under your account: %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 (enc_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 (Config->NSEmailReg) - { - na->nc->SetFlag(NI_UNCONFIRMED); - if (SendRegmail(u, na, source.owner)) - { - source.Reply(_("A passcode has been sent to %s, please type %s%s confirm to confirm your email address."), email.c_str(), Config->UseStrictPrivMsgString.c_str(), Config->NickServ.c_str()); - source.Reply(_("If you do not confirm your email address within %s your account will expire."), duration(Config->NSUnconfirmedExpire).c_str()); - } - } - else - ircdproto->SendAccountLogin(u, u->Account()); - ircdproto->SetAutoIdentificationToken(u); - - u->lastnickreg = Anope::CurTime; - } - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply("\n"); - 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.\n"), - Config->NickServ.c_str(), Config->NickServ.c_str()); - - if (!Config->NSForceEmail) - source.Reply(_(" \n" - "The parameter \037email\037 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.\n" - " \n")); - - 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->SetSyntax(""); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - if (!Config->NSEmailReg) - return; - - User *u = source.u; - NickAlias *na = findnick(u->nick); - - if (na == NULL) - source.Reply(NICK_NOT_REGISTERED); - else if (na->nc != u->Account() || u->Account()->HasFlag(NI_UNCONFIRMED) == false) - source.Reply(_("Your account is already confirmed.")); - else - { - if (Anope::CurTime < u->Account()->lastmail + Config->NSResendDelay) - source.Reply(_("Cannot send mail now; please retry a little later.")); - else if (!SendRegmail(u, na, source.owner)) - { - na->nc->lastmail = Anope::CurTime; - source.Reply(_("Your passcode has been re-sent to %s."), na->nc->email.c_str()); - Log(LOG_COMMAND, u, this) << "to resend registration verification code"; - } - else - Log() << "Unable to resend registration verification code for " << u->nick; - } - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - if (!Config->NSEmailReg) - return false; - - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("This command will re-send the auth code (also called passcode)\n" - "to the e-mail address of the user whom is performing it.")); - return true; - } - - void OnServHelp(CommandSource &source) - { - if (Config->NSEmailReg) - source.Reply(" %-14s %s", this->name.c_str(),_("Resend the registration passcode")); - } -}; - -class NSRegister : public Module -{ - CommandNSRegister commandnsregister; - CommandNSConfirm commandnsconfirm; - CommandNSResend commandnsrsend; - - public: - NSRegister(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandnsregister(this), commandnsconfirm(this), commandnsrsend(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandnsregister); - ModuleManager::RegisterService(&commandnsconfirm); - ModuleManager::RegisterService(&commandnsrsend); - } -}; - -static bool SendRegmail(User *u, NickAlias *na, BotInfo *bi) -{ - Anope::string code; - if (na->nc->GetExtRegular("ns_register_passcode", code) == false) - { - int chars[] = { - ' ', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', - 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', - 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', - 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', - 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' - }; - int idx, min = 1, max = 62; - for (idx = 0; idx < 9; ++idx) - code += chars[1 + static_cast((static_cast(max - min)) * getrandom16() / 65536.0) + min]; - na->nc->Extend("ns_register_passcode", new ExtensibleItemRegular(code)); - } - - Anope::string subject = Anope::printf(_("Nickname Registration (%s)"), na->nick.c_str()); - Anope::string message = Anope::printf(_("Hi,\n" - " \n" - "You have requested to register the nickname %s on %s.\n" - "Please type \" %s%s confirm %s \" to complete registration.\n" - " \n" - "If you don't know why this mail was sent to you, please ignore it silently.\n" - " \n" - "%s administrators."), na->nick.c_str(), Config->NetworkName.c_str(), Config->UseStrictPrivMsgString.c_str(), Config->NickServ.c_str(), code.c_str(), Config->NetworkName.c_str()); - - return Mail(u, na->nc, bi, subject, message); -} - -MODULE_INIT(NSRegister) diff --git a/modules/core/ns_release.cpp b/modules/core/ns_release.cpp deleted file mode 100644 index 20a3e295c..000000000 --- a/modules/core/ns_release.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/* NickServ core functions - * - * (C) 2003-2011 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 CommandNSRelease : public Command -{ - public: - CommandNSRelease(Module *creator) : Command(creator, "nickserv/release", 1, 2) - { - this->SetFlag(CFLAG_ALLOW_UNREGISTERED); - this->SetDesc(_("Regain custody of your nick after RECOVER")); - this->SetSyntax(_("\037nickname\037 [\037password\037]")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - const Anope::string &nick = params[0]; - Anope::string pass = params.size() > 1 ? params[1] : ""; - NickAlias *na; - - if (!(na = findnick(nick))) - source.Reply(NICK_X_NOT_REGISTERED, nick.c_str()); - else if (na->nc->HasFlag(NI_SUSPENDED)) - source.Reply(NICK_X_SUSPENDED, na->nick.c_str()); - else if (!na->HasFlag(NS_HELD)) - source.Reply(_("Nick \002%s\002 isn't being held."), nick.c_str()); - else if (!pass.empty()) - { - EventReturn MOD_RESULT; - FOREACH_RESULT(I_OnCheckAuthentication, OnCheckAuthentication(this, &source, params, na->nc->display, pass)); - if (MOD_RESULT == EVENT_STOP) - return; - - if (MOD_RESULT == EVENT_ALLOW) - { - Log(LOG_COMMAND, u, this) << "released " << na->nick; - na->Release(); - source.Reply(_("Services' hold on your nick has been released.")); - } - else - { - source.Reply(ACCESS_DENIED); - Log(LOG_COMMAND, u, this) << "invalid password for " << nick; - bad_password(u); - } - } - else - { - if (u->Account() == na->nc || (!na->nc->HasFlag(NI_SECURE) && is_on_access(u, na->nc)) || - (!u->fingerprint.empty() && na->nc->FindCert(u->fingerprint))) - { - na->Release(); - source.Reply(_("Services' hold on your nick has been released.")); - } - else - source.Reply(ACCESS_DENIED); - } - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - /* Convert Config->NSReleaseTimeout seconds to string format */ - Anope::string relstr = duration(Config->NSReleaseTimeout); - - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Instructs %s to remove any hold on your nickname\n" - "caused by automatic kill protection or use of the \002RECOVER\002\n" - "command. This holds lasts for %s;\n" - "This command gets rid of them sooner.\n" - " \n" - "In order to use the \002RELEASE\002 command for a nick, your\n" - "current address as shown in /WHOIS must be on that nick's\n" - "access list, you must be identified and in the group of\n" - "that nick, or you must supply the correct password for\n" - "the nickname."), Config->NickServ.c_str(), relstr.c_str()); - - - return true; - } -}; - -class NSRelease : public Module -{ - CommandNSRelease commandnsrelease; - - public: - NSRelease(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandnsrelease(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandnsrelease); - } -}; - -MODULE_INIT(NSRelease) diff --git a/modules/core/ns_resetpass.cpp b/modules/core/ns_resetpass.cpp deleted file mode 100644 index b0d40226f..000000000 --- a/modules/core/ns_resetpass.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/* NickServ core functions - * - * (C) 2003-2011 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, NickAlias *na, BotInfo *bi); - -class CommandNSResetPass : public Command -{ - public: - CommandNSResetPass(Module *creator) : Command(creator, "nickserv/resetpass", 1, 1) - { - this->SetFlag(CFLAG_ALLOW_UNREGISTERED); - this->SetDesc(_("Helps you reset lost passwords")); - this->SetSyntax(_("\037nickname\037")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - NickAlias *na; - - if (Config->RestrictMail && (!u->Account() || !u->HasCommand("nickserv/nickserv/resetpass"))) - source.Reply(ACCESS_DENIED); - else if (!(na = findnick(params[0]))) - source.Reply(NICK_X_NOT_REGISTERED, params[0].c_str()); - else - { - if (SendResetEmail(u, na, source.owner)) - { - Log(LOG_COMMAND, u, 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) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Sends a code key to the nickname with instructions on how to\n" - "reset their password.")); - return true; - } -}; - -class NSResetPass : public Module -{ - CommandNSResetPass commandnsresetpass; - - public: - NSResetPass(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandnsresetpass(this) - { - this->SetAuthor("Anope"); - - if (!Config->UseMail) - throw ModuleException("Not using mail."); - - ModuleManager::RegisterService(&commandnsresetpass); - - ModuleManager::Attach(I_OnPreCommand, this); - } - - EventReturn OnPreCommand(CommandSource &source, Command *command, std::vector ¶ms) - { - if (command->name == "nickserv/confirm" && params.size() > 1) - { - User *u = source.u; - NickAlias *na = findnick(params[0]); - - time_t t; - Anope::string c; - if (na && na->nc->GetExtRegular("ns_resetpass_code", c) && na->nc->GetExtRegular("ns_resetpass_time", t)) - { - const Anope::string &passcode = params[1]; - if (t < Anope::CurTime - 3600) - { - na->nc->Shrink("ns_resetpass_code"); - na->nc->Shrink("ns_resetpass_time"); - source.Reply(_("Your password reset request has expired.")); - } - else if (passcode.equals_cs(c)) - { - na->nc->Shrink("ns_resetpass_code"); - na->nc->Shrink("ns_resetpass_time"); - - Log(LOG_COMMAND, u, &commandnsresetpass) << "confirmed RESETPASS to forcefully identify to " << na->nick; - - na->nc->UnsetFlag(NI_UNCONFIRMED); - u->Identify(na); - - source.Reply(_("You are now identified for your nick. Change your passwor now.")); - - } - else - return EVENT_CONTINUE; - - return EVENT_STOP; - } - } - - return EVENT_CONTINUE; - } -}; - -static bool SendResetEmail(User *u, NickAlias *na, BotInfo *bi) -{ - int min = 1, max = 62; - int chars[] = { - ' ', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', - 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', - 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', - 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', - 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' - }; - - Anope::string passcode; - int idx; - for (idx = 0; idx < 20; ++idx) - passcode += chars[1 + static_cast((static_cast(max - min)) * getrandom16() / 65536.0) + min]; - - Anope::string subject = Anope::printf(translate(na->nc, _("Reset password request for %s")), na->nick.c_str()); - Anope::string message = Anope::printf(translate(na->nc, _( - "Hi,\n" - " \n" - "You have requested to have the password for %s reset.\n" - "To reset your password, type %s%s CONFIRM %s %s\n" - " \n" - "If you don't know why this mail was sent to you, please ignore it silently.\n" - " \n" - "%s administrators.")), na->nick.c_str(), Config->UseStrictPrivMsgString.c_str(), Config->NickServ.c_str(), na->nick.c_str(), passcode.c_str(), Config->NetworkName.c_str()); - - na->nc->Extend("ns_resetpass_code", new ExtensibleItemRegular(passcode)); - na->nc->Extend("ns_resetpass_time", new ExtensibleItemRegular(Anope::CurTime)); - - return Mail(u, na->nc, bi, subject, message); -} - -MODULE_INIT(NSResetPass) diff --git a/modules/core/ns_saset.cpp b/modules/core/ns_saset.cpp deleted file mode 100644 index a4aa2f422..000000000 --- a/modules/core/ns_saset.cpp +++ /dev/null @@ -1,173 +0,0 @@ -/* NickServ core functions - * - * (C) 2003-2011 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 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 ¶ms) - { - this->OnSyntaxError(source, ""); - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(_("Sets various nickname options. \037option\037 can be one of:")); - Anope::string this_name = source.command; - for (BotInfo::command_map::iterator it = source.owner->commands.begin(), it_end = source.owner->commands.end(); it != it_end; ++it) - { - const Anope::string &c_name = it->first; - CommandInfo &info = it->second; - - if (c_name.find_ci(this_name + " ") == 0) - { - service_reference command(info.name); - if (command) - { - source.command = c_name; - command->OnServHelp(source); - } - } - } - source.Reply(_("Type \002%s%s HELP SASET \037option\037\002 for more information\n" - "on a specific option. The options will be set on the given\n" - "\037nickname\037."), Config->UseStrictPrivMsgString.c_str(), Config->NickServ.c_str()); - return true; - } -}; - -class CommandNSSASetDisplay : public Command -{ - public: - CommandNSSASetDisplay(Module *creator) : Command(creator, "nickserv/saset/display", 2, 2) - { - this->SetDesc(_("Set the display of the group in Services")); - this->SetSyntax(_("\037nickname\037 \037new-display\037")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - NickAlias *setter_na = findnick(params[0]); - if (setter_na == NULL) - { - source.Reply(NICK_X_NOT_REGISTERED, params[0].c_str()); - return; - } - NickCore *nc = setter_na->nc; - - NickAlias *na = findnick(params[1]); - if (!na || na->nc != nc) - { - source.Reply(_("The new display for \002%s\002 MUST be a nickname of the nickname group!"), nc->display.c_str()); - return; - } - - change_core_display(nc, params[1]); - source.Reply(NICK_SET_DISPLAY_CHANGED, nc->display.c_str()); - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &) - { - this->SendSyntax(source); - 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 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 ¶ms) - { - User *u = source.u; - NickAlias *setter_na = findnick(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->NSSecureAdmins && u->Account() != nc && nc->IsServicesOper()) - { - source.Reply(ACCESS_DENIED); - return; - } - else if (nc->display.equals_ci(params[1]) || (Config->StrictPasswords && len < 5)) - { - source.Reply(MORE_OBSCURE_PASSWORD); - return; - } - else if (len > Config->PassLen) - { - source.Reply(PASSWORD_TOO_LONG); - return; - } - - enc_encrypt(params[1], nc->pass); - Anope::string tmp_pass; - if (enc_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()); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Changes the password used to identify as the nick's owner.")); - return true; - } -}; - -class NSSASet : public Module -{ - CommandNSSASet commandnssaset; - CommandNSSASetDisplay commandnssasetdisplay; - CommandNSSASetPassword commandnssasetpassword; - - public: - NSSASet(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandnssaset(this), commandnssasetdisplay(this), commandnssasetpassword(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandnssaset); - ModuleManager::RegisterService(&commandnssasetdisplay); - ModuleManager::RegisterService(&commandnssasetpassword); - } -}; - -MODULE_INIT(NSSASet) diff --git a/modules/core/ns_saset_noexpire.cpp b/modules/core/ns_saset_noexpire.cpp deleted file mode 100644 index 1bff9b461..000000000 --- a/modules/core/ns_saset_noexpire.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/* NickServ core functions - * - * (C) 2003-2011 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 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 ¶ms) - { - NickAlias *na = findnick(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")) - { - na->SetFlag(NS_NO_EXPIRE); - source.Reply(_("Nick %s \002will not\002 expire."), na->nick.c_str()); - } - else if (param.equals_ci("OFF")) - { - na->UnsetFlag(NS_NO_EXPIRE); - source.Reply(_("Nick %s \002will\002 expire."), na->nick.c_str()); - } - else - this->OnSyntaxError(source, "NOEXPIRE"); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &) - { - 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 NSSASetNoexpire : public Module -{ - CommandNSSASetNoexpire commandnssasetnoexpire; - - public: - NSSASetNoexpire(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandnssasetnoexpire(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandnssasetnoexpire); - } -}; - -MODULE_INIT(NSSASetNoexpire) diff --git a/modules/core/ns_sendpass.cpp b/modules/core/ns_sendpass.cpp deleted file mode 100644 index ca185c68b..000000000 --- a/modules/core/ns_sendpass.cpp +++ /dev/null @@ -1,108 +0,0 @@ -/* NickServ core functions - * - * (C) 2003-2011 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 SendPassMail(User *u, NickAlias *na, BotInfo *bi, const Anope::string &pass); - -class CommandNSSendPass : public Command -{ - public: - CommandNSSendPass(Module *creator) : Command(creator, "nickserv/sendpass", 1, 1) - { - this->SetFlag(CFLAG_ALLOW_UNREGISTERED); - this->SetDesc(_("Forgot your password? Try this")); - this->SetSyntax(_("\037nickname\037")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - const Anope::string &nick = params[0]; - NickAlias *na; - - if (Config->RestrictMail && (!u->Account() || !u->HasCommand("nickserv/sendpass"))) - source.Reply(ACCESS_DENIED); - else if (!(na = findnick(nick))) - source.Reply(NICK_X_NOT_REGISTERED, nick.c_str()); - else - { - Anope::string tmp_pass; - if (enc_decrypt(na->nc->pass, tmp_pass) == 1) - { - if (SendPassMail(u, na, source.owner, tmp_pass)) - { - Log(Config->RestrictMail ? LOG_ADMIN : LOG_COMMAND, u, this) << "for " << na->nick; - source.Reply(_("Password of \002%s\002 has been sent."), nick.c_str()); - } - } - else - source.Reply(_("SENDPASS command unavailable because encryption is in use.")); - } - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Send the password of the given nickname to the e-mail address\n" - "set in the nickname record. This command is really useful\n" - "to deal with lost passwords.\n" - " \n" - "May be limited to \002IRC operators\002 on certain networks.")); - return true; - } -}; - -class NSSendPass : public Module -{ - CommandNSSendPass commandnssendpass; - - public: - NSSendPass(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandnssendpass(this) - { - this->SetAuthor("Anope"); - - if (!Config->UseMail) - throw ModuleException("Not using mail."); - - Anope::string tmp_pass = "plain:tmp"; - if (enc_decrypt(tmp_pass, tmp_pass) == -1) - throw ModuleException("Incompatible with the encryption module being used"); - - ModuleManager::RegisterService(&commandnssendpass); - } -}; - -static bool SendPassMail(User *u, NickAlias *na, BotInfo *bi, const Anope::string &pass) -{ - char subject[BUFSIZE], message[BUFSIZE]; - - snprintf(subject, sizeof(subject), translate(na->nc, _("Nickname password (%s)")), na->nick.c_str()); - snprintf(message, sizeof(message), translate(na->nc, _( - "Hi,\n" - " \n" - "You have requested to receive the password of nickname %s by e-mail.\n" - "The password is %s. For security purposes, you should change it as soon as you receive this mail.\n" - " \n" - "If you don't know why this mail was sent to you, please ignore it silently.\n" - " \n" - "%s administrators.")), na->nick.c_str(), pass.c_str(), Config->NetworkName.c_str()); - - return Mail(u, na->nc, bi, subject, message); -} - -MODULE_INIT(NSSendPass) diff --git a/modules/core/ns_set.cpp b/modules/core/ns_set.cpp deleted file mode 100644 index ad7acb5a4..000000000 --- a/modules/core/ns_set.cpp +++ /dev/null @@ -1,158 +0,0 @@ -/* NickServ core functions - * - * (C) 2003-2011 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 ¶ms) - { - this->OnSyntaxError(source, ""); - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Sets various nickname options. \037option\037 can be one of:")); - Anope::string this_name = source.command; - for (BotInfo::command_map::iterator it = source.owner->commands.begin(), it_end = source.owner->commands.end(); it != it_end; ++it) - { - const Anope::string &c_name = it->first; - CommandInfo &info = it->second; - - if (c_name.find_ci(this_name + " ") == 0) - { - service_reference 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."), Config->UseStrictPrivMsgString.c_str(), source.owner->nick.c_str(), source.command.c_str()); - return true; - } -}; - -class CommandNSSetDisplay : public Command -{ - public: - CommandNSSetDisplay(Module *creator) : Command(creator, "nickserv/set/display", 1) - { - this->SetDesc(_("Set the display of your group in Services")); - this->SetSyntax(_("\037new-display\037")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - NickAlias *na = findnick(params[1]); - - if (!na || na->nc != u->Account()) - { - source.Reply(_("The new display MUST be a nickname of your nickname group!")); - return; - } - - change_core_display(u->Account(), params[1]); - source.Reply(NICK_SET_DISPLAY_CHANGED, u->Account()->display.c_str()); - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &) - { - 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 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 ¶ms) - { - User *u = source.u; - - const Anope::string ¶m = params[1]; - unsigned len = param.length(); - - if (u->Account()->display.equals_ci(param) || (Config->StrictPasswords && len < 5)) - { - source.Reply(MORE_OBSCURE_PASSWORD); - return; - } - else if (len > Config->PassLen) - { - source.Reply(PASSWORD_TOO_LONG); - return; - } - - enc_encrypt(param, u->Account()->pass); - Anope::string tmp_pass; - if (enc_decrypt(u->Account()->pass, tmp_pass) == 1) - source.Reply(_("Password for \002%s\002 changed to \002%s\002."), u->Account()->display.c_str(), tmp_pass.c_str()); - else - source.Reply(_("Password for \002%s\002 changed."), u->Account()->display.c_str()); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Changes the password used to identify you as the nick's\n" - "owner.")); - return true; - } -}; - -class NSSet : public Module -{ - CommandNSSet commandnsset; - CommandNSSetDisplay commandnssetdisplay; - CommandNSSetPassword commandnssetpassword; - - public: - NSSet(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandnsset(this), commandnssetdisplay(this), commandnssetpassword(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandnsset); - ModuleManager::RegisterService(&commandnssetdisplay); - ModuleManager::RegisterService(&commandnssetpassword); - } -}; - -MODULE_INIT(NSSet) diff --git a/modules/core/ns_set_autoop.cpp b/modules/core/ns_set_autoop.cpp deleted file mode 100644 index ff178141f..000000000 --- a/modules/core/ns_set_autoop.cpp +++ /dev/null @@ -1,107 +0,0 @@ -/* NickServ core functions - * - * (C) 2003-2011 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 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(_("Should services op you automatically.")); - this->SetSyntax(_("{ON | OFF}")); - } - - void Run(CommandSource &source, const Anope::string &user, const Anope::string ¶m) - { - NickAlias *na = findnick(user); - if (na == NULL) - { - source.Reply(NICK_X_NOT_REGISTERED, user.c_str()); - return; - } - NickCore *nc = na->nc; - - if (param.equals_ci("ON")) - { - nc->SetFlag(NI_AUTOOP); - source.Reply(_("Services will now autoop %s in channels."), nc->display.c_str()); - } - else if (param.equals_ci("OFF")) - { - nc->UnsetFlag(NI_AUTOOP); - source.Reply(_("Services will no longer autoop %s in channels."), nc->display.c_str()); - } - else - this->OnSyntaxError(source, "AUTOOP"); - - return; - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - this->Run(source, source.u->Account()->display, params[0]); - } - - bool OnHelp(CommandSource &source, const Anope::string &) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Sets whether you will be opped automatically. Set to ON to \n" - "allow ChanServ to op you automatically when entering channels.")); - 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 ¶ms) - { - this->Run(source, params[0], params[1]); - } - - bool OnHelp(CommandSource &source, const Anope::string &) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Sets whether the given nickname will be opped automatically.\n" - "Set to \002ON\002 to allow ChanServ to op the given nickname \n" - "omatically when joining channels.")); - return true; - } -}; - -class NSSetAutoOp : public Module -{ - CommandNSSetAutoOp commandnssetautoop; - CommandNSSASetAutoOp commandnssasetautoop; - - public: - NSSetAutoOp(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandnssetautoop(this), commandnssasetautoop(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandnssetautoop); - ModuleManager::RegisterService(&commandnssasetautoop); - } -}; - -MODULE_INIT(NSSetAutoOp) diff --git a/modules/core/ns_set_email.cpp b/modules/core/ns_set_email.cpp deleted file mode 100644 index f0914eb51..000000000 --- a/modules/core/ns_set_email.cpp +++ /dev/null @@ -1,186 +0,0 @@ -/* NickServ core functions - * - * (C) 2003-2011 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 SendConfirmMail(User *u, BotInfo *bi) -{ - int chars[] = { - ' ', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', - 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', - 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', - 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', - 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' - }; - int idx, min = 1, max = 62; - Anope::string code; - for (idx = 0; idx < 9; ++idx) - code += chars[1 + static_cast((static_cast(max - min)) * getrandom16() / 65536.0) + min]; - u->Account()->Extend("ns_set_email_passcode", new ExtensibleItemRegular(code)); - - Anope::string subject = _("Email confirmation"); - Anope::string message = Anope::printf(_("Hi,\n" - " \n" - "You have requested to change your email address to %s.\n" - "Please type \" %s%s confirm %s \" to confirm this change.\n" - " \n" - "If you don't know why this mail was sent to you, please ignore it silently.\n" - " \n" - "%s administrators."), u->Account()->email.c_str(), Config->UseStrictPrivMsgString.c_str(), Config->NickServ.c_str(), code.c_str(), Config->NetworkName.c_str()); - - return Mail(u, u->Account(), bi, subject, message); -} - -class CommandNSSetEmail : public Command -{ - 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 ¶m) - { - User *u = source.u; - NickAlias *na = findnick(user); - if (!na) - { - source.Reply(NICK_X_NOT_REGISTERED, user.c_str()); - return; - } - NickCore *nc = na->nc; - - if (param.empty() && Config->NSForceEmail) - { - source.Reply(_("You cannot unset the e-mail on this network.")); - return; - } - else if (Config->NSSecureAdmins && u->Account() != nc && nc->IsServicesOper()) - { - source.Reply(ACCESS_DENIED); - return; - } - else if (!param.empty() && !MailValidate(param)) - { - source.Reply(MAIL_X_INVALID, param.c_str()); - return; - } - - if (!param.empty() && Config->NSConfirmEmailChanges && !u->IsServicesOper()) - { - u->Account()->Extend("ns_set_email", new ExtensibleItemRegular(param)); - Anope::string old = u->Account()->email; - u->Account()->email = param; - if (SendConfirmMail(u, source.owner)) - source.Reply(_("A confirmation email has been sent to \002%s\002. Follow the instructions in it to change your email address."), param.c_str()); - u->Account()->email = old; - } - else - { - if (!param.empty()) - { - 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 - { - nc->email.clear(); - source.Reply(_("E-mail address for \002%s\002 unset."), nc->display.c_str()); - } - } - - return; - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - this->Run(source, source.u->Account()->display, params.size() ? params[0] : ""); - } - - bool OnHelp(CommandSource &source, const Anope::string &) - { - 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 ¶ms) - { - this->Run(source, params[0], params.size() > 1 ? params[1] : ""); - } - - bool OnHelp(CommandSource &source, const Anope::string &) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Associates the given E-mail address with the nickname.")); - return true; - } -}; - -class NSSetEmail : public Module -{ - CommandNSSetEmail commandnssetemail; - CommandNSSASetEmail commandnssasetemail; - - public: - NSSetEmail(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandnssetemail(this), commandnssasetemail(this) - { - this->SetAuthor("Anope"); - - ModuleManager::Attach(I_OnPreCommand, this); - - ModuleManager::RegisterService(&commandnssetemail); - ModuleManager::RegisterService(&commandnssasetemail); - } - - EventReturn OnPreCommand(CommandSource &source, Command *command, std::vector ¶ms) - { - User *u = source.u; - if (command->name == "nickserv/confirm" && !params.empty() && u->IsIdentified()) - { - Anope::string new_email, passcode; - if (u->Account()->GetExtRegular("ns_set_email", new_email) && u->Account()->GetExtRegular("ns_set_email_passcode", passcode)) - { - if (params[0] == passcode) - { - u->Account()->email = new_email; - Log(LOG_COMMAND, u, command) << "to confirm their email address change to " << u->Account()->email; - source.Reply(_("Your email address has been changed to \002%s\002."), u->Account()->email.c_str()); - u->Account()->Shrink("ns_set_email"); - u->Account()->Shrink("ns_set_email_passcode"); - return EVENT_STOP; - } - } - } - - return EVENT_CONTINUE; - } -}; - -MODULE_INIT(NSSetEmail) diff --git a/modules/core/ns_set_greet.cpp b/modules/core/ns_set_greet.cpp deleted file mode 100644 index 03bcd3834..000000000 --- a/modules/core/ns_set_greet.cpp +++ /dev/null @@ -1,108 +0,0 @@ -/* NickServ core functions - * - * (C) 2003-2011 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 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 ¶m) - { - NickAlias *na = findnick(user); - if (!na) - { - source.Reply(NICK_X_NOT_REGISTERED, user.c_str()); - return; - } - NickCore *nc = na->nc; - - if (!param.empty()) - { - nc->greet = param; - source.Reply(_("Greet message for \002%s\002 changed to \002%s\002."), nc->display.c_str(), nc->greet.c_str()); - } - else - { - nc->greet.clear(); - source.Reply(_("Greet message for \002%s\002 unset."), nc->display.c_str()); - } - - return; - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - this->Run(source, source.u->Account()->display, params.size() > 0 ? params[0] : ""); - } - - bool OnHelp(CommandSource &source, const Anope::string &) - { - 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 ¶ms) - { - this->Run(source, params[0], params.size() > 1 ? params[1] : ""); - } - - bool OnHelp(CommandSource &source, const Anope::string &) - { - 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 NSSetGreet : public Module -{ - CommandNSSetGreet commandnssetgreet; - CommandNSSASetGreet commandnssasetgreet; - - public: - NSSetGreet(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandnssetgreet(this), commandnssasetgreet(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandnssetgreet); - ModuleManager::RegisterService(&commandnssasetgreet); - } -}; - -MODULE_INIT(NSSetGreet) diff --git a/modules/core/ns_set_hide.cpp b/modules/core/ns_set_hide.cpp deleted file mode 100644 index e0fb0e85e..000000000 --- a/modules/core/ns_set_hide.cpp +++ /dev/null @@ -1,149 +0,0 @@ -/* NickServ core functions - * - * (C) 2003-2011 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 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 ¶m, const Anope::string &arg) - { - NickAlias *na = findnick(user); - if (!na) - { - source.Reply(NICK_X_NOT_REGISTERED, user.c_str()); - return; - } - NickCore *nc = na->nc; - - Anope::string onmsg, offmsg; - NickCoreFlag flag; - - if (param.equals_ci("EMAIL")) - { - flag = NI_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 = NI_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 = NI_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 = NI_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")) - { - nc->SetFlag(flag); - source.Reply(onmsg.c_str(), nc->display.c_str(), Config->NickServ.c_str()); - } - else if (arg.equals_ci("OFF")) - { - nc->UnsetFlag(flag); - source.Reply(offmsg.c_str(), nc->display.c_str(), Config->NickServ.c_str()); - } - else - this->OnSyntaxError(source, "HIDE"); - - return; - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - this->Run(source, source.u->Account()->display, params[0], params[1]); - } - - bool OnHelp(CommandSource &source, const Anope::string &) - { - 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)."), Config->NickServ.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 ¶ms) - { - this->ClearSyntax(); - this->Run(source, params[0], params[1], params[2]); - } - - bool OnHelp(CommandSource &source, const Anope::string &) - { - 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)."), Config->NickServ.c_str()); - return true; - } -}; - -class NSSetHide : public Module -{ - CommandNSSetHide commandnssethide; - CommandNSSASetHide commandnssasethide; - - public: - NSSetHide(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandnssethide(this), commandnssasethide(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandnssethide); - ModuleManager::RegisterService(&commandnssasethide); - } -}; - -MODULE_INIT(NSSetHide) diff --git a/modules/core/ns_set_kill.cpp b/modules/core/ns_set_kill.cpp deleted file mode 100644 index f57a93893..000000000 --- a/modules/core/ns_set_kill.cpp +++ /dev/null @@ -1,149 +0,0 @@ -/* NickServ core functions - * - * (C) 2003-2011 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 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 ¶m) - { - NickAlias *na = findnick(user); - if (!na) - { - source.Reply(NICK_X_NOT_REGISTERED, user.c_str()); - return; - } - NickCore *nc = na->nc; - - if (param.equals_ci("ON")) - { - nc->SetFlag(NI_KILLPROTECT); - nc->UnsetFlag(NI_KILL_QUICK); - nc->UnsetFlag(NI_KILL_IMMED); - source.Reply(_("Protection is now \002on\002 for \002%s\002."), nc->display.c_str()); - } - else if (param.equals_ci("QUICK")) - { - nc->SetFlag(NI_KILLPROTECT); - nc->SetFlag(NI_KILL_QUICK); - nc->UnsetFlag(NI_KILL_IMMED); - 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->NSAllowKillImmed) - { - nc->SetFlag(NI_KILLPROTECT); - nc->SetFlag(NI_KILL_IMMED); - nc->UnsetFlag(NI_KILL_QUICK); - 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->UnsetFlag(NI_KILLPROTECT); - nc->UnsetFlag(NI_KILL_QUICK); - nc->UnsetFlag(NI_KILL_IMMED); - 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 ¶ms) - { - this->Run(source, source.u->Account()->display, params[0]); - } - - bool OnHelp(CommandSource &source, const Anope::string &) - { - 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, 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."), Config->NickServ.c_str()); - return true; - } -}; - -class CommandNSSASetKill : public CommandNSSetKill -{ - public: - CommandNSSASetKill(Module *creator) : CommandNSSetKill(creator, "nickserv/saset/kill", 2) - { - this->SetSyntax(_("\037nickname\037 {ON | QUICK | IMMED | OFF}")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - this->ClearSyntax(); - this->Run(source, params[0], params[1]); - } - - bool OnHelp(CommandSource &source, const Anope::string &) - { - 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."), Config->NickServ.c_str()); - return true; - } -}; - -class NSSetKill : public Module -{ - CommandNSSetKill commandnssetkill; - CommandNSSASetKill commandnssasetkill; - - public: - NSSetKill(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandnssetkill(this), commandnssasetkill(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandnssetkill); - ModuleManager::RegisterService(&commandnssasetkill); - } -}; - -MODULE_INIT(NSSetKill) diff --git a/modules/core/ns_set_language.cpp b/modules/core/ns_set_language.cpp deleted file mode 100644 index 3429ad688..000000000 --- a/modules/core/ns_set_language.cpp +++ /dev/null @@ -1,121 +0,0 @@ -/* NickServ core functions - * - * (C) 2003-2011 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 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 ¶m) - { - NickAlias *na = findnick(user); - if (!na) - { - source.Reply(NICK_X_NOT_REGISTERED, user.c_str()); - return; - } - NickCore *nc = na->nc; - - for (unsigned j = 0; j < languages.size(); ++j) - { - if (param == "en" || languages[j] == param) - break; - else if (j + 1 == languages.size()) - { - this->OnSyntaxError(source, ""); - return; - } - } - - nc->language = param != "en" ? param : ""; - source.Reply(_("Language changed to \002English\002.")); - - return; - } - - void Execute(CommandSource &source, const std::vector ¶m) - { - this->Run(source, source.u->Account()->display, param[0]); - } - - bool OnHelp(CommandSource &source, const Anope::string &) - { - 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 (English)"); - for (unsigned j = 0; j < languages.size(); ++j) - { - const Anope::string &langname = anope_gettext(languages[j].c_str(), _("English")); - if (langname == "English") - continue; - source.Reply(" %s (%s)", 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 ¶ms) - { - this->Run(source, params[0], params[1]); - } - - bool OnHelp(CommandSource &source, const Anope::string &) - { - 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:")); - return true; - } -}; - -class NSSetLanguage : public Module -{ - CommandNSSetLanguage commandnssetlanguage; - CommandNSSASetLanguage commandnssasetlanguage; - - public: - NSSetLanguage(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandnssetlanguage(this), commandnssasetlanguage(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandnssetlanguage); - ModuleManager::RegisterService(&commandnssasetlanguage); - } -}; - -MODULE_INIT(NSSetLanguage) diff --git a/modules/core/ns_set_message.cpp b/modules/core/ns_set_message.cpp deleted file mode 100644 index 677002c78..000000000 --- a/modules/core/ns_set_message.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/* NickServ core functions - * - * (C) 2003-2011 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 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 ¶m) - { - NickAlias *na = findnick(user); - if (!na) - { - source.Reply(NICK_X_NOT_REGISTERED, user.c_str()); - return; - } - NickCore *nc = na->nc; - - if (!Config->UsePrivmsg) - { - source.Reply(_("Option \002%s\02 cannot be set on this network."), "MSG"); - return; - } - - if (param.equals_ci("ON")) - { - nc->SetFlag(NI_MSG); - source.Reply(_("Services will now reply to \002%s\002 with \002messages\002."), nc->display.c_str()); - } - else if (param.equals_ci("OFF")) - { - nc->UnsetFlag(NI_MSG); - source.Reply(_("Services will now reply to \002%s\002 with \002notices\002."), nc->display.c_str()); - } - else - this->OnSyntaxError(source, "MSG"); - - return; - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - this->Run(source, source.u->Account()->display, params[0]); - } - - bool OnHelp(CommandSource &source, const Anope::string &) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Allows you to choose the way Services are communicating with \n" - "you. With \002MSG\002 set, Services will use messages, else they'll \n" - "use notices.")); - return true; - } -}; - -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 &) - { - 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 ¶ms) - { - this->Run(source, params[0], params[1]); - } -}; - -class NSSetMessage : public Module -{ - CommandNSSetMessage commandnssetmessage; - CommandNSSASetMessage commandnssasetmessage; - - public: - NSSetMessage(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandnssetmessage(this), commandnssasetmessage(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandnssetmessage); - ModuleManager::RegisterService(&commandnssasetmessage); - } -}; - -MODULE_INIT(NSSetMessage) diff --git a/modules/core/ns_set_private.cpp b/modules/core/ns_set_private.cpp deleted file mode 100644 index 63fec57d6..000000000 --- a/modules/core/ns_set_private.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/* NickServ core functions - * - * (C) 2003-2011 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 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(Anope::printf(_("Prevent the nickname from appearing in a \002%s%s LIST\002"), Config->UseStrictPrivMsgString.c_str(), Config->NickServ.c_str())); - this->SetSyntax(_("{ON | OFF}")); - } - - void Run(CommandSource &source, const Anope::string &user, const Anope::string ¶m) - { - NickAlias *na = findnick(user); - if (!na) - { - source.Reply(NICK_X_NOT_REGISTERED, user.c_str()); - return; - } - NickCore *nc = na->nc; - - if (param.equals_ci("ON")) - { - nc->SetFlag(NI_PRIVATE); - source.Reply(_("Private option is now \002on\002 for \002%s\002."), nc->display.c_str()); - } - else if (param.equals_ci("OFF")) - { - nc->UnsetFlag(NI_PRIVATE); - source.Reply(_("Private option is now \002off\002 for \002%s\002."), nc->display.c_str()); - } - else - this->OnSyntaxError(source, "PRIVATE"); - - return; - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - this->Run(source, source.u->Account()->display, params[0]); - } - - bool OnHelp(CommandSource &source, const Anope::string &) - { - 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.)"), - Config->NickServ.c_str(), Config->NickServ.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 ¶ms) - { - this->Run(source, params[0], params[1]); - } - - bool OnHelp(CommandSource &source, const Anope::string &) - { - 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.)"), - Config->NickServ.c_str(), Config->NickServ.c_str()); - return true; - } -}; - -class NSSetPrivate : public Module -{ - CommandNSSetPrivate commandnssetprivate; - CommandNSSASetPrivate commandnssasetprivate; - - public: - NSSetPrivate(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandnssetprivate(this), commandnssasetprivate(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandnssetprivate); - ModuleManager::RegisterService(&commandnssasetprivate); - } -}; - -MODULE_INIT(NSSetPrivate) diff --git a/modules/core/ns_set_secure.cpp b/modules/core/ns_set_secure.cpp deleted file mode 100644 index 31314a5a5..000000000 --- a/modules/core/ns_set_secure.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/* NickServ core functions - * - * (C) 2003-2011 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 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 ¶m) - { - NickAlias *na = findnick(user); - if (!na) - { - source.Reply(NICK_X_NOT_REGISTERED, user.c_str()); - return; - } - NickCore *nc = na->nc; - - if (param.equals_ci("ON")) - { - nc->SetFlag(NI_SECURE); - source.Reply(_("Secure option is now \002on\002 for \002%s\002."), nc->display.c_str()); - } - else if (param.equals_ci("OFF")) - { - nc->UnsetFlag(NI_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 ¶ms) - { - this->Run(source, source.u->Account()->display, params[0]); - } - - bool OnHelp(CommandSource &source, const Anope::string &) - { - 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."), Config->NickServ.c_str(), Config->NickServ.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 ¶ms) - { - this->Run(source, params[0], params[1]); - } - - bool OnHelp(CommandSource &source, const Anope::string &) - { - 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."), Config->NickServ.c_str(), Config->NickServ.c_str()); - return true; - } -}; - -class NSSetSecure : public Module -{ - CommandNSSetSecure commandnssetsecure; - CommandNSSASetSecure commandnssasetsecure; - - public: - NSSetSecure(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandnssetsecure(this), commandnssasetsecure(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandnssetsecure); - ModuleManager::RegisterService(&commandnssasetsecure); - } -}; - -MODULE_INIT(NSSetSecure) diff --git a/modules/core/ns_status.cpp b/modules/core/ns_status.cpp deleted file mode 100644 index 9d8b58fcb..000000000 --- a/modules/core/ns_status.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/* NickServ core functions - * - * (C) 2003-2011 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 CommandNSStatus : public Command -{ - public: - CommandNSStatus(Module *creator) : Command(creator, "nickserv/status", 0, 16) - { - this->SetFlag(CFLAG_ALLOW_UNREGISTERED); - this->SetDesc(_("Returns the owner status of the given nickname")); - this->SetSyntax(_("\037nickname\037...")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - const Anope::string &nick = !params.empty() ? params[0] : u->nick; - NickAlias *na = findnick(nick); - spacesepstream sep(nick); - Anope::string nickbuf; - - while (sep.GetToken(nickbuf)) - { - User *u2 = finduser(nickbuf); - 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->IsRecognized()) /* Nick is recognised, but NOT identified */ - source.Reply(_("STATUS %s %d %s"), nickbuf.c_str(), 2, u2->Account() ? u2->Account()->display.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() : ""); - } - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Returns whether the user using the given nickname is\n" - "recognized as the owner of the nickname. The response has\n" - "this format:\n" - " \n" - " \037nickname\037 \037status-code\037 \037account\037\n" - " \n" - "where \037nickname\037 is the nickname sent with the command,\n" - "\037status-code\037 is one of the following, and \037account\037\n" - "is the account they are logged in as.\n" - " \n" - " 0 - no such user online \002or\002 nickname not registered\n" - " 1 - user not recognized as nickname's owner\n" - " 2 - user recognized as owner via access list only\n" - " 3 - user recognized as owner via password identification\n" - " \n" - "Up to sixteen nicknames may be sent with each command; the\n" - "rest will be ignored. If no nickname is given, your status\n" - "will be returned.")); - return true; - } -}; - -class NSStatus : public Module -{ - CommandNSStatus commandnsstatus; - - public: - NSStatus(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandnsstatus(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandnsstatus); - } -}; - -MODULE_INIT(NSStatus) diff --git a/modules/core/ns_suspend.cpp b/modules/core/ns_suspend.cpp deleted file mode 100644 index 50cf039ce..000000000 --- a/modules/core/ns_suspend.cpp +++ /dev/null @@ -1,160 +0,0 @@ -/* NickServ core functions - * - * (C) 2003-2011 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 CommandNSSuspend : public Command -{ - public: - CommandNSSuspend(Module *creator) : Command(creator, "nickserv/suspend", 2, 2) - { - this->SetDesc(_("Suspend a given nick")); - this->SetSyntax(_("\037nickname\037 \037reason\037")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - - const Anope::string &nick = params[0]; - const Anope::string &reason = params[1]; - - if (readonly) - { - source.Reply(READ_ONLY_MODE); - return; - } - - NickAlias *na = findnick(nick); - if (!na) - { - source.Reply(NICK_X_NOT_REGISTERED, nick.c_str()); - return; - } - - if (Config->NSSecureAdmins && na->nc->IsServicesOper()) - { - source.Reply(ACCESS_DENIED); - return; - } - - na->nc->SetFlag(NI_SUSPENDED); - na->nc->SetFlag(NI_SECURE); - na->nc->UnsetFlag(NI_KILLPROTECT); - na->nc->UnsetFlag(NI_KILL_QUICK); - na->nc->UnsetFlag(NI_KILL_IMMED); - - for (std::list::iterator it = na->nc->aliases.begin(), it_end = na->nc->aliases.end(); it != it_end; ++it) - { - NickAlias *na2 = *it; - - if (na2->nc == na->nc) - { - na2->last_quit = reason; - - User *u2 = finduser(na2->nick); - if (u2) - { - u2->Logout(); - u2->Collide(na2); - } - } - } - - Log(LOG_ADMIN, u, this) << "for " << nick << " (" << (!reason.empty() ? reason : "No reason") << ")"; - source.Reply(_("Nick %s is now suspended."), nick.c_str()); - - FOREACH_MOD(I_OnNickSuspended, OnNickSuspend(na)); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Suspends a registered nickname, which prevents from being used\n" - "while keeping all the data for that nick.")); - 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 ¶ms) - { - User *u = source.u; - const Anope::string &nick = params[0]; - - if (readonly) - { - source.Reply(READ_ONLY_MODE); - return; - } - - NickAlias *na = findnick(nick); - if (!na) - { - source.Reply(NICK_X_NOT_REGISTERED, nick.c_str()); - return; - } - - if (Config->NSSecureAdmins && na->nc->IsServicesOper()) - { - source.Reply(ACCESS_DENIED); - return; - } - - na->nc->UnsetFlag(NI_SUSPENDED); - - Log(LOG_ADMIN, u, this) << "for " << na->nick; - source.Reply(_("Nick %s is now released."), nick.c_str()); - - FOREACH_MOD(I_OnNickUnsuspended, OnNickUnsuspended(na)); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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; - - public: - NSSuspend(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandnssuspend(this), commandnsunsuspend(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandnssuspend); - ModuleManager::RegisterService(&commandnsunsuspend); - } -}; - -MODULE_INIT(NSSuspend) diff --git a/modules/core/ns_update.cpp b/modules/core/ns_update.cpp deleted file mode 100644 index d870e3532..000000000 --- a/modules/core/ns_update.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* NickServ core functions - * - * (C) 2003-2011 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->SetSyntax(""); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - NickAlias *na = findnick(u->nick); - - if (!na) - { - source.Reply(NICK_NOT_REGISTERED); - return; - } - - na->last_realname = u->realname; - na->last_seen = Anope::CurTime; - - FOREACH_MOD(I_OnNickUpdate, OnNickUpdate(u)); - - source.Reply(_("Status updated (memos, vhost, chmodes, flags)."), Config->NickServ.c_str()); - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &) - { - 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, CORE), - commandnsupdate(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandnsupdate); - } -}; - -MODULE_INIT(NSUpdate) diff --git a/modules/core/os_akill.cpp b/modules/core/os_akill.cpp deleted file mode 100644 index 05602a746..000000000 --- a/modules/core/os_akill.cpp +++ /dev/null @@ -1,464 +0,0 @@ -/* OperServ core functions - * - * (C) 2003-2011 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 service_reference akills("xlinemanager/sgline"); - -class AkillDelCallback : public NumberList -{ - CommandSource &source; - unsigned Deleted; - public: - AkillDelCallback(CommandSource &_source, const Anope::string &numlist) : NumberList(numlist, true), source(_source), Deleted(0) - { - } - - ~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) - { - if (!Number) - return; - - XLine *x = akills->GetEntry(Number - 1); - - if (!x) - return; - - ++Deleted; - DoDel(source, x); - } - - static void DoDel(CommandSource &source, XLine *x) - { - akills->DelXLine(x); - } -}; - -class AkillListCallback : public NumberList -{ - protected: - CommandSource &source; - bool SentHeader; - public: - AkillListCallback(CommandSource &_source, const Anope::string &numlist) : NumberList(numlist, false), source(_source), SentHeader(false) - { - } - - ~AkillListCallback() - { - if (!SentHeader) - source.Reply(_("No matching entries on the AKILL list.")); - else - source.Reply(END_OF_ANY_LIST, "Akill"); - } - - void HandleNumber(unsigned Number) - { - if (!Number) - return; - - XLine *x = akills->GetEntry(Number - 1); - - if (!x) - return; - - if (!SentHeader) - { - SentHeader = true; - source.Reply(_("Current AKILL list:\n" - " Num Mask Reason")); - } - - DoList(source, x, Number); - } - - static void DoList(CommandSource &source, XLine *x, unsigned Number) - { - source.Reply(OPER_LIST_FORMAT, Number + 1, x->Mask.c_str(), x->Reason.c_str()); - } -}; - -class AkillViewCallback : public AkillListCallback -{ - public: - AkillViewCallback(CommandSource &_source, const Anope::string &numlist) : AkillListCallback(_source, numlist) - { - } - - void HandleNumber(unsigned Number) - { - if (!Number) - return; - - XLine *x = akills->GetEntry(Number - 1); - - if (!x) - return; - - if (!SentHeader) - { - SentHeader = true; - source.Reply(_("Current AKILL list:")); - } - - DoList(source, x, Number); - } - - static void DoList(CommandSource &source, XLine *x, unsigned Number) - { - source.Reply(OPER_VIEW_FORMAT, Number + 1, x->Mask.c_str(), x->By.c_str(), do_strftime(x->Created).c_str(), expire_left(source.u->Account(), x->Expires).c_str(), x->Reason.c_str()); - } -}; - -class CommandOSAKill : public Command -{ - private: - void DoAdd(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - unsigned last_param = 2; - Anope::string expiry, mask; - time_t expires; - - mask = params.size() > 1 ? params[1] : ""; - if (!mask.empty() && mask[0] == '+') - { - expiry = mask; - mask = params.size() > 2 ? params[2] : ""; - last_param = 3; - } - - expires = !expiry.empty() ? dotime(expiry) : Config->AutokillExpiry; - /* 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()) - { - std::pair canAdd = akills->CanAdd(mask, expires); - if (mask.find('!') != Anope::string::npos) - source.Reply(_("\002Reminder\002: AKILL masks cannot contain nicknames; make sure you have \002not\002 included a nick portion in your mask.")); - else if (mask.find('@') == Anope::string::npos) - source.Reply(BAD_USERHOST_MASK); - else if (mask.find_first_not_of("~@.*?") == Anope::string::npos) - source.Reply(USERHOST_MASK_TOO_WIDE, mask.c_str()); - else if (canAdd.first == 1) - source.Reply(_("\002%s\002 already exists on the AKILL list."), canAdd.second->Mask.c_str()); - else if (canAdd.first == 2) - source.Reply(_("Expiry time of \002%s\002 changed."), canAdd.second->Mask.c_str()); - else if (canAdd.first == 3) - source.Reply(_("\002%s\002 is already covered by %s."), mask.c_str(), canAdd.second->Mask.c_str()); - else - { - User *user = finduser(mask); - if (user) - mask = "*@" + user->host; - unsigned int affected = 0; - for (Anope::insensitive_map::iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it) - if (Anope::Match(it->second->GetIdent() + "@" + it->second->host, mask)) - ++affected; - float percent = static_cast(affected) / static_cast(UserListByNick.size()) * 100.0; - - if (percent > 95) - { - source.Reply(USERHOST_MASK_TOO_WIDE, mask.c_str()); - Log(LOG_ADMIN, u, this) << "tried to akill " << percent << "% of the network (" << affected << " users)"; - return; - } - - XLine *x = akills->Add(mask, u->nick, expires, reason); - - EventReturn MOD_RESULT; - FOREACH_RESULT(I_OnAddXLine, OnAddXLine(u, x, akills)); - if (MOD_RESULT == EVENT_STOP) - { - delete x; - return; - } - - source.Reply(_("\002%s\002 added to the AKILL list."), mask.c_str()); - - Log(LOG_ADMIN, u, this) << "on " << mask << " (" << reason << ") expires in " << (expires ? duration(expires - Anope::CurTime) : "never") << " [affects " << affected << " user(s) (" << percent << "%)]"; - - if (readonly) - source.Reply(READ_ONLY_MODE); - } - } - else - this->OnSyntaxError(source, "ADD"); - - return; - } - - void DoDel(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - 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); - 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; - } - - FOREACH_MOD(I_OnDelXLine, OnDelXLine(u, x, akills)); - - AkillDelCallback::DoDel(source, x); - source.Reply(_("\002%s\002 deleted from the AKILL list."), mask.c_str()); - } - - if (readonly) - source.Reply(READ_ONLY_MODE); - - return; - } - - void DoList(CommandSource &source, const std::vector ¶ms) - { - if (akills->GetList().empty()) - { - source.Reply(_("AKILL list is empty.")); - 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) - { - AkillListCallback list(source, mask); - list.Process(); - } - else - { - bool SentHeader = false; - - for (unsigned i = 0, end = akills->GetCount(); i < end; ++i) - { - XLine *x = akills->GetEntry(i); - - if (mask.empty() || mask.equals_ci(x->Mask) || Anope::Match(x->Mask, mask)) - { - if (!SentHeader) - { - SentHeader = true; - source.Reply(_("Current AKILL list:\n" - " Num Mask Reason")); - } - - AkillListCallback::DoList(source, x, i); - } - } - - if (!SentHeader) - source.Reply(_("No matching entries on the AKILL list.")); - else - source.Reply(END_OF_ANY_LIST, "Akill"); - } - - return; - } - - void DoView(CommandSource &source, const std::vector ¶ms) - { - if (akills->GetList().empty()) - { - source.Reply(_("AKILL list is empty.")); - 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) - { - AkillViewCallback list(source, mask); - list.Process(); - } - else - { - bool SentHeader = false; - - for (unsigned i = 0, end = akills->GetCount(); i < end; ++i) - { - XLine *x = akills->GetEntry(i); - - if (mask.empty() || mask.equals_ci(x->Mask) || Anope::Match(x->Mask, mask)) - { - if (!SentHeader) - { - SentHeader = true; - source.Reply(_("Current AKILL list:")); - } - - AkillViewCallback::DoList(source, x, i); - } - } - - if (!SentHeader) - source.Reply(_("No matching entries on the AKILL list.")); - } - - return; - } - - void DoClear(CommandSource &source) - { - User *u = source.u; - FOREACH_MOD(I_OnDelXLine, OnDelXLine(u, NULL, akills)); - akills->Clear(); - source.Reply(_("The AKILL list has been cleared.")); - - return; - } - public: - CommandOSAKill(Module *creator) : Command(creator, "operserv/akill", 1, 4) - { - 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}")); - this->SetSyntax(_("LIST [\037mask\037 | \037list\037]")); - this->SetSyntax(_("VIEW [\037mask\037 | \037list\037]")); - this->SetSyntax(_("CLEAR")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - 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) - { - 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 (K-line) for\n" - "the mask which the user matched.\n" - " \n" - "\002AKILL ADD\002 adds the given nick or user@host/ip mask to the AKILL\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 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.\n" - " \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, CORE), - commandosakill(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandosakill); - } -}; - -MODULE_INIT(OSAKill) diff --git a/modules/core/os_chankill.cpp b/modules/core/os_chankill.cpp deleted file mode 100644 index 9ce895c59..000000000 --- a/modules/core/os_chankill.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/* OperServ core functions - * - * (C) 2003-2011 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 service_reference akills("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 ¶ms) - { - if (!akills) - return; - - User *u = source.u; - Anope::string expiry, channel; - time_t expires; - unsigned last_param = 1; - Channel *c; - - channel = params[0]; - if (!channel.empty() && channel[0] == '+') - { - expiry = channel; - channel = params[1]; - last_param = 2; - } - - expires = !expiry.empty() ? dotime(expiry) : Config->ChankillExpiry; - 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->AddAkiller) - realreason = "[" + u->nick + "] " + reason; - else - realreason = reason; - - if ((c = findchan(channel))) - { - for (CUserList::iterator it = c->users.begin(), it_end = c->users.end(); it != it_end; ) - { - UserContainer *uc = *it++; - - if (uc->user->HasMode(UMODE_OPER)) - continue; - - akills->Add("*@" + uc->user->host, u->nick, expires, realreason); - akills->Check(uc->user); - } - - Log(LOG_ADMIN, u, this) << "(" << realreason << ")"; - } - else - source.Reply(CHAN_X_NOT_IN_USE, channel.c_str()); - } - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Puts an AKILL for every nick on the specified channel. It\n" - "uses the entire and complete real ident@host for every nick,\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, CORE), - commandoschankill(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandoschankill); - } -}; - -MODULE_INIT(OSChanKill) diff --git a/modules/core/os_config.cpp b/modules/core/os_config.cpp deleted file mode 100644 index 1d1fead33..000000000 --- a/modules/core/os_config.cpp +++ /dev/null @@ -1,217 +0,0 @@ -/* OperServ core functions - * - * (C) 2003-2011 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 CommandOSConfig : public Command -{ - void ChangeHash(ConfigDataHash &hash, const Anope::string &block, const Anope::string &iname, const Anope::string &value) - { - ConfigDataHash::iterator it = hash.find(block); - - KeyValList &list = it->second; - for (unsigned i = 0; i < list.size(); ++i) - { - const Anope::string &item = list[i].first; - if (item == iname) - { - list[i] = std::make_pair(item, value); - break; - } - } - } - - public: - CommandOSConfig(Module *creator) : Command(creator, "operserv/config", 1, 4) - { - this->SetDesc(_("View and change configuration file settings")); - this->SetSyntax(_("{\037MODIFY\037|\037VIEW\037} [\037block name\037 \037item name\037 \037item value\037]")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - const Anope::string &what = params[0]; - - if (what.equals_ci("MODIFY") && params.size() > 3) - { - ConfigItems configitems(Config); - - for (unsigned i = 0; !configitems.Values[i].tag.empty(); ++i) - { - ConfigItems::Item *v = &configitems.Values[i]; - if (v->tag.equals_cs(params[1]) && v->value.equals_cs(params[2])) - { - try - { - ValueItem vi(params[3]); - if (!v->validation_function(Config, v->tag, v->value, vi)) - throw ConfigException("Parameter failed to validate."); - - int dt = v->datatype; - - if (dt & DT_NORELOAD) - throw ConfigException("This item can not be changed while Anope is running."); - bool allow_wild = dt & DT_ALLOW_WILD; - dt &= ~(DT_ALLOW_NEWLINE | DT_ALLOW_WILD); - - /* Yay for *massive* copypaste from config.cpp */ - switch (dt) - { - case DT_NOSPACES: - { - ValueContainerString *vcs = debug_cast(v->val); - Config->ValidateNoSpaces(vi.GetValue(), v->tag, v->value); - vcs->Set(vi.GetValue()); - break; - } - case DT_HOSTNAME: - { - ValueContainerString *vcs = debug_cast(v->val); - Config->ValidateHostname(vi.GetValue(), v->tag, v->value); - vcs->Set(vi.GetValue()); - break; - } - case DT_IPADDRESS: - { - ValueContainerString *vcs = debug_cast(v->val); - Config->ValidateIP(vi.GetValue(), v->tag, v->value, allow_wild); - vcs->Set(vi.GetValue()); - break; - } - case DT_STRING: - { - ValueContainerString *vcs = debug_cast(v->val); - vcs->Set(vi.GetValue()); - break; - } - case DT_INTEGER: - { - int val = vi.GetInteger(); - ValueContainerInt *vci = debug_cast(v->val); - vci->Set(&val, sizeof(int)); - break; - } - case DT_UINTEGER: - { - unsigned val = vi.GetInteger(); - ValueContainerUInt *vci = debug_cast(v->val); - vci->Set(&val, sizeof(unsigned)); - break; - } - case DT_LUINTEGER: - { - unsigned long val = vi.GetInteger(); - ValueContainerLUInt *vci = debug_cast(v->val); - vci->Set(&val, sizeof(unsigned long)); - break; - } - case DT_TIME: - { - time_t time = dotime(vi.GetValue()); - ValueContainerTime *vci = debug_cast(v->val); - vci->Set(&time, sizeof(time_t)); - break; - } - case DT_BOOLEAN: - { - bool val = vi.GetBool(); - ValueContainerBool *vcb = debug_cast(v->val); - vcb->Set(&val, sizeof(bool)); - break; - } - default: - break; - } - } - catch (const ConfigException &ex) - { - source.Reply(_("Error changing configuration value: ") + ex.GetReason()); - return; - } - - ChangeHash(Config->config_data, params[1], params[2], params[3]); - - Log(LOG_ADMIN, source.u, 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()); - return; - } - } - - source.Reply("There is no configuration value named %s:%s", params[1].c_str(), params[2].c_str()); - } - else if (what.equals_ci("VIEW")) - { - /* Blocks we should show */ - const Anope::string show_blocks[] = { "botserv", "chanserv", "defcon", "global", "memoserv", "nickserv", "networkinfo", "operserv", "options", "" }; - - Log(LOG_ADMIN, source.u, this) << "VIEW"; - - for (ConfigDataHash::const_iterator it = Config->config_data.begin(), it_end = Config->config_data.end(); it != it_end; ++it) - { - const Anope::string &bname = it->first; - const KeyValList &list = it->second; - - bool ok = false; - for (unsigned i = 0; !show_blocks[i].empty(); ++i) - if (bname == show_blocks[i]) - ok = true; - if (ok == false) - continue; - - source.Reply(_("%s settings:"), bname.c_str()); - for (unsigned i = 0; i < list.size(); ++i) - { - const Anope::string &first = list[i].first, second = list[i].second; - - source.Reply(_(" Name: %-15s Value: %s"), first.c_str(), second.c_str()); - } - } - - source.Reply(_("End of configuration.")); - } - else - this->OnSyntaxError(source, what); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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 RELOAD command is used.\n" - " \n" - "Example:\n" - " \002MODIFY nickserv forcemail no\002")); - return true; - } -}; - -class OSConfig : public Module -{ - CommandOSConfig commandosconfig; - - public: - OSConfig(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandosconfig(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandosconfig); - } -}; - -MODULE_INIT(OSConfig) diff --git a/modules/core/os_forbid.cpp b/modules/core/os_forbid.cpp deleted file mode 100644 index d4b4c2b41..000000000 --- a/modules/core/os_forbid.cpp +++ /dev/null @@ -1,342 +0,0 @@ -/* OperServ core functions - * - * (C) 2003-2011 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 "os_forbid.h" - -class MyForbidService : public ForbidService -{ - std::vector forbidData; - - public: - MyForbidService(Module *m) : ForbidService(m) { } - - void AddForbid(ForbidData *d) - { - this->forbidData.push_back(d); - } - - void RemoveForbid(ForbidData *d) - { - std::vector::iterator it = std::find(this->forbidData.begin(), this->forbidData.end(), d); - if (it != this->forbidData.end()) - this->forbidData.erase(it); - delete d; - } - - ForbidData *FindForbid(const Anope::string &mask, ForbidType type) - { - const std::vector &forbids = this->GetForbids(); - for (unsigned i = forbids.size(); i > 0; --i) - { - ForbidData *d = this->forbidData[i - 1]; - - if ((type == FT_NONE || type == d->type) && Anope::Match(mask, d->mask)) - return d; - } - return NULL; - } - - const std::vector &GetForbids() - { - for (unsigned i = this->forbidData.size(); i > 0; --i) - { - ForbidData *d = this->forbidData[i - 1]; - - if (d->expires && Anope::CurTime >= d->expires) - { - Anope::string type = "none"; - if (d->type == FT_NICK) - type = "nick"; - else if (d->type == FT_CHAN) - type = "chan"; - else if (d->type == FT_EMAIL) - type = "email"; - - Log(LOG_NORMAL, Config->OperServ + "/forbid") << "Expiring forbid for " << d->mask << " type " << type; - this->forbidData.erase(this->forbidData.begin() + i - 1); - delete d; - } - } - - return this->forbidData; - } -}; - -class CommandOSForbid : public Command -{ - service_reference fs; - public: - CommandOSForbid(Module *creator) : Command(creator, "operserv/forbid", 1, 5), fs("forbid") - { - this->SetDesc(_("Forbid usage of nicknames, channels, and emails")); - this->SetSyntax(_("ADD {NICK|CHAN|EMAIL} [+\037expiry\037] \037entry\037\002 [\037reason\037]")); - this->SetSyntax(_("DEL {NICK|CHAN|EMAIL} \037entry\037")); - this->SetSyntax(_("LIST (NICK|CHAN|EMAIL)")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - if (!this->fs) - return; - - const Anope::string &command = params[0]; - const Anope::string &subcommand = params.size() > 1 ? params[1] : ""; - - ForbidType type = FT_NONE; - if (subcommand.equals_ci("NICK")) - type = FT_NICK; - else if (subcommand.equals_ci("CHAN")) - type = FT_CHAN; - else if (subcommand.equals_ci("EMAIL")) - type = FT_EMAIL; - - if (command.equals_ci("ADD") && params.size() > 2 && type != FT_NONE) - { - const Anope::string &expiry = params[2][0] == '+' ? params[2] : ""; - const Anope::string &entry = params.size() > 3 && !expiry.empty() ? params[3] : params[2]; - Anope::string reason = !expiry.empty() && params.size() > 4 ? params[4] : (params.size() > 3 ? params[3] : ""); - - time_t expiryt = 0; - if (!expiry.empty()) - { - expiryt = dotime(expiry); - if (expiryt) - expiryt += Anope::CurTime; - } - - ForbidData *d = this->fs->FindForbid(entry, type); - bool created = false; - if (d == NULL) - { - d = new ForbidData(); - created = true; - } - - d->mask = entry; - d->creator = source.u->nick; - d->reason = reason; - d->created = Anope::CurTime; - d->expires = expiryt; - d->type = type; - if (created) - this->fs->AddForbid(d); - - Log(LOG_ADMIN, source.u, this) << "to add a forbid on " << entry << " of type " << subcommand; - source.Reply(_("Added a%s forbid on %s to expire on %s"), type == FT_CHAN ? "n" : "", entry.c_str(), d->expires ? do_strftime(d->expires).c_str() : "never"); - } - else if (command.equals_ci("DEL") && params.size() > 2 && type != FT_NONE) - { - const Anope::string &entry = params[2]; - - ForbidData *d = this->fs->FindForbid(entry, type); - if (d != NULL) - { - Log(LOG_ADMIN, source.u, this) << "to remove forbid " << d->mask << " of type " << subcommand; - source.Reply(_("%s deleted from the %s forbid list."), d->mask.c_str(), subcommand.c_str()); - } - else - source.Reply(_("Forbid on %s was not found."), entry.c_str()); - } - else if (command.equals_ci("LIST")) - { - const std::vector &forbids = this->fs->GetForbids(); - if (forbids.empty()) - source.Reply(_("Forbid list is empty.")); - else - { - source.Reply(_("Forbid list:")); - source.Reply(_("Mask Type Reason")); - - for (unsigned i = 0; i < forbids.size(); ++i) - { - ForbidData *d = forbids[i]; - - Anope::string ftype; - if (d->type == FT_NICK) - ftype = "NICK"; - else if (d->type == FT_CHAN) - ftype = "CHAN"; - else if (d->type == FT_EMAIL) - ftype = "EMAIL"; - else - continue; - - source.Reply("%-10s %-5s %s", d->mask.c_str(), ftype.c_str(), d->reason.c_str()); - source.Reply(_("By %s, expires on %s"), d->creator.c_str(), d->expires ? do_strftime(d->expires).c_str() : "never"); - } - - source.Reply(_("End of forbid list.")); - } - } - else - this->OnSyntaxError(source, command); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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.")); - return true; - } -}; - -class OSForbid : public Module -{ - MyForbidService forbidService; - CommandOSForbid commandosforbid; - - public: - OSForbid(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - forbidService(this), commandosforbid(this) - { - this->SetAuthor("Anope"); - - Implementation i[] = { I_OnUserConnect, I_OnUserNickChange, I_OnJoinChannel, I_OnPreCommand, I_OnDatabaseWrite, I_OnDatabaseRead }; - ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); - - ModuleManager::RegisterService(&this->forbidService); - - ModuleManager::RegisterService(&commandosforbid); - } - - void OnUserConnect(dynamic_reference &u, bool &exempt) - { - if (!u || exempt) - return; - - this->OnUserNickChange(u, ""); - } - - void OnUserNickChange(User *u, const Anope::string &) - { - if (u->HasMode(UMODE_OPER)) - return; - - ForbidData *d = this->forbidService.FindForbid(u->nick, FT_NICK); - if (d != NULL) - { - BotInfo *bi = findbot(Config->OperServ); - if (bi) - { - if (d->reason.empty()) - u->SendMessage(bi, _("This nickname has been forbidden.")); - else - u->SendMessage(bi, _("This nickname has been forbidden: %s"), d->reason.c_str()); - } - u->Collide(NULL); - } - } - - void OnJoinChannel(User *u, Channel *c) - { - if (u->HasMode(UMODE_OPER)) - return; - - BotInfo *bi = findbot(Config->OperServ); - ForbidData *d = this->forbidService.FindForbid(c->name, FT_CHAN); - if (bi != NULL && d != NULL) - { - if (!c->HasFlag(CH_INHABIT)) - { - /* Join ChanServ and set a timer for this channel to part ChanServ later */ - new ChanServTimer(c); - - /* Set +si to prevent rejoin */ - c->SetMode(NULL, CMODE_NOEXTERNAL); - c->SetMode(NULL, CMODE_TOPIC); - c->SetMode(NULL, CMODE_SECRET); - c->SetMode(NULL, CMODE_INVITE); - } - - if (d->reason.empty()) - c->Kick(bi, u, _("This channel has been forbidden.")); - else - c->Kick(bi, u, _("This channel has been forbidden: %s"), d->reason.c_str()); - } - } - - EventReturn OnPreCommand(CommandSource &source, Command *command, std::vector ¶ms) - { - if (source.u->HasMode(UMODE_OPER)) - return EVENT_CONTINUE; - else if (command->name == "nickserv/register" && params.size() > 1) - { - ForbidData *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; - } - } - - return EVENT_CONTINUE; - } - - void OnDatabaseWrite(void (*Write)(const Anope::string &)) - { - std::vector forbids = this->forbidService.GetForbids(); - for (unsigned i = 0; i < forbids.size(); ++i) - { - ForbidData *f = forbids[i]; - Anope::string ftype; - if (f->type == FT_NICK) - ftype = "NICK"; - else if (f->type == FT_CHAN) - ftype = "CHAN"; - else if (f->type == FT_EMAIL) - ftype = "EMAIL"; - Write("FORBID " + f->mask + " " + f->creator + " " + stringify(f->created) + " " + stringify(f->expires) + " " + ftype + " " + f->reason); - } - } - - EventReturn OnDatabaseRead(const std::vector ¶ms) - { - if (params.size() > 5 && params[0] == "FORBID") - { - ForbidData *f = new ForbidData(); - f->mask = params[1]; - f->creator = params[2]; - f->created = convertTo(params[3]); - f->expires = convertTo(params[4]); - if (params[5] == "NICK") - f->type = FT_NICK; - else if (params[5] == "CHAN") - f->type = FT_CHAN; - else if (params[5] == "EMAIL") - f->type = FT_EMAIL; - else - f->type = FT_NONE; - f->reason = params.size() > 6 ? params[6] : ""; - - this->forbidService.AddForbid(f); - } - - return EVENT_CONTINUE; - } -}; - -MODULE_INIT(OSForbid) diff --git a/modules/core/os_forbid.h b/modules/core/os_forbid.h deleted file mode 100644 index b91aa3be8..000000000 --- a/modules/core/os_forbid.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef OS_FORBID_H -#define OS_FORBID_H - -enum ForbidType -{ - FT_NONE, - FT_NICK, - FT_CHAN, - FT_EMAIL -}; - -struct ForbidData -{ - Anope::string mask; - Anope::string creator; - Anope::string reason; - time_t created; - time_t expires; - ForbidType type; -}; - -class ForbidService : public Service -{ - public: - ForbidService(Module *m) : Service(m, "forbid") { } - - virtual void AddForbid(ForbidData *d) = 0; - - virtual void RemoveForbid(ForbidData *d) = 0; - - virtual ForbidData *FindForbid(const Anope::string &mask, ForbidType type) = 0; - - virtual const std::vector &GetForbids() = 0; -}; - -#endif - diff --git a/modules/core/os_ignore.cpp b/modules/core/os_ignore.cpp deleted file mode 100644 index 9f50ec967..000000000 --- a/modules/core/os_ignore.cpp +++ /dev/null @@ -1,350 +0,0 @@ -/* OperServ core functions - * - * (C) 2003-2011 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 "os_ignore.h" - -class OSIgnoreService : public IgnoreService -{ - public: - OSIgnoreService(Module *o, const Anope::string &n) : IgnoreService(o, n) { } - - void AddIgnore(const Anope::string &mask, const Anope::string &creator, const Anope::string &reason, time_t delta = Anope::CurTime) - { - /* If it s an existing user, we ignore the hostmask. */ - Anope::string realmask = mask; - size_t user, host; - - User *u = finduser(mask); - if (u) - realmask = "*!*@" + u->host; - /* Determine whether we get a nick or a mask. */ - else if ((host = mask.find('@')) != Anope::string::npos) - { - /* Check whether we have a nick too.. */ - if ((user = mask.find('!')) != Anope::string::npos) - { - /* this should never happen */ - if (user > host) - return; - } - else - /* We have user@host. Add nick wildcard. */ - realmask = "*!" + mask; - } - /* We only got a nick.. */ - else - realmask = mask + "!*@*"; - - /* Check if we already got an identical entry. */ - IgnoreData *ign = this->Find(realmask); - if (ign != NULL) - { - if (!delta) - ign->time = 0; - else - ign->time = Anope::CurTime + delta; - } - /* Create new entry.. */ - else - { - IgnoreData newign; - newign.mask = realmask; - newign.creator = creator; - newign.reason = reason; - newign.time = delta ? Anope::CurTime + delta : 0; - this->ignores.push_back(newign); - } - } - - bool DelIgnore(const Anope::string &mask) - { - for (std::list::iterator it = this->ignores.begin(), it_end = this->ignores.end(); it != it_end; ++it) - { - IgnoreData &idn = *it; - if (idn.mask.equals_ci(mask)) - { - this->ignores.erase(it); - return true; - } - } - - return false; - } - - IgnoreData *Find(const Anope::string &mask) - { - User *u = finduser(mask); - std::list::iterator ign = this->ignores.begin(), ign_end = this->ignores.end(); - - if (u) - { - for (; ign != ign_end; ++ign) - { - Entry ignore_mask(CMODE_BEGIN, ign->mask); - if (ignore_mask.Matches(u)) - 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)) - break; - } - - /* Check whether the entry has timed out */ - if (ign != ign_end)// && (*ign)->time && (*ign)->time <= Anope::CurTime) - { - IgnoreData &id = *ign; - - if (id.time && id.time <= Anope::CurTime) - { - Log(LOG_DEBUG) << "Expiring ignore entry " << id.mask; - this->ignores.erase(ign); - } - else - return &id; - } - - return NULL; - } -}; - -class CommandOSIgnore : public Command -{ - private: - void DoAdd(CommandSource &source, const std::vector ¶ms) - { - service_reference ignore_service("ignore"); - 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 = dotime(time); - - if (t <= -1) - { - source.Reply(_("You have to enter a valid number as time.")); - return; - } - - ignore_service->AddIgnore(nick, source.u->nick, reason, t); - if (!t) - source.Reply(_("\002%s\002 will now permanently be ignored."), nick.c_str()); - else - source.Reply(_("\002%s\002 will now be ignored for \002%s\002."), nick.c_str(), time.c_str()); - } - - return; - } - - void DoList(CommandSource &source) - { - service_reference ignore_service("ignore"); - if (!ignore_service) - return; - - const std::list &ignores = ignore_service->GetIgnores(); - if (ignores.empty()) - source.Reply(_("Ignore list is empty.")); - else - { - source.Reply(_("Services ignore list:\n" - " Mask Creator Reason Expires")); - for (std::list::const_iterator ign = ignores.begin(), ign_end = ignores.end(); ign != ign_end; ++ign) - { - const IgnoreData &ignore = *ign; - - source.Reply(" %-11s %-11s %-11s %s", ignore.mask.c_str(), ignore.creator.c_str(), ignore.reason.c_str(), do_strftime(ignore.time).c_str()); - } - } - - return; - } - - void DoDel(CommandSource &source, const std::vector ¶ms) - { - service_reference ignore_service("ignore"); - if (!ignore_service) - return; - - const Anope::string nick = params.size() > 1 ? params[1] : ""; - if (nick.empty()) - this->OnSyntaxError(source, "DEL"); - else if (ignore_service->DelIgnore(nick)) - source.Reply(_("\002%s\002 will no longer be ignored."), nick.c_str()); - else - source.Reply(_("Nick \002%s\002 not found on ignore list."), nick.c_str()); - - return; - } - - void DoClear(CommandSource &source) - { - service_reference ignore_service("ignore"); - if (!ignore_service) - return; - - ignore_service->ClearIgnores(); - 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 \037time\037 \037nick\037 \037reason\037")); - this->SetSyntax(_("DEL \037nick\037")); - this->SetSyntax(_("LIST")); - this->SetSyntax(_("CLEAR")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - 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) - { - 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 user@host\n" - "or nick!user@host, everything else will be considered a nick.\n" - "Wildcards are permitted.\n" - " \n" - "Ignores will not be enforced on IRC Operators.")); - return true; - } -}; - -class OSIgnore : public Module -{ - OSIgnoreService osignoreservice; - CommandOSIgnore commandosignore; - - public: - OSIgnore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - osignoreservice(this, "ignore"), commandosignore(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandosignore); - - Implementation i[] = { I_OnDatabaseRead, I_OnDatabaseWrite, I_OnBotPrivmsg }; - ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); - - ModuleManager::RegisterService(&this->osignoreservice); - } - - EventReturn OnDatabaseRead(const std::vector ¶ms) - { - if (params.size() >= 4 && params[0].equals_ci("OS") && params[1].equals_ci("IGNORE")) - { - service_reference ignore_service("ignore"); - if (ignore_service) - { - const Anope::string &mask = params[2]; - time_t time = params[3].is_pos_number_only() ? convertTo(params[3]) : 0; - const Anope::string &creator = params.size() > 4 ? params[4] : ""; - const Anope::string &reason = params.size() > 5 ? params[5] : ""; - ignore_service->AddIgnore(mask, creator, reason, time - Anope::CurTime); - - return EVENT_STOP; - } - } - - return EVENT_CONTINUE; - } - - void OnDatabaseWrite(void (*Write)(const Anope::string &)) - { - for (std::list::iterator ign = this->osignoreservice.GetIgnores().begin(), ign_end = this->osignoreservice.GetIgnores().end(); ign != ign_end; ) - { - if (ign->time && ign->time <= Anope::CurTime) - { - Log(LOG_DEBUG) << "[os_ignore] Expiring ignore entry " << ign->mask; - ign = this->osignoreservice.GetIgnores().erase(ign); - } - else - { - std::stringstream buf; - buf << "OS IGNORE " << ign->mask << " " << ign->time << " " << ign->creator << " :" << ign->reason; - Write(buf.str()); - ++ign; - } - } - } - - EventReturn OnBotPrivmsg(User *u, BotInfo *bi, Anope::string &message) - { - if (this->osignoreservice.Find(u->nick)) - return EVENT_STOP; - - return EVENT_CONTINUE; - } -}; - -MODULE_INIT(OSIgnore) diff --git a/modules/core/os_ignore.h b/modules/core/os_ignore.h deleted file mode 100644 index 056161a79..000000000 --- a/modules/core/os_ignore.h +++ /dev/null @@ -1,39 +0,0 @@ -/* OperServ ignore interface - * - * (C) 2003-2011 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. - */ - - -struct IgnoreData -{ - Anope::string mask; - Anope::string creator; - Anope::string reason; - time_t time; /* When do we stop ignoring them? */ -}; - -class IgnoreService : public Service -{ - protected: - std::list ignores; - - IgnoreService(Module *c, const Anope::string &n) : Service(c, n) { } - - public: - virtual void AddIgnore(const Anope::string &mask, const Anope::string &creator, const Anope::string &reason, time_t delta = Anope::CurTime) = 0; - - virtual bool DelIgnore(const Anope::string &mask) = 0; - - inline void ClearIgnores() { this->ignores.clear(); } - - virtual IgnoreData *Find(const Anope::string &mask) = 0; - - inline std::list &GetIgnores() { return this->ignores; } -}; - diff --git a/modules/core/os_jupe.cpp b/modules/core/os_jupe.cpp deleted file mode 100644 index a689b33e2..000000000 --- a/modules/core/os_jupe.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/* OperServ core functions - * - * (C) 2003-2011 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 ¶ms) - { - User *u = source.u; - const Anope::string &jserver = params[0]; - const Anope::string &reason = params.size() > 1 ? params[1] : ""; - Server *server = Server::Find(jserver); - - if (!isValidHost(jserver, 3)) - source.Reply(_("Please use a valid server name when juping")); - else if (server && (server == Me || server == Me->GetLinks().front())) - source.Reply(_("You can not jupe your services server or your uplink server.")); - else - { - Anope::string rbuf = "Juped by " + u->nick + (!reason.empty() ? ": " + reason : ""); - if (server) - ircdproto->SendSquit(jserver, rbuf); - Server *juped_server = new Server(Me, jserver, 1, rbuf, ircd->ts6 ? ts6_sid_retrieve() : "", SERVER_JUPED); - ircdproto->SendServer(juped_server); - - Log(LOG_ADMIN, u, this) << "on " << jserver << " (" << rbuf << ")"; - } - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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 \", 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, CORE), - commandosjupe(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandosjupe); - } -}; - -MODULE_INIT(OSJupe) diff --git a/modules/core/os_kick.cpp b/modules/core/os_kick.cpp deleted file mode 100644 index 341ea1ee6..000000000 --- a/modules/core/os_kick.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/* OperServ core functions - * - * (C) 2003-2011 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 ¶ms) - { - User *u = source.u; - const Anope::string &chan = params[0]; - const Anope::string &nick = params[1]; - const Anope::string &s = params[2]; - Channel *c; - User *u2; - - if (!(c = findchan(chan))) - { - source.Reply(CHAN_X_NOT_IN_USE, chan.c_str()); - return; - } - else if (c->bouncy_modes) - { - source.Reply(_("Services is unable to change modes. Are your servers' U:lines configured correctly?")); - return; - } - else if (!(u2 = finduser(nick))) - { - source.Reply(NICK_X_NOT_IN_USE, nick.c_str()); - return; - } - - c->Kick(source.owner, u2, "%s (%s)", u->nick.c_str(), s.c_str()); - Log(LOG_ADMIN, u, this) << "on " << u2->nick << " in " << c->name; - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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.owner->nick.c_str()); - return true; - } -}; - -class OSKick : public Module -{ - CommandOSKick commandoskick; - - public: - OSKick(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandoskick(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandoskick); - } -}; - -MODULE_INIT(OSKick) diff --git a/modules/core/os_kill.cpp b/modules/core/os_kill.cpp deleted file mode 100644 index 11a0636ef..000000000 --- a/modules/core/os_kill.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* OperServ core functions - * - * (C) 2003-2011 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 CommandOSKill : public Command -{ - public: - CommandOSKill(Module *creator) : Command(creator, "operserv/kill", 1, 2) - { - this->SetDesc(_("Kill a user")); - this->SetSyntax(_("\037user\037 [\037reason\037]")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - const Anope::string &nick = params[0]; - Anope::string reason = params.size() > 1 ? params[1] : ""; - - User *u2 = finduser(nick); - if (u2 == NULL) - source.Reply(NICK_X_NOT_IN_USE, nick.c_str()); - else if (u2->IsProtected() || u2->server == Me) - source.Reply(ACCESS_DENIED); - else - { - if (reason.empty()) - reason = "No reason specified"; - if (Config->AddAkiller) - reason = "(" + u->nick + ") " + reason; - Log(LOG_ADMIN, u, this) << "on " << u2->nick << " for " << reason; - u2->Kill(Config->OperServ, reason); - } - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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.")); - return true; - } -}; - -class OSKill : public Module -{ - CommandOSKill commandoskill; - - public: - OSKill(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandoskill(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandoskill); - } -}; - -MODULE_INIT(OSKill) diff --git a/modules/core/os_list.cpp b/modules/core/os_list.cpp deleted file mode 100644 index 9a582f97b..000000000 --- a/modules/core/os_list.cpp +++ /dev/null @@ -1,187 +0,0 @@ -/* OperServ core functions - * - * (C) 2003-2011 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 CommandOSChanList : public Command -{ - public: - CommandOSChanList(Module *creator) : Command(creator, "operserv/chanlist", 0, 2) - { - this->SetDesc(_("Lists all channel records")); - this->SetSyntax(_("[{\037pattern\037 | \037nick\037} [\037SECRET\037]]")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - const Anope::string &pattern = !params.empty() ? params[0] : ""; - const Anope::string &opt = params.size() > 1 ? params[1] : ""; - std::list Modes; - User *u2; - - if (!opt.empty() && opt.equals_ci("SECRET")) - { - Modes.push_back(CMODE_SECRET); - Modes.push_back(CMODE_PRIVATE); - } - - if (!pattern.empty() && (u2 = finduser(pattern))) - { - source.Reply(_("\002%s\002 channel list:\n" - "Name Users Modes Topic"), u2->nick.c_str()); - - for (UChannelList::iterator uit = u2->chans.begin(), uit_end = u2->chans.end(); uit != uit_end; ++uit) - { - ChannelContainer *cc = *uit; - - if (!Modes.empty()) - for (std::list::iterator it = Modes.begin(), it_end = Modes.end(); it != it_end; ++it) - if (!cc->chan->HasMode(*it)) - continue; - - source.Reply(_("%-20s %4d +%-6s %s"), cc->chan->name.c_str(), cc->chan->users.size(), cc->chan->GetModes(true, true).c_str(), !cc->chan->topic.empty() ? cc->chan->topic.c_str() : ""); - } - } - else - { - source.Reply(_("Channel list:\n" - "Name Users Modes Topic")); - - for (channel_map::const_iterator cit = ChannelList.begin(), cit_end = ChannelList.end(); cit != cit_end; ++cit) - { - Channel *c = cit->second; - - if (!pattern.empty() && !Anope::Match(c->name, pattern)) - continue; - if (!Modes.empty()) - for (std::list::iterator it = Modes.begin(), it_end = Modes.end(); it != it_end; ++it) - if (!c->HasMode(*it)) - continue; - - source.Reply(_("%-20s %4d +%-6s %s"), c->name.c_str(), c->users.size(), c->GetModes(true, true).c_str(), !c->topic.empty() ? c->topic.c_str() : ""); - } - } - - source.Reply(_("End of channel list.")); - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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" - "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.")); - return true; - } -}; - -class CommandOSUserList : public Command -{ - public: - CommandOSUserList(Module *creator) : Command(creator, "operserv/userlist", 0, 2) - { - this->SetDesc(_("Lists all user records")); - this->SetSyntax(_("[{\037pattern\037 | \037channel\037} [\037INVISIBLE\037]]")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - const Anope::string &pattern = !params.empty() ? params[0] : ""; - const Anope::string &opt = params.size() > 1 ? params[1] : ""; - Channel *c; - std::list Modes; - - if (!opt.empty() && opt.equals_ci("INVISIBLE")) - Modes.push_back(UMODE_INVIS); - - if (!pattern.empty() && (c = findchan(pattern))) - { - source.Reply(_("\002%s\002 users list:\n" - "Nick Mask"), pattern.c_str()); - - for (CUserList::iterator cuit = c->users.begin(), cuit_end = c->users.end(); cuit != cuit_end; ++cuit) - { - UserContainer *uc = *cuit; - - if (!Modes.empty()) - for (std::list::iterator it = Modes.begin(), it_end = Modes.end(); it != it_end; ++it) - if (!uc->user->HasMode(*it)) - continue; - - source.Reply(_("%-20s %s@%s"), uc->user->nick.c_str(), uc->user->GetIdent().c_str(), uc->user->GetDisplayedHost().c_str()); - } - } - else - { - source.Reply(_("Users list:\n" - "Nick Mask")); - - for (Anope::insensitive_map::iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it) - { - User *u2 = it->second; - - if (!pattern.empty()) - { - Anope::string mask = u2->nick + "!" + u2->GetIdent() + "@" + u2->GetDisplayedHost(); - if (!Anope::Match(mask, pattern)) - continue; - if (!Modes.empty()) - for (std::list::iterator mit = Modes.begin(), mit_end = Modes.end(); mit != mit_end; ++mit) - if (!u2->HasMode(*mit)) - continue; - } - source.Reply(_("%-20s %s@%s"), u2->nick.c_str(), u2->GetIdent().c_str(), u2->GetDisplayedHost().c_str()); - } - } - - source.Reply(_("End of users list.")); - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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.")); - return true; - } -}; - -class OSList : public Module -{ - CommandOSChanList commandoschanlist; - CommandOSUserList commandosuserlist; - - public: - OSList(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandoschanlist(this), commandosuserlist(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandoschanlist); - ModuleManager::RegisterService(&commandosuserlist); - } -}; - -MODULE_INIT(OSList) diff --git a/modules/core/os_login.cpp b/modules/core/os_login.cpp deleted file mode 100644 index 94a82e334..000000000 --- a/modules/core/os_login.cpp +++ /dev/null @@ -1,97 +0,0 @@ -/* OperServ core functions - * - * (C) 2003-2011 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->SetDesc(Anope::printf(_("Login to %s"), Config->OperServ.c_str())); - this->SetSyntax(_("\037password\037")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - const Anope::string &password = params[0]; - - Oper *o = source.u->Account()->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 (source.u->GetExt("os_login_password_correct")) - source.Reply(_("You are already identified.")); - else if (o->password != password) - { - source.Reply(PASSWORD_INCORRECT); - bad_password(source.u); - } - else - { - Log(LOG_ADMIN, source.u, this) << "and successfully identified to " << source.owner->nick; - source.u->Extend("os_login_password_correct"); - source.Reply(_("Password accepted.")); - } - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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.owner->nick.c_str()); - return true; - } -}; - -class OSLogin : public Module -{ - CommandOSLogin commandoslogin; - - public: - OSLogin(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandoslogin(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandoslogin); - - ModuleManager::Attach(I_IsServicesOper, this); - } - - ~OSLogin() - { - for (Anope::insensitive_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it) - it->second->Shrink("os_login_password_correct"); - } - - EventReturn IsServicesOper(User *u) - { - if (!u->Account()->o->password.empty()) - { - if (u->GetExt("os_login_password_correct")) - return EVENT_ALLOW; - return EVENT_STOP; - } - - return EVENT_CONTINUE; - } -}; - -MODULE_INIT(OSLogin) diff --git a/modules/core/os_main.cpp b/modules/core/os_main.cpp deleted file mode 100644 index b555b9ede..000000000 --- a/modules/core/os_main.cpp +++ /dev/null @@ -1,371 +0,0 @@ -/* OperServ core functions - * - * (C) 2003-2011 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 BotInfo *OperServ; - -class SGLineManager : public XLineManager -{ - public: - SGLineManager(Module *creator) : XLineManager(creator, "xlinemanager/sgline", 'G') { } - - XLine *Add(const Anope::string &mask, const Anope::string &creator, time_t expires, const Anope::string &reason) - { - Anope::string realreason = reason; - if (!creator.empty() && Config->AddAkiller) - realreason = "[" + creator + "] " + reason; - - XLine *x = new XLine(mask, creator, expires, realreason); - - this->AddXLine(x); - - if (UplinkSock && Config->AkillOnAdd) - this->Send(NULL, x); - - return x; - } - - void Del(XLine *x) - { - ircdproto->SendAkillDel(x); - } - - void OnMatch(User *u, XLine *x) - { - if (u) - u->Kill(Config->OperServ, x->Reason); - ircdproto->SendAkill(u, x); - } - - void OnExpire(XLine *x) - { - if (Config->WallAkillExpire) - ircdproto->SendGlobops(OperServ, "AKILL on %s has expired", x->Mask.c_str()); - } - - void Send(User *u, XLine *x) - { - ircdproto->SendAkill(u, x); - } -}; - -class SZLineManager : public XLineManager -{ - public: - SZLineManager(Module *creator) : XLineManager(creator, "xlinemanager/szline", 'Z') { } - - XLine *Add(const Anope::string &mask, const Anope::string &creator, time_t expires, const Anope::string &reason) - { - XLine *x = new XLine(mask, creator, expires, reason); - - this->AddXLine(x); - - if (UplinkSock) - this->Send(NULL, x); - - return x; - } - - void Del(XLine *x) - { - ircdproto->SendSZLineDel(x); - } - - void OnMatch(User *u, XLine *x) - { - if (u) - { - Anope::string reason = "Z-Lined: " + x->Reason; - u->Kill(Config->OperServ, reason); - } - - ircdproto->SendSZLine(u, x); - } - - void OnExpire(XLine *x) - { - if (Config->WallSZLineExpire) - ircdproto->SendGlobops(OperServ, "SZLINE on \2%s\2 has expired", x->Mask.c_str()); - } - - void Send(User *u, XLine *x) - { - ircdproto->SendSZLine(u, x); - } -}; - -class SQLineManager : public XLineManager -{ - public: - SQLineManager(Module *creator) : XLineManager(creator, "xlinemanager/sqline", 'Q') { } - - XLine *Add(const Anope::string &mask, const Anope::string &creator, time_t expires, const Anope::string &reason) - { - XLine *x = new XLine(mask, creator, expires, reason); - - this->AddXLine(x); - - if (Config->KillonSQline) - { - 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)) - continue; - for (CUserList::iterator it = c->users.begin(), it_end = c->users.end(); it != it_end; ) - { - UserContainer *uc = *it; - ++it; - - if (uc->user->HasMode(UMODE_OPER) || uc->user->server == Me) - continue; - c->Kick(NULL, uc->user, "%s", reason.c_str()); - } - } - } - else - { - for (Anope::insensitive_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end();) - { - User *user = it->second; - ++it; - - if (!user->HasMode(UMODE_OPER) && user->server != Me && Anope::Match(user->nick, x->Mask)) - user->Kill(Config->ServerName, rreason); - } - } - } - - if (UplinkSock) - this->Send(NULL, x); - - return x; - } - - void Del(XLine *x) - { - ircdproto->SendSQLineDel(x); - } - - void OnMatch(User *u, XLine *x) - { - if (u) - { - Anope::string reason = "Q-Lined: " + x->Reason; - u->Kill(Config->OperServ, reason); - } - - this->Send(u, x); - } - - void OnExpire(XLine *x) - { - if (Config->WallSQLineExpire) - ircdproto->SendGlobops(OperServ, "SQLINE on \2%s\2 has expired", x->Mask.c_str()); - } - - void Send(User *u, XLine *x) - { - ircdproto->SendSQLine(u, x); - } - - bool CheckChannel(Channel *c) - { - if (ircd->chansqline) - for (std::vector::const_iterator it = this->GetList().begin(), it_end = this->GetList().end(); it != it_end; ++it) - if (Anope::Match(c->name, (*it)->Mask)) - return true; - return false; - } -}; - -class SNLineManager : public XLineManager -{ - public: - SNLineManager(Module *creator) : XLineManager(creator, "xlinemanager/snline", 'N') { } - - XLine *Add(const Anope::string &mask, const Anope::string &creator, time_t expires, const Anope::string &reason) - { - XLine *x = new XLine(mask, creator, expires, reason); - - this->AddXLine(x); - - if (Config->KillonSNline && !ircd->sglineenforce) - { - Anope::string rreason = "G-Lined: " + reason; - - for (Anope::insensitive_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end();) - { - User *user = it->second; - ++it; - - if (!user->HasMode(UMODE_OPER) && user->server != Me && Anope::Match(user->realname, x->Mask)) - user->Kill(Config->ServerName, rreason); - } - } - - return x; - } - - void Del(XLine *x) - { - ircdproto->SendSGLineDel(x); - } - - void OnMatch(User *u, XLine *x) - { - if (u) - { - Anope::string reason = "G-Lined: " + x->Reason; - u->Kill(Config->OperServ, reason); - } - this->Send(u, x); - } - - void OnExpire(XLine *x) - { - if (Config->WallSNLineExpire) - ircdproto->SendGlobops(OperServ, "SNLINE on \2%s\2 has expired", x->Mask.c_str()); - } - - void Send(User *u, XLine *x) - { - ircdproto->SendSGLine(u, x); - } - - XLine *Check(User *u) - { - for (unsigned i = this->XLines.size(); i > 0; --i) - { - XLine *x = this->XLines[i - 1]; - - if (x->Expires && x->Expires < Anope::CurTime) - { - this->OnExpire(x); - this->Del(x); - delete x; - this->XLines.erase(XLines.begin() + i - 1); - continue; - } - - if (Anope::Match(u->realname, x->Mask)) - { - this->OnMatch(u, x); - return x; - } - } - - return NULL; - } -}; - -class OperServCore : public Module -{ - SGLineManager sglines; - SZLineManager szlines; - SQLineManager sqlines; - SNLineManager snlines; - - public: - OperServCore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - sglines(this), szlines(this), sqlines(this), snlines(this) - { - this->SetAuthor("Anope"); - - OperServ = findbot(Config->OperServ); - if (OperServ == NULL) - throw ModuleException("No bot named " + Config->OperServ); - - Implementation i[] = { I_OnBotPrivmsg, I_OnServerQuit, I_OnUserModeSet, I_OnUserModeUnset, I_OnUserConnect, I_OnUserNickChange, I_OnPreHelp }; - ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); - - ModuleManager::RegisterService(&sglines); - ModuleManager::RegisterService(&szlines); - ModuleManager::RegisterService(&sqlines); - ModuleManager::RegisterService(&snlines); - - /* Yes, these are in this order for a reason. Most violent->least violent. */ - XLineManager::RegisterXLineManager(&sglines); - XLineManager::RegisterXLineManager(&szlines); - XLineManager::RegisterXLineManager(&sqlines); - XLineManager::RegisterXLineManager(&snlines); - } - - EventReturn OnBotPrivmsg(User *u, BotInfo *bi, Anope::string &message) - { - if (Config->OSOpersOnly && !u->HasMode(UMODE_OPER) && bi->nick == Config->OperServ) - { - u->SendMessage(bi, ACCESS_DENIED); - return EVENT_STOP; - } - - return EVENT_CONTINUE; - } - - void OnServerQuit(Server *server) - { - if (server->HasFlag(SERVER_JUPED)) - ircdproto->SendGlobops(OperServ, "Received SQUIT for juped server %s", server->GetName().c_str()); - } - - void OnUserModeSet(User *u, UserModeName Name) - { - if (Name == UMODE_OPER) - { - if (Config->WallOper) - ircdproto->SendGlobops(OperServ, "\2%s\2 is now an IRC operator.", u->nick.c_str()); - Log(OperServ) << u->nick << " is now an IRC operator"; - } - } - - void OnUserModeUnset(User *u, UserModeName Name) - { - if (Name == UMODE_OPER) - Log(OperServ) << u->nick << " is no longer an IRC operator"; - } - - void OnUserConnect(dynamic_reference &u, bool &exempt) - { - if (u && !exempt) - XLineManager::CheckAll(u); - } - - void OnUserNickChange(User *u, const Anope::string &oldnick) - { - if (ircd->sqline && !u->HasMode(UMODE_OPER)) - this->sqlines.Check(u); - } - - EventReturn OnCheckKick(User *u, ChannelInfo *ci, bool &kick) - { - if (this->sqlines.CheckChannel(ci->c)) - kick = true; - return EVENT_CONTINUE; - } - - void OnPreHelp(CommandSource &source, const std::vector ¶ms) - { - if (!params.empty() || source.owner->nick != Config->OperServ) - return; - source.Reply(_("%s commands:"), Config->OperServ.c_str()); - } -}; - -MODULE_INIT(OperServCore) - diff --git a/modules/core/os_mode.cpp b/modules/core/os_mode.cpp deleted file mode 100644 index b3e62ca25..000000000 --- a/modules/core/os_mode.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* OperServ core functions - * - * (C) 2003-2011 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 CommandOSMode : public Command -{ - public: - CommandOSMode(Module *creator) : Command(creator, "operserv/mode", 2, 2) - { - this->SetDesc(_("Change channel modes")); - this->SetSyntax(_("\037channel\037 \037modes\037")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - const Anope::string &target = params[0]; - const Anope::string &modes = params[1]; - - Channel *c = findchan(target); - if (!c) - source.Reply(CHAN_X_NOT_IN_USE, target.c_str()); - else if (c->bouncy_modes) - source.Reply(_("Services is unable to change modes. Are your servers' U:lines configured correctly?")); - else - { - c->SetModes(source.owner, false, modes.c_str()); - - Log(LOG_ADMIN, u, this) << modes << " on " << target; - } - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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.")); - return true; - } -}; - -class CommandOSUMode : public Command -{ - public: - CommandOSUMode(Module *creator) : Command(creator, "operserv/umode", 2, 2) - { - this->SetDesc(_("Change channel or user modes")); - this->SetSyntax(_("\037user\037 \037modes\037")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - const Anope::string &target = params[0]; - const Anope::string &modes = params[1]; - - User *u2 = finduser(target); - if (!u2) - source.Reply(NICK_X_NOT_IN_USE, target.c_str()); - else - { - u2->SetModes(source.owner, "%s", modes.c_str()); - source.Reply(_("Changed usermodes of \002%s\002 to %s."), u2->nick.c_str(), modes.c_str()); - - u2->SendMessage(source.owner, _("\002%s\002 changed your usermodes to %s."), u->nick.c_str(), modes.c_str()); - - Log(LOG_ADMIN, u, this) << modes << " on " << target; - } - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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.")); - return true; - } -}; - -class OSMode : public Module -{ - CommandOSMode commandosmode; - CommandOSUMode commandosumode; - - public: - OSMode(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandosmode(this), commandosumode(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandosmode); - ModuleManager::RegisterService(&commandosumode); - } -}; - -MODULE_INIT(OSMode) diff --git a/modules/core/os_modinfo.cpp b/modules/core/os_modinfo.cpp deleted file mode 100644 index 38d9cf905..000000000 --- a/modules/core/os_modinfo.cpp +++ /dev/null @@ -1,245 +0,0 @@ -/* OperServ core functions - * - * (C) 2003-2011 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 CommandOSModInfo : public Command -{ - public: - CommandOSModInfo(Module *creator) : Command(creator, "operserv/modinfo", 1, 1) - { - this->SetDesc(_("Info about a loaded module")); - this->SetSyntax(_("\037modname\037")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - const Anope::string &file = params[0]; - - 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() : "?", do_strftime(m->created).c_str()); - - std::vector services = ModuleManager::GetServiceKeys(); - for (unsigned i = 0; i < services.size(); ++i) - { - Service *s = ModuleManager::GetService(services[i]); - if (!s || s->owner != m) - continue; - source.Reply(_(" Providing service: \002%s\002"), s->name.c_str()); - - for (botinfo_map::const_iterator it = BotListByNick.begin(), it_end = BotListByNick.end(); it != it_end; ++it) - { - BotInfo *bi = it->second; - - for (BotInfo::command_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 != s->name) - 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(), s->name.c_str()); - } - } - } - } - else - source.Reply(_("No information about module \002%s\002 is available"), file.c_str()); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("This command lists information about the specified loaded module")); - return true; - } -}; - -class CommandOSModList : public Command -{ - public: - CommandOSModList(Module *creator) : Command(creator, "operserv/modlist", 0, 1) - { - this->SetDesc(_("List loaded modules")); - this->SetSyntax(_("[Core|3rd|protocol|encryption|supported]")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - const Anope::string ¶m = !params.empty() ? params[0] : ""; - - int count = 0; - int showCore = 0; - int showThird = 1; - int showProto = 1; - int showEnc = 1; - int showSupported = 1; - int showDB = 1; - - char core[] = "Core"; - char third[] = "3rd"; - char proto[] = "Protocol"; - char enc[] = "Encryption"; - char supported[] = "Supported"; - char db[] = "Database"; - - if (!param.empty()) - { - if (param.equals_ci(core)) - { - showCore = 1; - showThird = 0; - showProto = 0; - showEnc = 0; - showSupported = 0; - showDB = 0; - } - else if (param.equals_ci(third)) - { - showCore = 0; - showThird = 1; - showSupported = 0; - showProto = 0; - showEnc = 0; - showDB = 0; - } - else if (param.equals_ci(proto)) - { - showCore = 0; - showThird = 0; - showProto = 1; - showEnc = 0; - showSupported = 0; - showDB = 0; - } - else if (param.equals_ci(supported)) - { - showCore = 0; - showThird = 0; - showProto = 0; - showSupported = 1; - showEnc = 0; - showDB = 0; - } - else if (param.equals_ci(enc)) - { - showCore = 0; - showThird = 0; - showProto = 0; - showSupported = 0; - showEnc = 1; - showDB = 0; - } - else if (param.equals_ci(db)) - { - showCore = 0; - showThird = 0; - showProto = 0; - showSupported = 0; - showEnc = 0; - showDB = 1; - } - } - - source.Reply(_("Current Module list:")); - - for (std::list::iterator it = Modules.begin(), it_end = Modules.end(); it != it_end; ++it) - { - Module *m = *it; - - switch (m->type) - { - case CORE: - if (showCore) - { - source.Reply(_("Module: \002%s\002 [%s] [%s]"), m->name.c_str(), m->version.c_str(), core); - ++count; - } - break; - case THIRD: - if (showThird) - { - source.Reply(_("Module: \002%s\002 [%s] [%s]"), m->name.c_str(), m->version.c_str(), third); - ++count; - } - break; - case PROTOCOL: - if (showProto) - { - source.Reply(_("Module: \002%s\002 [%s] [%s]"), m->name.c_str(), m->version.c_str(), proto); - ++count; - } - break; - case SUPPORTED: - if (showSupported) - { - source.Reply(_("Module: \002%s\002 [%s] [%s]"), m->name.c_str(), m->version.c_str(), supported); - ++count; - } - break; - case ENCRYPTION: - if (showEnc) - { - source.Reply(_("Module: \002%s\002 [%s] [%s]"), m->name.c_str(), m->version.c_str(), enc); - ++count; - } - break; - case DATABASE: - if (showDB) - { - source.Reply(_("Module: \002%s\002 [%s] [%s]"), m->name.c_str(), m->version.c_str(), db); - ++count; - } - break; - default: - break; - } - } - if (!count) - source.Reply(_("No modules currently loaded")); - else - source.Reply(_("%d Modules loaded."), count); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Lists all currently loaded modules.")); - return true; - } -}; - -class OSModInfo : public Module -{ - CommandOSModInfo commandosmodinfo; - CommandOSModList commandosmodlist; - - public: - OSModInfo(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandosmodinfo(this), commandosmodlist(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandosmodinfo); - ModuleManager::RegisterService(&commandosmodlist); - } -}; - -MODULE_INIT(OSModInfo) diff --git a/modules/core/os_module.cpp b/modules/core/os_module.cpp deleted file mode 100644 index 562e130aa..000000000 --- a/modules/core/os_module.cpp +++ /dev/null @@ -1,202 +0,0 @@ -/* OperServ core functions - * - * (C) 2003-2011 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 CommandOSModLoad : public Command -{ - public: - CommandOSModLoad(Module *creator) : Command(creator, "operserv/modload", 1, 1) - { - this->SetDesc(_("Load a module")); - this->SetSyntax(_("\037modname\037")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - const Anope::string &mname = params[0]; - - ModuleReturn status = ModuleManager::LoadModule(mname, u); - if (status == MOD_ERR_OK) - { - ircdproto->SendGlobops(source.owner, "%s loaded module %s", u->nick.c_str(), mname.c_str()); - source.Reply(_("Module \002%s\002 loaded"), mname.c_str()); - - /* If a user is loading this module, then the core databases have already been loaded - * so trigger the event manually - */ - Module *m = ModuleManager::FindModule(mname); - if (m) - m->OnPostLoadDatabases(); - } - else if (status == MOD_ERR_EXISTS) - source.Reply(_("Module \002%s\002 is already loaded."), mname.c_str()); - else - source.Reply(_("Unable to load module \002%s\002"), mname.c_str()); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("This command loads the module named FileName from the modules\n" - "directory.")); - return true; - } -}; - -class CommandOSModReLoad : public Command -{ - public: - CommandOSModReLoad(Module *creator) : Command(creator, "operserv/modreload", 1, 1) - { - this->SetDesc(_("Reload a module")); - this->SetSyntax(_("\037modname\037")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - 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()); - return; - } - - if (!m->handle || m->GetPermanent()) - { - source.Reply(_("Unable to remove module \002%s\002"), m->name.c_str()); - return; - } - - /* Unrecoverable */ - bool fatal = m->type == PROTOCOL; - ModuleReturn status = ModuleManager::UnloadModule(m, u); - - if (status != MOD_ERR_OK) - { - source.Reply(_("Unable to remove module \002%s\002"), mname.c_str()); - return; - } - - status = ModuleManager::LoadModule(mname, u); - if (status == MOD_ERR_OK) - { - ircdproto->SendGlobops(source.owner, "%s reloaded module %s", u->nick.c_str(), mname.c_str()); - source.Reply(_("Module \002%s\002 reloaded"), mname.c_str()); - - /* If a user is loading this module, then the core databases have already been loaded - * so trigger the event manually - */ - m = ModuleManager::FindModule(mname); - if (m) - m->OnPostLoadDatabases(); - } - else - { - if (fatal) - throw FatalException("Unable to reload module " + mname); - else - source.Reply(_("Unable to load module \002%s\002"), mname.c_str()); - } - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("This command reloads the module named FileName.")); - return true; - } -}; - -class CommandOSModUnLoad : public Command -{ - public: - CommandOSModUnLoad(Module *creator) : Command(creator, "operserv/modunload", 1, 1) - { - this->SetDesc(_("Un-Load a module")); - this->SetSyntax(_("\037modname\037")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - 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()); - return; - } - - if (!m->handle || m->GetPermanent() || m->type == PROTOCOL) - { - source.Reply(_("Unable to remove module \002%s\002"), m->name.c_str()); - return; - } - - Log() << "Trying to unload module [" << mname << "]"; - - ModuleReturn status = ModuleManager::UnloadModule(m, u); - - if (status == MOD_ERR_OK) - { - source.Reply(_("Module \002%s\002 unloaded"), mname.c_str()); - ircdproto->SendGlobops(source.owner, "%s unloaded module %s", u->nick.c_str(), mname.c_str()); - } - else - source.Reply(_("Unable to remove module \002%s\002"), mname.c_str()); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("This command unloads the module named FileName from the modules\n" - "directory.")); - return true; - } -}; - -class OSModule : public Module -{ - CommandOSModLoad commandosmodload; - CommandOSModReLoad commandosmodreload; - CommandOSModUnLoad commandosmodunload; - - public: - OSModule(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandosmodload(this), commandosmodreload(this), commandosmodunload(this) - { - this->SetAuthor("Anope"); - this->SetPermanent(true); - - ModuleManager::RegisterService(&commandosmodload); - ModuleManager::RegisterService(&commandosmodreload); - ModuleManager::RegisterService(&commandosmodunload); - } -}; - -MODULE_INIT(OSModule) diff --git a/modules/core/os_news.cpp b/modules/core/os_news.cpp deleted file mode 100644 index 9c9aae0a4..000000000 --- a/modules/core/os_news.cpp +++ /dev/null @@ -1,450 +0,0 @@ -/* OperServ core functions - * - * (C) 2003-2011 Anope Team - * Contact us at info@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 "global.h" -#include "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.")} - } -}; - -class MyNewsService : public NewsService -{ - std::vector 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]; - } - - void AddNewsItem(NewsItem *n) - { - this->newsItems[n->type].push_back(n); - } - - void DelNewsItem(NewsItem *n) - { - std::vector &list = this->GetNewsList(n->type); - std::vector::iterator it = std::find(list.begin(), list.end(), n); - if (it != list.end()) - list.erase(it); - delete n; - } - - std::vector &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 -{ - service_reference ns; - - protected: - void DoList(CommandSource &source, NewsType type, const char **msgs) - { - std::vector &list = this->ns->GetNewsList(type); - if (list.empty()) - source.Reply(msgs[MSG_LIST_NONE]); - else - { - source.Reply(msgs[MSG_LIST_HEADER]); - for (unsigned i = 0, end = list.size(); i < end; ++i) - source.Reply(_("%5d (%s by %s)\n"" %s"), i + 1, do_strftime(list[i]->time).c_str(), !list[i]->who.empty() ? list[i]->who.c_str() : "", list[i]->text.c_str()); - source.Reply(END_OF_ANY_LIST, "News"); - } - - return; - } - - void DoAdd(CommandSource &source, const std::vector ¶ms, NewsType type, const char **msgs) - { - const Anope::string text = params.size() > 1 ? params[1] : ""; - - if (text.empty()) - this->OnSyntaxError(source, "ADD"); - else - { - if (readonly) - source.Reply(READ_ONLY_MODE); - - NewsItem *news = new NewsItem(); - news->type = type; - news->text = text; - news->time = Anope::CurTime; - news->who = source.u->nick; - - this->ns->AddNewsItem(news); - - source.Reply(msgs[MSG_ADDED]); - } - - return; - } - - void DoDel(CommandSource &source, const std::vector ¶ms, NewsType type, const char **msgs) - { - const Anope::string &text = params.size() > 1 ? params[1] : ""; - - if (text.empty()) - this->OnSyntaxError(source, "DEL"); - else - { - std::vector &list = this->ns->GetNewsList(type); - if (list.empty()) - source.Reply(msgs[MSG_LIST_NONE]); - else - { - if (readonly) - source.Reply(READ_ONLY_MODE); - if (!text.equals_ci("ALL")) - { - try - { - unsigned num = convertTo(text); - if (num > 0 && num <= list.size()) - { - this->ns->DelNewsItem(list[num - 1]); - source.Reply(msgs[MSG_DELETED], num); - 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]); - } - } - } - - return; - } - - void DoNews(CommandSource &source, const std::vector ¶ms, NewsType type) - { - if (!this->ns) - return; - - const Anope::string &cmd = params[0]; - - const char **msgs = findmsgs(type); - if (!msgs) - throw CoreException("news: Invalid type to do_news()"); - - if (cmd.equals_ci("LIST")) - return this->DoList(source, type, msgs); - else if (cmd.equals_ci("ADD")) - return this->DoAdd(source, params, type, msgs); - else if (cmd.equals_ci("DEL")) - return this->DoDel(source, params, type, msgs); - else - this->OnSyntaxError(source, ""); - - return; - } - public: - NewsBase(Module *creator, const Anope::string &newstype) : Command(creator, newstype, 1, 2), ns("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 ¶ms) = 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 ¶ms) - { - return this->DoNews(source, params, NEWS_LOGON); - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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.)\n" - "NewsCount can be configured in services.conf.\n" - " \n" - "LOGONNEWS may only be used by Services Operators."), Config->NewsCount); - 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 ¶ms) - { - return this->DoNews(source, params, NEWS_OPER); - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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.)\n" - "NewsCount can be configured in services.conf.\n" - " \n" - "OPERNEWS may only be used by Services Operators."), Config->NewsCount); - 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 ¶ms) - { - return this->DoNews(source, params, NEWS_RANDOM); - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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.\n" - " \n" - "RANDOMNEWS may only be used by Services Operators.")); - return true; - } -}; - -class OSNews : public Module -{ - MyNewsService newsservice; - - CommandOSLogonNews commandoslogonnews; - CommandOSOperNews commandosopernews; - CommandOSRandomNews commandosrandomnews; - - void DisplayNews(User *u, NewsType Type) - { - std::vector &newsList = this->newsservice.GetNewsList(Type); - if (newsList.empty()) - 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"); - - static unsigned cur_rand_news = 0; - unsigned displayed = 0; - for (unsigned i = 0, end = newsList.size(); i < end; ++i) - { - if (Type == NEWS_RANDOM && i != cur_rand_news) - continue; - - BotInfo *gl = findbot(Config->Global); - if (!gl && !BotListByNick.empty()) - gl = BotListByNick.begin()->second; - BotInfo *os = findbot(Config->OperServ); - if (!os) - os = gl; - if (gl) - u->SendMessage(Type != NEWS_OPER ? gl : os, msg.c_str(), do_strftime(newsList[i]->time).c_str(), newsList[i]->text.c_str()); - - ++displayed; - - if (Type == NEWS_RANDOM) - { - ++cur_rand_news; - break; - } - else if (displayed >= Config->NewsCount) - 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, CORE), - newsservice(this), commandoslogonnews(this), commandosopernews(this), commandosrandomnews(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandoslogonnews); - ModuleManager::RegisterService(&commandosopernews); - ModuleManager::RegisterService(&commandosrandomnews); - - Implementation i[] = { I_OnUserModeSet, I_OnUserConnect, I_OnDatabaseRead, I_OnDatabaseWrite }; - ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); - - ModuleManager::RegisterService(&this->newsservice); - } - - void OnUserModeSet(User *u, UserModeName Name) - { - if (Name == UMODE_OPER) - DisplayNews(u, NEWS_OPER); - } - - void OnUserConnect(dynamic_reference &user, bool &) - { - if (!user || !user->server->IsSynced()) - return; - - DisplayNews(user, NEWS_LOGON); - DisplayNews(user, NEWS_RANDOM); - } - - EventReturn OnDatabaseRead(const std::vector ¶ms) - { - if (params[0].equals_ci("OS") && params.size() >= 7 && params[1].equals_ci("NEWS")) - { - NewsItem *n = new NewsItem(); - // params[2] was news number - n->time = params[3].is_number_only() ? convertTo(params[3]) : 0; - n->who = params[4]; - if (params[5].equals_ci("LOGON")) - n->type = NEWS_LOGON; - else if (params[5].equals_ci("RANDOM")) - n->type = NEWS_RANDOM; - else if (params[5].equals_ci("OPER")) - n->type = NEWS_OPER; - n->text = params[6]; - - this->newsservice.AddNewsItem(n); - - return EVENT_STOP; - } - - return EVENT_CONTINUE; - } - - void OnDatabaseWrite(void (*Write)(const Anope::string &)) - { - for (unsigned i = 0; i < 3; ++i) - { - std::vector &list = this->newsservice.GetNewsList(static_cast(i)); - for (std::vector::iterator it = list.begin(); it != list.end(); ++it) - { - NewsItem *n = *it; - Anope::string ntype; - if (n->type == NEWS_LOGON) - ntype = "LOGON"; - else if (n->type == NEWS_RANDOM) - ntype = "RANDOM"; - else if (n->type == NEWS_OPER) - ntype = "OPER"; - Anope::string buf = "OS NEWS 0 " + stringify(n->time) + " " + n->who + " " + ntype + " :" + n->text; - Write(buf); - } - } - } -}; - -MODULE_INIT(OSNews) diff --git a/modules/core/os_news.h b/modules/core/os_news.h deleted file mode 100644 index c55f56d42..000000000 --- a/modules/core/os_news.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef OS_NEWS -#define OS_NEWS - -enum NewsType -{ - NEWS_LOGON, - NEWS_RANDOM, - NEWS_OPER -}; - -struct NewsMessages -{ - NewsType type; - Anope::string name; - const char *msgs[10]; -}; - -struct NewsItem -{ - NewsType type; - Anope::string text; - Anope::string who; - time_t time; -}; - -class NewsService : public Service -{ - public: - NewsService(Module *m) : Service(m, "news") { } - - virtual void AddNewsItem(NewsItem *n) = 0; - - virtual void DelNewsItem(NewsItem *n) = 0; - - virtual std::vector &GetNewsList(NewsType t) = 0; -}; - -#endif // OS_NEWS - diff --git a/modules/core/os_noop.cpp b/modules/core/os_noop.cpp deleted file mode 100644 index 2eec522a3..000000000 --- a/modules/core/os_noop.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/* OperServ core functions - * - * (C) 2003-2011 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 CommandOSNOOP : public Command -{ - public: - CommandOSNOOP(Module *creator) : Command(creator, "operserv/noop", 2, 2) - { - this->SetDesc(_("Temporarily remove all O:lines of a server remotely")); - this->SetSyntax(_("SET \037server\037")); - this->SetSyntax(_("REVOKE \037server\037")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - const Anope::string &cmd = params[0]; - const Anope::string &server = params[1]; - - Server *s = Server::Find(server); - if (s == NULL) - source.Reply(_("Server %s does not exist."), server.c_str()); - else if (cmd.equals_ci("SET")) - { - /* Remove the O:lines */ - ircdproto->SendSVSNOOP(s, true); - - Log(LOG_ADMIN, u, this) << "SET on " << s->GetName(); - source.Reply(_("All O:lines of \002%s\002 have been removed."), s->GetName().c_str()); - - Anope::string reason = "NOOP command used by " + u->nick; - /* Kill all the IRCops of the server */ - for (Anope::insensitive_map::iterator it = UserListByNick.begin(); it != UserListByNick.end();) - { - User *u2 = it->second; - ++it; - - if (u2 && u2->HasMode(UMODE_OPER) && u2->server == s) - u2->Kill(Config->OperServ, reason); - } - } - else if (cmd.equals_ci("REVOKE")) - { - Log(LOG_ADMIN, u, this) << "REVOKE on " << s->GetName(); - ircdproto->SendSVSNOOP(s, false); - source.Reply(_("All O:lines of \002%s\002 have been reset."), s->GetName().c_str()); - } - else - this->OnSyntaxError(source, ""); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("\002NOOP SET\002 remove all O:lines of the given\n" - "\002server\002 and kill all IRCops currently on it to\n" - "prevent them from rehashing the server (because this\n" - "would just cancel the effect).\n" - "\002NOOP REVOKE\002 makes all removed O:lines available again\n" - "on the given \002server\002.\n")); - return true; - } -}; - -class OSNOOP : public Module -{ - CommandOSNOOP commandosnoop; - - public: - OSNOOP(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandosnoop(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandosnoop); - } -}; - -MODULE_INIT(OSNOOP) diff --git a/modules/core/os_oline.cpp b/modules/core/os_oline.cpp deleted file mode 100644 index adebc52a1..000000000 --- a/modules/core/os_oline.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/* OperServ core functions - * - * (C) 2003-2011 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 CommandOSOLine : public Command -{ - public: - CommandOSOLine(Module *creator) : Command(creator, "operserv/oline", 2, 2) - { - this->SetDesc(_("Give Operflags to a certain user")); - this->SetSyntax(_("\037nick\037 \037flags\037")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - const Anope::string &nick = params[0]; - const Anope::string &flag = params[1]; - User *u2 = NULL; - - /* let's check whether the user is online */ - if (!(u2 = finduser(nick))) - source.Reply(NICK_X_NOT_IN_USE, nick.c_str()); - else if (u2 && flag[0] == '+') - { - ircdproto->SendSVSO(Config->OperServ, nick, flag); - u2->SetMode(source.owner, UMODE_OPER); - u2->SendMessage(source.owner, _("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()); - ircdproto->SendGlobops(source.owner, "\2%s\2 used OLINE for %s", u->nick.c_str(), nick.c_str()); - } - else if (u2 && flag[0] == '-') - { - ircdproto->SendSVSO(Config->OperServ, nick, flag); - source.Reply(_("Operflags \002%s\002 have been added for \002%s\002."), flag.c_str(), nick.c_str()); - ircdproto->SendGlobops(source.owner, "\2%s\2 used OLINE for %s", u->nick.c_str(), nick.c_str()); - } - else - this->OnSyntaxError(source, ""); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Allows Services Opers 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.")); - return true; - } -}; - -class OSOLine : public Module -{ - CommandOSOLine commandosoline; - - public: - OSOLine(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandosoline(this) - { - this->SetAuthor("Anope"); - - if (!ircd || !ircd->omode) - throw ModuleException("Your IRCd does not support OMODE."); - - ModuleManager::RegisterService(&commandosoline); - } -}; - -MODULE_INIT(OSOLine) diff --git a/modules/core/os_oper.cpp b/modules/core/os_oper.cpp deleted file mode 100644 index 237c0b5bc..000000000 --- a/modules/core/os_oper.cpp +++ /dev/null @@ -1,219 +0,0 @@ -/* OperServ core functions - * - * (C) 2003-2011 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 CommandOSOper : public Command -{ - 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 ¶ms) - { - const Anope::string &subcommand = params[0]; - - if (subcommand.equals_ci("ADD") && params.size() > 2) - { - const Anope::string &oper = params[1]; - const Anope::string &type = params[2]; - - NickAlias *na = findnick(oper); - if (na == NULL) - source.Reply(NICK_X_NOT_REGISTERED, oper.c_str()); - else - { - OperType *ot = OperType::Find(type); - if (ot == NULL) - source.Reply(_("Oper type \2%s\2 has not been configured."), type.c_str()); - else - { - if (na->nc->o) - delete na->nc->o; - na->nc->o = new Oper(na->nc->display, "", "", ot); - - Log(LOG_ADMIN, source.u, this) << "ADD " << na->nick << " as type " << ot->GetName(); - source.Reply("%s (%s) added to the \2%s\2 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]; - - NickAlias *na = findnick(oper); - if (na == NULL) - source.Reply(NICK_X_NOT_REGISTERED, oper.c_str()); - else if (!na->nc || !na->nc->o) - source.Reply(_("Nick \2%s\2 is not a services operator."), oper.c_str()); - else - { - delete na->nc->o; - na->nc->o = NULL; - - Log(LOG_ADMIN, source.u, 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) - { - NickCore *nc = it->second; - - if (!nc->o) - continue; - - source.Reply(_("%-8s %s"), nc->o->name.c_str(), nc->o->ot->GetName().c_str()); - for (std::list::const_iterator it2 = nc->aliases.begin(), it2_end = nc->aliases.end(); it2 != it2_end; ++it2) - if (Oper::Find((*it2)->nick) != NULL) - source.Reply(_(" This oper is configured in the configuration file as %s"), (*it2)->nick.c_str()); - for (std::list::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") && params.size() > 1) - { - Anope::string fulltype = params[1]; - if (params.size() > 2) - fulltype += " " + params[2]; - OperType *ot = OperType::Find(fulltype); - if (ot == NULL) - source.Reply(_("Oper type \2%s\2 has not been configured."), fulltype.c_str()); - else - { - if (ot->GetCommands().empty()) - source.Reply(_("Opertype \2%s\2 has no allowed commands."), ot->GetName().c_str()); - else - { - source.Reply(_("Available commands for \2%s\2:"), ot->GetName().c_str()); - Anope::string buf; - for (std::list::const_iterator it = ot->GetCommands().begin(), it_end = ot->GetCommands().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 \2%s\2 has no allowed privileges."), ot->GetName().c_str()); - else - { - source.Reply(_("Available privileges for \2%s\2:"), ot->GetName().c_str()); - Anope::string buf; - for (std::list::const_iterator it = ot->GetPrivs().begin(), it_end = ot->GetPrivs().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 \2%s\2 receives modes \2%s\2 once identifying."), ot->GetName().c_str(), ot->modes.c_str()); - } - } - else - this->OnSyntaxError(source, subcommand); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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 -{ - CommandOSOper commandosoper; - - public: - OSOper(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandosoper(this) - { - this->SetAuthor("Anope"); - - Implementation i[] = { I_OnDatabaseWrite, I_OnDatabaseRead }; - ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); - - ModuleManager::RegisterService(&commandosoper); - } - - void OnDatabaseWrite(void (*Write)(const Anope::string &)) - { - for (nickcore_map::const_iterator it = NickCoreList.begin(), it_end = NickCoreList.end(); it != it_end; ++it) - { - NickCore *nc = it->second; - if (!nc->o) - continue; - bool found = false; - for (std::list::const_iterator it2 = nc->aliases.begin(), it2_end = nc->aliases.end(); it2 != it2_end; ++it2) - if (Oper::Find((*it2)->nick) != NULL) - found = true; - if (found == false) - { - Write("OPER " + nc->display + " :" + nc->o->ot->GetName()); - } - } - } - - EventReturn OnDatabaseRead(const std::vector ¶ms) - { - if (params[0] == "OPER" && params.size() > 2) - { - NickCore *nc = findcore(params[1]); - if (!nc || nc->o) - return EVENT_CONTINUE; - OperType *ot = OperType::Find(params[2]); - if (ot == NULL) - return EVENT_CONTINUE; - nc->o = new Oper(nc->display, "", "", ot); - Log(LOG_NORMAL, "operserv/oper") << "Tied oper " << nc->display << " to type " << ot->GetName(); - } - return EVENT_CONTINUE; - } -}; - -MODULE_INIT(OSOper) diff --git a/modules/core/os_reload.cpp b/modules/core/os_reload.cpp deleted file mode 100644 index d4d8eac7c..000000000 --- a/modules/core/os_reload.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* OperServ core functions - * - * (C) 2003-2011 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")); - this->SetSyntax(""); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - ServerConfig *old_config = Config; - - try - { - Config = new ServerConfig(); - FOREACH_MOD(I_OnReload, OnReload()); - delete old_config; - source.Reply(_("Services' configuration file has been reloaded.")); - } - catch (const ConfigException &ex) - { - Config = old_config; - Log() << "Error reloading configuration file: " << ex.GetReason(); - source.Reply(_("Error reloading confguration file: ") + ex.GetReason()); - } - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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, CORE), - commandosreload(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandosreload); - } -}; - -MODULE_INIT(OSReload) diff --git a/modules/core/os_session.cpp b/modules/core/os_session.cpp deleted file mode 100644 index 990cd1b4b..000000000 --- a/modules/core/os_session.cpp +++ /dev/null @@ -1,749 +0,0 @@ -/* OperServ core functions - * - * (C) 2003-2011 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 "os_session.h" - -static service_reference sessionservice("session"); - -class MySessionService : public SessionService -{ - SessionMap Sessions; - ExceptionVector Exceptions; - public: - MySessionService(Module *m) : SessionService(m) { } - - void AddException(Exception *e) - { - this->Exceptions.push_back(e); - } - - void DelException(Exception *e) - { - 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) - { - for (std::vector::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) || (u->ip() && Anope::Match(u->ip.addr(), e->mask))) - return e; - } - return NULL; - } - - Exception *FindException(const Anope::string &host) - { - for (std::vector::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; - } - - return NULL; - } - - ExceptionVector &GetExceptions() - { - return this->Exceptions; - } - - void AddSession(Session *s) - { - this->Sessions[s->host] = s; - } - - void DelSession(Session *s) - { - this->Sessions.erase(s->host); - } - - Session *FindSession(const Anope::string &mask) - { - SessionMap::iterator it = this->Sessions.find(mask); - if (it != this->Sessions.end()) - return it->second; - return NULL; - } - - SessionMap &GetSessions() - { - return this->Sessions; - } -}; - -class ExpireTimer : public Timer -{ - public: - ExpireTimer() : Timer(Config->ExpireTimeout, Anope::CurTime, true) { } - - void Tick(time_t) - { - if (!sessionservice) - return; - for (unsigned i = sessionservice->GetExceptions().size(); i > 0; --i) - { - Exception *e = sessionservice->GetExceptions()[i - 1]; - - if (!e->expires || e->expires > Anope::CurTime) - continue; - BotInfo *bi = findbot(Config->OperServ); - if (Config->WallExceptionExpire && bi) - ircdproto->SendGlobops(bi, "Session exception for %s has expired.", e->mask.c_str()); - sessionservice->DelException(e); - delete e; - } - } -}; - -class ExceptionDelCallback : public NumberList -{ - protected: - CommandSource &source; - unsigned Deleted; - public: - ExceptionDelCallback(CommandSource &_source, const Anope::string &numlist) : NumberList(numlist, true), source(_source), Deleted(0) - { - } - - ~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) - { - if (!Number || Number > sessionservice->GetExceptions().size()) - return; - - ++Deleted; - - DoDel(source, Number - 1); - } - - static void DoDel(CommandSource &source, unsigned index) - { - Exception *e = sessionservice->GetExceptions()[index]; - FOREACH_MOD(I_OnExceptionDel, OnExceptionDel(source.u, e)); - - sessionservice->DelException(e); - delete e; - } -}; - -class ExceptionListCallback : public NumberList -{ - protected: - CommandSource &source; - bool SentHeader; - public: - ExceptionListCallback(CommandSource &_source, const Anope::string &numlist) : NumberList(numlist, false), source(_source), SentHeader(false) - { - } - - virtual void HandleNumber(unsigned Number) - { - if (!Number || Number > sessionservice->GetExceptions().size()) - return; - - if (!SentHeader) - { - SentHeader = true; - source.Reply(_("Current Session Limit Exception list:")); - source.Reply(_("Num Limit Host")); - } - - DoList(source, Number - 1); - } - - static void DoList(CommandSource &source, unsigned index) - { - if (index >= sessionservice->GetExceptions().size()) - return; - - source.Reply(_("%3d %4d %s"), index + 1, sessionservice->GetExceptions()[index]->limit, sessionservice->GetExceptions()[index]->mask.c_str()); - } -}; - -class ExceptionViewCallback : public ExceptionListCallback -{ - public: - ExceptionViewCallback(CommandSource &_source, const Anope::string &numlist) : ExceptionListCallback(_source, numlist) - { - } - - void HandleNumber(unsigned Number) - { - if (!Number || Number > sessionservice->GetExceptions().size()) - return; - - if (!SentHeader) - { - SentHeader = true; - source.Reply(_("Current Session Limit Exception list:")); - } - - DoList(source, Number - 1); - } - - static void DoList(CommandSource &source, unsigned index) - { - if (index >= sessionservice->GetExceptions().size()) - return; - - Anope::string expirebuf = expire_left(source.u->Account(), sessionservice->GetExceptions()[index]->expires); - - source.Reply(_("%3d. %s (by %s on %s; %s)\n " " Limit: %-4d - %s"), index + 1, sessionservice->GetExceptions()[index]->mask.c_str(), !sessionservice->GetExceptions()[index]->who.empty() ? sessionservice->GetExceptions()[index]->who.c_str() : "", do_strftime((sessionservice->GetExceptions()[index]->time ? sessionservice->GetExceptions()[index]->time : Anope::CurTime)).c_str(), expirebuf.c_str(), sessionservice->GetExceptions()[index]->limit, sessionservice->GetExceptions()[index]->reason.c_str()); - } -}; - -class CommandOSSession : public Command -{ - private: - void DoList(CommandSource &source, const std::vector ¶ms) - { - Anope::string param = params[1]; - - unsigned mincount = 0; - try - { - mincount = convertTo(param); - } - catch (const ConvertException &) { } - - if (mincount <= 1) - source.Reply(_("Invalid threshold value. It must be a valid integer greater than 1.")); - else - { - source.Reply(_("Hosts with at least \002%d\002 sessions:"), mincount); - source.Reply(_("Sessions Host")); - - for (SessionService::SessionMap::iterator it = sessionservice->GetSessions().begin(), it_end = sessionservice->GetSessions().end(); it != it_end; ++it) - { - Session *session = it->second; - - if (session->count >= mincount) - source.Reply(_("%6d %s"), session->count, session->host.c_str()); - } - } - - return; - } - - void DoView(CommandSource &source, const std::vector ¶ms) - { - Anope::string param = params[1]; - Session *session = sessionservice->FindSession(param); - - if (!session) - source.Reply(_("\002%s\002 not found on session list."), param.c_str()); - else - { - Exception *exception = sessionservice->FindException(param); - source.Reply(_("The host \002%s\002 currently has \002%d\002 sessions with a limit of \002%d\002."), param.c_str(), session->count, exception ? exception-> limit : Config->DefSessionLimit); - } - - return; - } - 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 ¶ms) - { - const Anope::string &cmd = params[0]; - - if (!Config->LimitSessions) - { - source.Reply(_("Session limiting is disabled.")); - return; - } - - 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, ""); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Allows Services Operators to view the session list.\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" - "\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" - "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 ¶ms) - { - User *u = source.u; - 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() ? dotime(expiry) : Config->ExceptionExpiry; - if (expires < 0) - { - source.Reply(BAD_EXPIRY_TIME); - return; - } - else if (expires > 0) - expires += Anope::CurTime; - - int limit = -1; - try - { - limit = convertTo(limitstr); - } - catch (const ConvertException &) { } - - if (limit < 0 || limit > static_cast(Config->MaxSessionLimit)) - { - source.Reply(_("Invalid session limit. It must be a valid integer greater than or equal to zero and less than \002%d\002."), Config->MaxSessionLimit); - return; - } - else - { - if (mask.find('!') != Anope::string::npos || mask.find('@') != Anope::string::npos) - { - source.Reply(_("Invalid hostmask. Only real hostmasks are valid as sessionservice->GetExceptions() are not matched against nicks or usernames.")); - return; - } - - for (std::vector::iterator it = sessionservice->GetExceptions().begin(), it_end = sessionservice->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()); - } - } - - Exception *exception = new Exception(); - exception->mask = mask; - exception->limit = limit; - exception->reason = reason; - exception->time = Anope::CurTime; - exception->who = u->nick; - exception->expires = expires; - - EventReturn MOD_RESULT; - FOREACH_RESULT(I_OnExceptionAdd, OnExceptionAdd(exception)); - if (MOD_RESULT == EVENT_STOP) - delete exception; - else - { - sessionservice->AddException(exception); - source.Reply(_("Session limit for \002%s\002 set to \002%d\002."), mask.c_str(), limit); - if (readonly) - source.Reply(READ_ONLY_MODE); - } - } - - return; - } - - void DoDel(CommandSource &source, const std::vector ¶ms) - { - 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); - list.Process(); - } - else - { - unsigned i = 0, end = sessionservice->GetExceptions().size(); - for (; i < end; ++i) - if (mask.equals_ci(sessionservice->GetExceptions()[i]->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 (readonly) - source.Reply(READ_ONLY_MODE); - - return; - } - - void DoMove(CommandSource &source, const std::vector ¶ms) - { - const Anope::string &n1str = params.size() > 1 ? params[1] : ""; /* From position */ - const Anope::string &n2str = params.size() > 2 ? params[2] : ""; /* To position */ - int n1, n2; - - if (n2str.empty()) - { - this->OnSyntaxError(source, "MOVE"); - return; - } - - n1 = n2 = -1; - try - { - n1 = convertTo(n1str); - n2 = convertTo(n2str); - } - catch (const ConvertException &) { } - - if (n1 >= 0 && n1 < sessionservice->GetExceptions().size() && n2 >= 0 && n2 < sessionservice->GetExceptions().size() && n1 != n2) - { - Exception *temp = sessionservice->GetExceptions()[n1]; - sessionservice->GetExceptions()[n1] = sessionservice->GetExceptions()[n2]; - sessionservice->GetExceptions()[n2] = temp; - - source.Reply(_("Exception for \002%s\002 (#%d) moved to position \002%d\002."), sessionservice->GetExceptions()[n1]->mask.c_str(), n1 + 1, n2 + 1); - - if (readonly) - source.Reply(READ_ONLY_MODE); - } - else - this->OnSyntaxError(source, "MOVE"); - - return; - } - - void DoList(CommandSource &source, const std::vector ¶ms) - { - Anope::string mask = params.size() > 1 ? params[1] : ""; - - if (!mask.empty() && mask.find_first_not_of("1234567890,-") == Anope::string::npos) - { - ExceptionListCallback list(source, mask); - list.Process(); - } - else - { - bool SentHeader = false; - - for (unsigned i = 0, end = sessionservice->GetExceptions().size(); i < end; ++i) - if (mask.empty() || Anope::Match(sessionservice->GetExceptions()[i]->mask, mask)) - { - if (!SentHeader) - { - SentHeader = true; - source.Reply(_("Current Session Limit Exception list:")); - source.Reply(_("Num Limit Host")); - } - - ExceptionListCallback::DoList(source, i); - } - - if (!SentHeader) - source.Reply(_("No matching entries on session-limit exception list.")); - } - - return; - } - - void DoView(CommandSource &source, const std::vector ¶ms) - { - Anope::string mask = params.size() > 1 ? params[1] : ""; - - if (!mask.empty() && mask.find_first_not_of("1234567890,-") == Anope::string::npos) - { - ExceptionViewCallback list(source, mask); - list.Process(); - } - else - { - bool SentHeader = false; - - for (unsigned i = 0, end = sessionservice->GetExceptions().size(); i < end; ++i) - if (mask.empty() || Anope::Match(sessionservice->GetExceptions()[i]->mask, mask)) - { - if (!SentHeader) - { - SentHeader = true; - source.Reply(_("Current Session Limit Exception list:")); - } - - ExceptionViewCallback::DoList(source, i); - } - - if (!SentHeader) - source.Reply(_("No matching entries on session-limit exception list.")); - } - - return; - } - - 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 | \037list\037}")); - this->SetSyntax(_("MOVE \037num\037 \037position\037")); - this->SetSyntax(_("LIST [\037mask\037 | \037list\037]")); - this->SetSyntax(_("VIEW [\037mask\037 | \037list\037]")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - const Anope::string &cmd = params[0]; - - if (!Config->LimitSessions) - { - source.Reply(_("Session limiting is disabled.")); - 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("MOVE")) - return this->DoMove(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, ""); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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, via a\n" - "/NOTICE from %s, of a source of help regarding session\n" - "limiting. The content of this notice is a config setting.\n"), - Config->OperServ.c_str()); - 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" - "\002EXCEPTION DEL\002 removes the given mask from the exception list.\n" - "\002EXCEPTION MOVE\002 moves exception \037num\037 to \037position\037. The\n" - "sessionservice->GetExceptions() inbetween will be shifted up or down to fill the gap.\n" - "\002EXCEPTION LIST\002 and \002EXCEPTION VIEW\002 show all current\n" - "sessionservice->GetExceptions(); if the optional mask is given, the list is limited\n" - "to those sessionservice->GetExceptions() 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 -{ - MySessionService ss; - ExpireTimer expiretimer; - CommandOSSession commandossession; - CommandOSException commandosexception; - service_reference akills; - - void AddSession(User *u, bool exempt) - { - Session *session = this->ss.FindSession(u->host); - - if (session) - { - bool kill = false; - if (Config->DefSessionLimit && session->count >= Config->DefSessionLimit) - { - 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 in do_kill or in do_quit - Adam - */ - ++session->count; - - if (kill && !exempt) - { - BotInfo *bi = findbot(Config->OperServ); - if (bi) - { - if (!Config->SessionLimitExceeded.empty()) - u->SendMessage(bi, Config->SessionLimitExceeded.c_str(), u->host.c_str()); - if (!Config->SessionLimitDetailsLoc.empty()) - u->SendMessage(bi, "%s", Config->SessionLimitDetailsLoc.c_str()); - } - - u->Kill(Config->OperServ, "Session limit exceeded"); - - ++session->hits; - if (Config->MaxSessionKill && session->hits >= Config->MaxSessionKill && akills) - { - const Anope::string &akillmask = "*@" + u->host; - const Anope::string &akillreason = "Session limit exceeded for " + u->host; - akills->Add(akillmask, Config->OperServ, Anope::CurTime + Config->SessionAutoKillExpiry, akillreason); - if (bi) - ircdproto->SendGlobops(bi, "Added a temporary AKILL for \2%s\2 due to excessive connections", akillmask.c_str()); - } - } - } - else - { - session = new Session(); - session->host = u->host; - session->count = 1; - session->hits = 0; - - this->ss.AddSession(session); - } - } - - void DelSession(User *u) - { - Session *session = this->ss.FindSession(u->host); - if (!session) - { - if (debug) - Log() << "session: Tried to delete non-existant session: " << u->host; - return; - } - - if (session->count > 1) - { - --session->count; - return; - } - - this->ss.DelSession(session); - delete session; - } - - public: - OSSession(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - ss(this), commandossession(this), commandosexception(this), akills("xlinemanager/sgline") - { - this->SetAuthor("Anope"); - - Implementation i[] = { I_OnUserConnect, I_OnUserLogoff }; - ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); - - ModuleManager::RegisterService(&commandossession); - ModuleManager::RegisterService(&commandosexception); - - ModuleManager::RegisterService(&this->ss); - } - - void OnUserConnect(dynamic_reference &user, bool &exempt) - { - if (user && Config->LimitSessions) - this->AddSession(user, exempt); - } - - void OnUserLogoff(User *u) - { - if (Config->LimitSessions && (!u->server || !u->server->IsULined())) - this->DelSession(u); - } -}; - -MODULE_INIT(OSSession) diff --git a/modules/core/os_session.h b/modules/core/os_session.h deleted file mode 100644 index faab37934..000000000 --- a/modules/core/os_session.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef OS_SESSION_H -#define OS_SESSION_H - -class SessionService : public Service -{ - public: - typedef Anope::map SessionMap; - typedef std::vector ExceptionVector; - - SessionService(Module *m) : Service(m, "session") { } - - virtual void AddException(Exception *e) = 0; - - virtual void DelException(Exception *e) = 0; - - virtual Exception *FindException(User *u) = 0; - - virtual Exception *FindException(const Anope::string &host) = 0; - - virtual ExceptionVector &GetExceptions() = 0; - - virtual void AddSession(Session *s) = 0; - - virtual void DelSession(Session *s) = 0; - - virtual Session *FindSession(const Anope::string &mask) = 0; - - virtual SessionMap &GetSessions() = 0; -}; - -#endif - diff --git a/modules/core/os_set.cpp b/modules/core/os_set.cpp deleted file mode 100644 index 2a50f851c..000000000 --- a/modules/core/os_set.cpp +++ /dev/null @@ -1,261 +0,0 @@ -/* OperServ core functions - * - * (C) 2003-2011 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 CommandOSSet : public Command -{ - private: - void DoList(CommandSource &source) - { - Log(LOG_ADMIN, source.u, this); - - Anope::string index; - - index = readonly ? _("%s is enabled") : _("%s is disabled"); - source.Reply(index.c_str(), "READONLY"); - index = debug ? _("%s is enabled") : _("%s is disabled"); - source.Reply(index.c_str(), "DEBUG"); - index = noexpire ? _("%s is enabled") : _("%s is disabled"); - source.Reply(index.c_str(), "NOEXPIRE"); - - return; - } - - void DoSetReadOnly(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - const Anope::string &setting = params.size() > 1 ? params[1] : ""; - - if (setting.empty()) - { - this->OnSyntaxError(source, "READONLY"); - return; - } - - if (setting.equals_ci("ON")) - { - readonly = true; - Log(LOG_ADMIN, u, this) << "READONLY ON"; - source.Reply(_("Services are now in \002read-only\002 mode.")); - } - else if (setting.equals_ci("OFF")) - { - readonly = false; - Log(LOG_ADMIN, u, this) << "READONLY OFF"; - source.Reply(_("Services are now in \002read-write\002 mode.")); - } - else - source.Reply(_("Setting for READONLY must be \002on\002 or \002off\002.")); - - return; - } - - void DoSetSuperAdmin(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - const Anope::string &setting = params.size() > 1 ? params[1] : ""; - - if (setting.empty()) - { - this->OnSyntaxError(source, "SUPERADMIN"); - return; - } - - /** - * Allow the user to turn super admin on/off - * - * Rob - **/ - if (!Config->SuperAdmin) - source.Reply(_("SuperAdmin setting not enabled in services.conf")); - else if (setting.equals_ci("ON")) - { - u->isSuperAdmin = 1; - source.Reply(_("You are now a SuperAdmin")); - Log(LOG_ADMIN, u, this) << "SUPERADMIN ON"; - ircdproto->SendGlobops(source.owner, _("%s is now a Super-Admin"), u->nick.c_str()); - } - else if (setting.equals_ci("OFF")) - { - u->isSuperAdmin = 0; - source.Reply(_("You are no longer a SuperAdmin")); - Log(LOG_ADMIN, u, this) << "SUPERADMIN OFF"; - ircdproto->SendGlobops(source.owner, _("%s is no longer a Super-Admin"), u->nick.c_str()); - } - else - source.Reply(_("Setting for SuperAdmin must be \002on\002 or \002off\002 (must be enabled in services.conf)")); - - return; - } - - void DoSetDebug(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - const Anope::string &setting = params.size() > 1 ? params[1] : ""; - - if (setting.empty()) - { - this->OnSyntaxError(source, "DEBUG"); - return; - } - - if (setting.equals_ci("ON")) - { - debug = 1; - Log(LOG_ADMIN, u, this) << "DEBUG ON"; - source.Reply(_("Services are now in debug mode.")); - } - else if (setting.equals_ci("OFF") || (setting[0] == '0' && setting.is_number_only() && !convertTo(setting))) - { - Log(LOG_ADMIN, u, this) << "DEBUG OFF"; - debug = 0; - source.Reply(_("Services are now in non-debug mode.")); - } - else - { - try - { - debug = convertTo(setting); - Log(LOG_ADMIN, u, this) << "DEBUG " << debug; - source.Reply(_("Services are now in debug mode (level %d)."), debug); - return; - } - catch (const ConvertException &) { } - - source.Reply(_("Setting for DEBUG must be \002ON\002, \002OFF\002, or a positive number.")); - } - - return; - } - - void DoSetNoExpire(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - const Anope::string &setting = params.size() > 1 ? params[1] : ""; - - if (setting.empty()) - { - this->OnSyntaxError(source, "NOEXPIRE"); - return; - } - - if (setting.equals_ci("ON")) - { - noexpire = true; - Log(LOG_ADMIN, u, this) << "NOEXPIRE ON"; - source.Reply(_("Services are now in \002no expire\002 mode.")); - } - else if (setting.equals_ci("OFF")) - { - noexpire = false; - Log(LOG_ADMIN, u, this) << "NOEXPIRE OFF"; - source.Reply(_("Services are now in \002expire\002 mode.")); - } - else - source.Reply(_("Setting for NOEXPIRE must be \002on\002 or \002off\002.")); - - return; - } - public: - CommandOSSet(Module *creator) : Command(creator, "operserv/set", 1, 2) - { - this->SetDesc(_("Set various global Services options")); - this->SetSyntax(_("\037option\037 \037setting\037")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - const Anope::string &option = params[0]; - - if (option.equals_ci("LIST")) - return this->DoList(source); - else if (option.equals_ci("READONLY")) - return this->DoSetReadOnly(source, params); - else if (option.equals_ci("SUPERADMIN")) - return this->DoSetSuperAdmin(source, params); - else if (option.equals_ci("DEBUG")) - return this->DoSetDebug(source, params); - else if (option.equals_ci("NOEXPIRE")) - return this->DoSetNoExpire(source, params); - else - source.Reply(_("Unknown option \002%s\002."), option.c_str()); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - if (subcommand.empty()) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Sets various global Services options. Option names\n" - "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" - " SUPERADMIN Activate or deactivate super-admin mode\n" - " LIST List the options")); - } - else if (subcommand.equals_ci("LIST")) - source.Reply(_("Syntax: \002LIST\n" - "Display the various %s settings"), Config->OperServ.c_str()); - 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 list and drop or forbid nicknames and\n" - "channels, but any such changes will not be saved unless\n" - "read-only mode is deactivated before Services is terminated\n" - "or restarted.\n" - " \n" - "This option is equivalent to the command-line option\n" - "\002-readonly\002.")); - else if (subcommand.equals_ci("NOEXPIRE")) - source.Reply(_("Syntax: \002NOEXPIRE {ON | OFF}\002\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" - "This option is equivalent to the command-line option\n" - "\002-noexpire\002.")); - else if (subcommand.equals_ci("SUPERADMIN")) - source.Reply(_("Syntax: \002SUPERADMIN {ON | OFF}\002\n" - "Setting this will grant you extra privileges such as the\n" - "ability to be \"founder\" on all channel's etc...\n" - "This option is \002not\002 persistent, and should only be used when\n" - "needed, and set back to OFF when no longer needed.")); - else - return false; - return true; - } -}; - -class OSSet : public Module -{ - CommandOSSet commandosset; - - public: - OSSet(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandosset(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandosset); - } -}; - -MODULE_INIT(OSSet) diff --git a/modules/core/os_shutdown.cpp b/modules/core/os_shutdown.cpp deleted file mode 100644 index bc3a19353..000000000 --- a/modules/core/os_shutdown.cpp +++ /dev/null @@ -1,117 +0,0 @@ -/* OperServ core functions - * - * (C) 2003-2011 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 CommandOSQuit : public Command -{ - public: - CommandOSQuit(Module *creator) : Command(creator, "operserv/quit", 0, 0) - { - this->SetDesc(_("Terminate Services WITHOUT saving")); - this->SetSyntax(""); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - quitmsg = "QUIT command received from " + u->nick; - quitting = true; - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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.")); - return true; - } -}; - -class CommandOSRestart : public Command -{ - public: - CommandOSRestart(Module *creator) : Command(creator, "operserv/restart", 0, 0) - { - this->SetDesc(_("Save databases and restart Services")); - this->SetSyntax(""); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - quitmsg = "RESTART command received from " + u->nick; - save_databases(); - quitting = restarting = true; - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(_("Causes Services to save all databases and then restart\n" - "(i.e. exit and immediately re-run the executable).")); - return true; - } -}; - -class CommandOSShutdown : public Command -{ - public: - CommandOSShutdown(Module *creator) : Command(creator, "operserv/shutdown", 0, 0) - { - this->SetDesc(_("Terminate services with save")); - this->SetSyntax(""); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - quitmsg = source.command + " command received from " + u->nick; - save_databases(); - quitting = true; - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Causes Services to save all databases and then shut down.")); - return true; - } -}; - -class OSShutdown : public Module -{ - CommandOSQuit commandosquit; - CommandOSRestart commandosrestart; - CommandOSShutdown commandosshutdown; - - public: - OSShutdown(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandosquit(this), commandosrestart(this), commandosshutdown(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandosquit); - ModuleManager::RegisterService(&commandosrestart); - ModuleManager::RegisterService(&commandosshutdown); - } -}; - -MODULE_INIT(OSShutdown) diff --git a/modules/core/os_stats.cpp b/modules/core/os_stats.cpp deleted file mode 100644 index 2c20395d0..000000000 --- a/modules/core/os_stats.cpp +++ /dev/null @@ -1,224 +0,0 @@ -/* OperServ core functions - * - * (C) 2003-2011 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" - -/** - * 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 -{ - service_reference akills, snlines, sqlines, szlines; - private: - void DoStatsAkill(CommandSource &source) - { - int timeout; - if (akills) - { - /* AKILLs */ - source.Reply(_("Current number of AKILLs: \002%d\002"), akills->GetCount()); - timeout = Config->AutokillExpiry + 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 (ircd->snline && snlines) - { - /* SNLINEs */ - source.Reply(_("Current number of SNLINEs: \002%d\002"), snlines->GetCount()); - timeout = Config->SNLineExpiry + 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 (ircd->sqline && sqlines) - { - /* SQLINEs */ - source.Reply(_("Current number of SQLINEs: \002%d\002"), sqlines->GetCount()); - timeout = Config->SQLineExpiry + 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")); - } - if (ircd->szline && szlines) - { - /* SZLINEs */ - source.Reply(_("Current number of SZLINEs: \002%d\002"), szlines->GetCount()); - timeout = Config->SZLineExpiry + 59; - if (timeout >= 172800) - source.Reply(_("Default SZLINE expiry time: \002%d days\002"), timeout / 86400); - else if (timeout >= 86400) - source.Reply(_("Default SZLINE expiry time: \0021 day\002")); - else if (timeout >= 7200) - source.Reply(_("Default SZLINE expiry time: \002%d hours\002"), timeout / 3600); - else if (timeout >= 3600) - source.Reply(_("Default SZLINE expiry time: \0021 hour\002")); - else if (timeout >= 120) - source.Reply(_("Default SZLINE expiry time: \002%d minutes\002"), timeout / 60); - else if (timeout >= 60) - source.Reply(_("Default SZLINE expiry time: \0021 minute\002")); - else - source.Reply(_("Default SZLINE expiry time: \002No expiration\002")); - } - return; - } - - void DoStatsReset(CommandSource &source) - { - maxusercnt = usercnt; - source.Reply(_("Statistics reset.")); - return; - } - - void DoStatsUptime(CommandSource &source) - { - time_t uptime = Anope::CurTime - start_time; - source.Reply(_("Current users: \002%d\002 (\002%d\002 ops)"), usercnt, opcnt); - source.Reply(_("Maximum users: \002%d\002 (%s)"), maxusercnt, do_strftime(maxusertime).c_str()); - source.Reply(_("Services up %s"), duration(uptime).c_str()); - - return; - } - - void DoStatsUplink(CommandSource &source) - { - Anope::string buf; - - for (unsigned j = 0; !Capab_Info[j].Token.empty(); ++j) - if (Capab.HasFlag(Capab_Info[j].Flag)) - buf += " " + Capab_Info[j].Token; - - 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; - } - - public: - CommandOSStats(Module *creator) : Command(creator, "operserv/stats", 0, 1), - akills("xlinemanager/sgline"), snlines("xlinemanager/snline"), sqlines("xlinemanager/sqline"), szlines("xlinemanager/szline") - { - this->SetDesc(_("Show status of Services and network")); - this->SetSyntax(_("[AKILL | ALL | RESET | UPLINK]")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - Anope::string extra = !params.empty() ? params[0] : ""; - - if (extra.equals_ci("RESET")) - return this->DoStatsReset(source); - - if (extra.equals_ci("ALL") || extra.equals_ci("AKILL")) - this->DoStatsAkill(source); - - if (extra.empty() || extra.equals_ci("ALL") || extra.equals_ci("UPTIME")) - this->DoStatsUptime(source); - - if (extra.equals_ci("ALL") || extra.equals_ci("UPLINK")) - this->DoStatsUplink(source); - - if (!extra.empty() && !extra.equals_ci("ALL") && !extra.equals_ci("AKILL") && !extra.equals_ci("UPLINK")) - source.Reply(_("Unknown STATS option \002%s\002."), extra.c_str()); - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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 \002ALL\002 displays the user and uptime statistics, and\n" - "everything you'd see with the \002UPLINK\002 option.")); - return true; - } -}; - -class OSStats : public Module -{ - CommandOSStats commandosstats; - - public: - OSStats(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandosstats(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandosstats); - } -}; - -MODULE_INIT(OSStats) diff --git a/modules/core/os_svsnick.cpp b/modules/core/os_svsnick.cpp deleted file mode 100644 index dc7c77a6c..000000000 --- a/modules/core/os_svsnick.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/* OperServ core functions - * - * (C) 2003-2011 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 CommandOSSVSNick : public Command -{ - public: - CommandOSSVSNick(Module *creator) : Command(creator, "operserv/svsnick", 2, 2) - { - this->SetDesc(_("Forcefully change a user's nickname")); - this->SetSyntax(_("\037nick\037 \037newnick\037")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - const Anope::string &nick = params[0]; - Anope::string newnick = params[1]; - User *u2; - - /* Truncate long nicknames to Config->NickLen characters */ - if (newnick.length() > Config->NickLen) - { - source.Reply(_("Nick \002%s\002 was truncated to %d characters."), newnick.c_str(), Config->NickLen, newnick.c_str()); - newnick = params[1].substr(0, Config->NickLen); - } - - /* Check for valid characters */ - if (newnick[0] == '-' || isdigit(newnick[0])) - { - source.Reply(_("Nick \002%s\002 is an illegal nickname and cannot be used."), newnick.c_str()); - return; - } - for (unsigned i = 0, end = newnick.length(); i < end; ++i) - if (!isvalidnick(newnick[i])) - { - source.Reply(_("Nick \002%s\002 is an illegal nickname and cannot be used."), newnick.c_str()); - return; - } - - /* Check for a nick in use or a forbidden/suspended nick */ - if (!(u2 = finduser(nick))) - source.Reply(NICK_X_NOT_IN_USE, nick.c_str()); - else if (finduser(newnick)) - source.Reply(_("Nick \002%s\002 is currently in use."), newnick.c_str()); - else - { - source.Reply(_("The nick \002%s\002 is now being changed to \002%s\002."), nick.c_str(), newnick.c_str()); - ircdproto->SendGlobops(source.owner, "%s used SVSNICK to change %s to %s", u->nick.c_str(), nick.c_str(), newnick.c_str()); - ircdproto->SendForceNickChange(u2, newnick, Anope::CurTime); - } - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Forcefully changes a user's nickname from nick to newnick.")); - return true; - } -}; - -class OSSVSNick : public Module -{ - CommandOSSVSNick commandossvsnick; - - public: - OSSVSNick(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandossvsnick(this) - { - this->SetAuthor("Anope"); - - if (!ircd || !ircd->svsnick) - throw ModuleException("Your IRCd does not support SVSNICK"); - - ModuleManager::RegisterService(&commandossvsnick); - } -}; - -MODULE_INIT(OSSVSNick) diff --git a/modules/core/os_sxline.cpp b/modules/core/os_sxline.cpp deleted file mode 100644 index f949cb7d2..000000000 --- a/modules/core/os_sxline.cpp +++ /dev/null @@ -1,807 +0,0 @@ -/* OperServ core functions - * - * (C) 2003-2011 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."), this->command->name.c_str()); - else if (Deleted == 1) - source.Reply(_("Deleted 1 entry from the %s list."), this->command->name.c_str()); - else - source.Reply(_("Deleted %d entries from the %s list."), Deleted, this->command->name.c_str()); - } - - void HandleNumber(unsigned Number) - { - if (!Number) - return; - - XLine *x = this->xlm->GetEntry(Number - 1); - - if (!x) - return; - - ++Deleted; - DoDel(this->xlm, source, x); - } - - static void DoDel(XLineManager *xlm, CommandSource &source, XLine *x) - { - xlm->DelXLine(x); - } -}; - -class SXLineListCallback : public NumberList -{ - protected: - XLineManager *xlm; - Command *command; - CommandSource &source; - bool SentHeader; - public: - SXLineListCallback(XLineManager *x, Command *c, CommandSource &_source, const Anope::string &numlist) : NumberList(numlist, false), xlm(x), command(c), source(_source), SentHeader(false) - { - } - - ~SXLineListCallback() - { - if (!SentHeader) - source.Reply(_("No matching entries on the %s list."), this->command->name.c_str()); - } - - virtual void HandleNumber(unsigned Number) - { - if (!Number) - return; - - XLine *x = this->xlm->GetEntry(Number - 1); - - if (!x) - return; - - if (!SentHeader) - { - SentHeader = true; - source.Reply(_("Current %s list:\n Num Mask Reason"), this->command->name.c_str()); - } - - DoList(source, x, Number - 1); - } - - static void DoList(CommandSource &source, XLine *x, unsigned Number) - { - source.Reply(OPER_LIST_FORMAT, Number + 1, x->Mask.c_str(), x->Reason.c_str()); - } -}; - -class SXLineViewCallback : public SXLineListCallback -{ - public: - SXLineViewCallback(XLineManager *x, Command *c, CommandSource &_source, const Anope::string &numlist) : SXLineListCallback(x, c, _source, numlist) - { - } - - void HandleNumber(unsigned Number) - { - if (!Number) - return; - - XLine *x = this->xlm->GetEntry(Number - 1); - - if (!x) - return; - - if (!SentHeader) - { - SentHeader = true; - source.Reply(_("Current %s list:"), this->command->name.c_str()); - } - - DoList(source, x, Number - 1); - } - - static void DoList(CommandSource &source, XLine *x, unsigned Number) - { - Anope::string expirebuf = expire_left(source.u->Account(), x->Expires); - source.Reply(OPER_VIEW_FORMAT, Number + 1, x->Mask.c_str(), x->By.c_str(), do_strftime(x->Created).c_str(), expirebuf.c_str(), x->Reason.c_str()); - } -}; - -class CommandOSSXLineBase : public Command -{ - private: - virtual XLineManager* xlm() = 0; - - virtual void OnAdd(CommandSource &source, const std::vector ¶ms) = 0; - - void OnDel(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - - if (!this->xlm() || this->xlm()->GetList().empty()) - { - source.Reply(_("%s list is empty."), this->name.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(), this->name.c_str()); - return; - } - - FOREACH_MOD(I_OnDelXLine, OnDelXLine(u, x, this->xlm())); - - SXLineDelCallback::DoDel(this->xlm(), source, x); - source.Reply(_("\002%s\002 deleted from the %s list."), mask.c_str(), this->name.c_str()); - } - - if (readonly) - source.Reply(READ_ONLY_MODE); - - return; - } - - void OnList(CommandSource &source, const std::vector ¶ms) - { - if (!this->xlm() || this->xlm()->GetList().empty()) - { - source.Reply(_("%s list is empty."), this->name.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) - { - SXLineListCallback list(this->xlm(), this, source, mask); - list.Process(); - } - else - { - bool SentHeader = false; - - for (unsigned i = 0, end = this->xlm()->GetCount(); i < end; ++i) - { - XLine *x = this->xlm()->GetEntry(i); - - if (mask.empty() || mask.equals_ci(x->Mask) || Anope::Match(x->Mask, mask)) - { - if (!SentHeader) - { - SentHeader = true; - source.Reply(_("Current %s list:\n Num Mask Reason"), this->name.c_str()); - } - - SXLineListCallback::DoList(source, x, i); - } - } - - if (!SentHeader) - source.Reply(_("No matching entries on the %s list."), this->name.c_str()); - else - source.Reply(END_OF_ANY_LIST, this->name.c_str()); - } - - return; - } - - void OnView(CommandSource &source, const std::vector ¶ms) - { - if (!this->xlm () || this->xlm()->GetList().empty()) - { - source.Reply(_("%s list is empty."), this->name.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) - { - SXLineViewCallback list(this->xlm(), this, source, mask); - list.Process(); - } - else - { - bool SentHeader = false; - - for (unsigned i = 0, end = this->xlm()->GetCount(); i < end; ++i) - { - XLine *x = this->xlm()->GetEntry(i); - - if (mask.empty() || mask.equals_ci(x->Mask) || Anope::Match(x->Mask, mask)) - { - if (!SentHeader) - { - SentHeader = true; - source.Reply(_("Current %s list:"), this->name.c_str()); - } - - SXLineViewCallback::DoList(source, x, i); - } - } - - if (!SentHeader) - source.Reply(_("No matching entries on the %s list."), this->name.c_str()); - } - - return; - } - - void OnClear(CommandSource &source) - { - User *u = source.u; - FOREACH_MOD(I_OnDelXLine, OnDelXLine(u, NULL, this->xlm())); - this->xlm()->Clear(); - source.Reply(_("The %s list has been cleared."), this->name.c_str()); - - return; - } - public: - CommandOSSXLineBase(Module *creator, const Anope::string &cmd) : Command(creator, cmd, 1, 3) - { - this->SetDesc(Anope::printf(_("Manipulate the %s list"), cmd.c_str())); - this->SetSyntax(_("ADD [+\037expiry\037] \037mask\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]")); - this->SetSyntax(_("CLEAR")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - 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 ¶ms) - { - if (!this->xlm()) - return; - - User *u = source.u; - unsigned last_param = 2; - Anope::string param, expiry; - time_t expires; - - param = params.size() > 1 ? params[1] : ""; - if (!param.empty() && param[0] == '+') - { - expiry = param; - param = params.size() > 2 ? params[2] : ""; - last_param = 3; - } - - expires = !expiry.empty() ? dotime(expiry) : Config->SNLineExpiry; - /* 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()) - { - std::pair canAdd = this->xlm()->CanAdd(mask, expires); - if (mask.find_first_not_of("*?") == Anope::string::npos) - source.Reply(USERHOST_MASK_TOO_WIDE, mask.c_str()); - else if (canAdd.first == 1) - source.Reply(_("\002%s\002 already exists on the %s list."), canAdd.second->Mask.c_str(), this->name.c_str()); - else if (canAdd.first == 2) - source.Reply(_("Expiry time of \002%s\002 changed."), canAdd.second->Mask.c_str()); - else if (canAdd.first == 3) - source.Reply(_("\002%s\002 is already covered by %s."), mask.c_str(), canAdd.second->Mask.c_str()); - else - { - /* 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); - unsigned int affected = 0; - for (Anope::insensitive_map::iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it) - if (Anope::Match(it->second->realname, mask)) - ++affected; - float percent = static_cast(affected) / static_cast(UserListByNick.size()) * 100.0; - - if (percent > 95) - { - source.Reply(USERHOST_MASK_TOO_WIDE, mask.c_str()); - Log(LOG_ADMIN, u, this) << "tried to " << this->name << " " << percent << "% of the network (" << affected << " users)"; - return; - } - - XLine *x = this->xlm()->Add(mask, u->nick, expires, reason); - - EventReturn MOD_RESULT; - FOREACH_RESULT(I_OnAddXLine, OnAddXLine(u, x, this->xlm())); - if (MOD_RESULT == EVENT_STOP) - { - delete x; - return; - } - - source.Reply(_("\002%s\002 added to the %s list."), mask.c_str(), this->name.c_str()); - Log(LOG_ADMIN, u, this) << "on " << mask << " (" << reason << ") expires in " << (expires ? duration(expires - Anope::CurTime) : "never") << " [affects " << affected << " user(s) (" << percent << "%)]"; - - if (readonly) - source.Reply(READ_ONLY_MODE); - } - - } - else - this->OnSyntaxError(source, "ADD"); - - return; - } - - service_reference snlines; - public: - CommandOSSNLine(Module *creator) : CommandOSSXLineBase(creator, "operserv/snline"), snlines("xlinemanager/snline") - { - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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.\n")); - 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" - "Note: because the realname mask may contain spaces, the\n" - "separator between it and the reason is a colon.\n")); - 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 ¶ms) - { - if (!this->xlm()) - return; - - User *u = source.u; - unsigned last_param = 2; - Anope::string expiry, mask; - time_t expires; - - mask = params.size() > 1 ? params[1] : ""; - if (!mask.empty() && mask[0] == '+') - { - expiry = mask; - mask = params.size() > 2 ? params[2] : ""; - last_param = 3; - } - - expires = !expiry.empty() ? dotime(expiry) : Config->SQLineExpiry; - /* 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()) - { - std::pair canAdd = this->sqlines->CanAdd(mask, expires); - if (mask.find_first_not_of("*") == Anope::string::npos) - source.Reply(USERHOST_MASK_TOO_WIDE, mask.c_str()); - else if (canAdd.first == 1) - source.Reply(_("\002%s\002 already exists on the SQLINE list."), canAdd.second->Mask.c_str()); - else if (canAdd.first == 2) - source.Reply(_("Expiry time of \002%s\002 changed."), canAdd.second->Mask.c_str()); - else if (canAdd.first == 3) - source.Reply(_("\002%s\002 is already covered by %s."), mask.c_str(), canAdd.second->Mask.c_str()); - else - { - unsigned int affected = 0; - for (Anope::insensitive_map::iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it) - if (Anope::Match(it->second->nick, mask)) - ++affected; - float percent = static_cast(affected) / static_cast(UserListByNick.size()) * 100.0; - - if (percent > 95) - { - source.Reply(USERHOST_MASK_TOO_WIDE, mask.c_str()); - Log(LOG_ADMIN, u, this) << "tried to SQLine " << percent << "% of the network (" << affected << " users)"; - return; - } - - XLine *x = this->sqlines->Add(mask, u->nick, expires, reason); - - EventReturn MOD_RESULT; - FOREACH_RESULT(I_OnAddXLine, OnAddXLine(u, x, this->xlm())); - if (MOD_RESULT == EVENT_STOP) - { - delete x; - return; - } - - source.Reply(_("\002%s\002 added to the SQLINE list."), mask.c_str()); - Log(LOG_ADMIN, u, this) << "on " << mask << " (" << reason << ") expires in " << (expires ? duration(expires - Anope::CurTime) : "never") << " [affects " << affected << " user(s) (" << percent << "%)]"; - - if (readonly) - source.Reply(READ_ONLY_MODE); - } - - } - else - this->OnSyntaxError(source, "ADD"); - - return; - } - - service_reference sqlines; - public: - CommandOSSQLine(Module *creator) : CommandOSSXLineBase(creator, "operserv/sqline"), sqlines("xlinemanager/sqline") - { - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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 (on IRCds that \n" - "support it).\n")); - 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.\n")); - 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 CommandOSSZLine : public CommandOSSXLineBase -{ - XLineManager *xlm() - { - return this->szlines; - } - - void OnAdd(CommandSource &source, const std::vector ¶ms) - { - if (!this->xlm()) - return; - - User *u = source.u; - unsigned last_param = 2; - Anope::string expiry, mask; - time_t expires; - - mask = params.size() > 1 ? params[1] : ""; - if (!mask.empty() && mask[0] == '+') - { - expiry = mask; - mask = params.size() > 2 ? params[2] : ""; - last_param = 3; - } - - expires = !expiry.empty() ? dotime(expiry) : Config->SZLineExpiry; - /* 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()) - { - std::pair canAdd = this->szlines->CanAdd(mask, expires); - if (mask.find('!') != Anope::string::npos || mask.find('@') != Anope::string::npos) - source.Reply(_("You can only add IP masks to the SZLINE list.")); - else if (mask.find_first_not_of("*?") == Anope::string::npos) - source.Reply(USERHOST_MASK_TOO_WIDE, mask.c_str()); - else if (canAdd.first == 1) - source.Reply(_("\002%s\002 already exists on the SZLINE list."), canAdd.second->Mask.c_str()); - else if (canAdd.first == 2) - source.Reply(_("Expiry time of \002%s\002 changed."), canAdd.second->Mask.c_str()); - else if (canAdd.first == 3) - source.Reply(_("\002%s\002 is already covered by %s."), mask.c_str(), canAdd.second->Mask.c_str()); - else - { - User *user = finduser(mask); - if (user && user->ip()) - mask = user->ip.addr(); - unsigned int affected = 0; - for (Anope::insensitive_map::iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it) - if (it->second->ip() && Anope::Match(it->second->ip.addr(), mask)) - ++affected; - float percent = static_cast(affected) / static_cast(UserListByNick.size()) * 100.0; - - if (percent > 95) - { - source.Reply(USERHOST_MASK_TOO_WIDE, mask.c_str()); - Log(LOG_ADMIN, u, this) << "tried to SZLine " << percent << "% of the network (" << affected << " users)"; - return; - } - - XLine *x = this->szlines->Add(mask, u->nick, expires, reason); - - EventReturn MOD_RESULT; - FOREACH_RESULT(I_OnAddXLine, OnAddXLine(u, x, this->xlm())); - if (MOD_RESULT == EVENT_STOP) - { - delete x; - return; - } - - source.Reply(_("\002%s\002 added to the SZLINE list."), mask.c_str()); - Log(LOG_ADMIN, u, this) << "on " << mask << " (" << reason << ") expires in " << (expires ? duration(expires - Anope::CurTime) : "never") << " [affects " << affected << " user(s) (" << percent << "%)]"; - - if (readonly) - source.Reply(READ_ONLY_MODE); - } - - } - else - this->OnSyntaxError(source, "ADD"); - - return; - } - - service_reference szlines; - public: - CommandOSSZLine(Module *creator) : CommandOSSXLineBase(creator, "operserv/szline"), szlines("xlinemanager/szline") - { - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Allows Services operators to manipulate the SZLINE list. If\n" - "a user with an IP matching an SZLINE mask attempts to \n" - "connect, Services will not allow it to pursue his IRC\n" - "session (and this, whether the IP has a PTR RR or not).\n" - " \n")); - source.Reply(_("\002SZLINE ADD\002 adds the given (nick's) IP mask to the SZLINE\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 SZLINE 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 SZLINE default expiry time can be found with the\n" - "\002STATS AKILL\002 command.\n")); - source.Reply(_(" \n" - "The \002SZLINE DEL\002 command removes the given mask from the\n" - "SZLINE 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 \002SZLINE LIST\002 command displays the SZLINE 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" - " \002SZLINE LIST 2-5,7-9\002\n" - " Lists SZLINE entries numbered 2 through 5 and 7 \n" - " through 9.\n" - " \n" - "\002SZLINE VIEW\002 is a more verbose version of \002SZLINE LIST\002, and \n" - "will show who added an SZLINE, the date it was added, and when\n" - "it expires, as well as the IP mask and reason.\n" - " \n" - "\002SZLINE CLEAR\002 clears all entries of the SZLINE list.")); - return true; - } -}; - -class OSSXLine : public Module -{ - CommandOSSNLine commandossnline; - CommandOSSQLine commandossqline; - CommandOSSZLine commandosszline; - - public: - OSSXLine(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), - commandossnline(this), commandossqline(this), commandosszline(this) - { - this->SetAuthor("Anope"); - - if (ircd && ircd->snline) - ModuleManager::RegisterService(&commandossnline); - if (ircd && ircd->sqline) - ModuleManager::RegisterService(&commandossqline); - if (ircd && ircd->szline) - ModuleManager::RegisterService(&commandosszline); - } -}; - -MODULE_INIT(OSSXLine) diff --git a/modules/core/os_update.cpp b/modules/core/os_update.cpp deleted file mode 100644 index 776835cf7..000000000 --- a/modules/core/os_update.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* OperServ core functions - * - * (C) 2003-2011 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")); - this->SetSyntax(""); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - source.Reply(_("Updating databases.")); - save_databases(); - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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, CORE), - commandosupdate(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandosupdate); - } -}; - -MODULE_INIT(OSUpdate) diff --git a/modules/database/db_mysql.cpp b/modules/database/db_mysql.cpp new file mode 100644 index 000000000..59ab412b3 --- /dev/null +++ b/modules/database/db_mysql.cpp @@ -0,0 +1,1566 @@ +#include "module.h" +#include "../extra/sql.h" +#include "../commands/os_session.h" + +static Anope::string ToString(const std::vector &strings) +{ + Anope::string ret; + + for (unsigned i = 0; i < strings.size(); ++i) + ret += " " + strings[i]; + + if (!ret.empty()) + ret.erase(ret.begin()); + + return ret; +} + +static std::vector MakeVector(const Anope::string &buf) +{ + Anope::string s; + spacesepstream sep(buf); + std::vector params; + + while (sep.GetToken(s)) + { + if (s[0] == ':') + { + s.erase(s.begin()); + if (!s.empty() && !sep.StreamEnd()) + params.push_back(s + " " + sep.GetRemaining()); + else if (!s.empty()) + params.push_back(s); + } + else + params.push_back(s); + } + + return params; +} + +static NickAlias *CurNick = NULL; +static NickCore *CurCore = NULL; +static ChannelInfo *CurChannel = NULL; +static BotInfo *CurBot = NULL; + +static void Write(const Anope::string &data); +static void WriteNickMetadata(const Anope::string &key, const Anope::string &data); +static void WriteCoreMetadata(const Anope::string &key, const Anope::string &data); +static void WriteChannelMetadata(const Anope::string &key, const Anope::string &data); +static void WriteBotMetadata(const Anope::string &key, const Anope::string &data); + +class CommandSQLSync : public Command +{ + public: + CommandSQLSync(Module *creator) : Command(creator, "SQLSYNC", 0, 0) + { + this->SetDesc(_("Import your databases to SQL")); + this->SetSyntax(""); + } + + void Execute(CommandSource &source, const std::vector ¶ms); + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("This command syncs your databases with SQL. You should\n" + "only have to execute this command once, when you initially\n" + "import your databases into SQL.")); + return true; + } +}; + +class MySQLInterface : public SQLInterface +{ + public: + MySQLInterface(Module *o) : SQLInterface(o) { } + + void OnResult(const SQLResult &r); + + void OnError(const SQLResult &r); +}; + +class DBMySQL; +static DBMySQL *me; +class DBMySQL : public Module +{ + private: + CommandSQLSync commandsqlsync; + MySQLInterface sqlinterface; + service_reference SQL; + + public: + service_reference SessionInterface; + time_t lastwarn; + bool ro; + + void RunQuery(const SQLQuery &query) + { + if (SQL) + { + if (readonly && this->ro) + { + readonly = this->ro = false; + BotInfo *bi = findbot(Config->OperServ); + if (bi) + ircdproto->SendGlobops(bi, "Found SQL again, going out of readonly mode..."); + } + + SQL->Run(&sqlinterface, query); + } + else + { + if (Anope::CurTime - Config->UpdateTimeout > lastwarn) + { + BotInfo *bi = findbot(Config->OperServ); + if (bi) + ircdproto->SendGlobops(bi, "Unable to locate SQL reference, is m_mysql loaded? Going to readonly..."); + readonly = this->ro = true; + this->lastwarn = Anope::CurTime; + } + } + } + + DBMySQL(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, DATABASE), commandsqlsync(this), sqlinterface(this), SQL("mysql/main"), SessionInterface("session") + { + me = this; + + this->lastwarn = 0; + this->ro = false; + + Implementation i[] = { + I_OnLoadDatabase, I_OnServerConnect + }; + ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); + + ModuleManager::RegisterService(&commandsqlsync); + + if (CurrentUplink) + OnServerConnect(); + } + + void OnServerConnect() + { + Implementation i[] = { + /* Misc */ + I_OnSaveDatabase, I_OnPostCommand, + /* NickServ */ + I_OnNickAddAccess, I_OnNickEraseAccess, I_OnNickClearAccess, + I_OnDelCore, I_OnNickForbidden, I_OnNickGroup, + I_OnNickRegister, I_OnChangeCoreDisplay, + I_OnNickSuspended, I_OnDelNick, + /* ChanServ */ + I_OnAccessAdd, I_OnAccessDel, I_OnAccessClear, I_OnLevelChange, + I_OnChanForbidden, I_OnDelChan, I_OnChanRegistered, I_OnChanSuspend, + I_OnAkickAdd, I_OnAkickDel, I_OnMLock, I_OnUnMLock, + /* BotServ */ + I_OnBotCreate, I_OnBotChange, I_OnBotDelete, + I_OnBotAssign, I_OnBotUnAssign, + I_OnBadWordAdd, I_OnBadWordDel, + /* MemoServ */ + I_OnMemoSend, I_OnMemoDel, + /* OperServ */ + I_OnExceptionAdd, I_OnExceptionDel, + I_OnAddXLine, I_OnDelXLine, + /* HostServ */ + I_OnSetVhost, I_OnDeleteVhost + }; + ModuleManager::Attach(i, this, 40); + } + + EventReturn OnLoadDatabase() + { + if (!SQL) + { + Log() << "Error, unable to find service reference for SQL, is m_mysql loaded and configured properly?"; + return EVENT_CONTINUE; + } + + SQLQuery query; + + query = "SELECT * FROM `anope_ns_core`"; + SQLResult r = SQL->RunQuery(query); + for (int i = 0; i < r.Rows(); ++i) + { + NickCore *nc = new NickCore(r.Get(i, "display")); + nc->pass = r.Get(i, "pass"); + nc->email = r.Get(i, "email"); + nc->greet = r.Get(i, "greet"); + + spacesepstream sep(r.Get(i, "flags")); + Anope::string buf; + std::vector flags; + while (sep.GetToken(buf)) + flags.push_back(buf); + nc->FromString(flags); + + nc->language = r.Get(i, "language"); + nc->memos.memomax = r.Get(i, "memomax").is_number_only() ? convertTo(r.Get(i, "memomax")) : 20; + } + + query = "SELECT * FROM `anope_ns_access`"; + r = SQL->RunQuery(query); + for (int i = 0; i < r.Rows(); ++i) + { + NickCore *nc = findcore(r.Get(i, "display")); + if (!nc) + { + Log() << "MySQL: Got NickCore access entry for nonexistant core " << r.Get(i, "display"); + continue; + } + + nc->AddAccess(r.Get(i, "access")); + } + + query = "SELECT * FROM `anope_ns_core_metadata`"; + r = SQL->RunQuery(query); + for (int i = 0; i < r.Rows(); ++i) + { + NickCore *nc = findcore(r.Get(i, "display")); + if (!nc) + { + Log() << "MySQL: Got NickCore access entry for nonexistant core " << r.Get(i, "display"); + continue; + } + + EventReturn MOD_RESULT;; + std::vector Params = MakeVector(r.Get(i, "value")); + FOREACH_RESULT(I_OnDatabaseReadMetadata, OnDatabaseReadMetadata(nc, r.Get(i, "name"), Params)); + } + + query = "SELECT * FROM `anope_ns_alias`"; + r = SQL->RunQuery(query); + for (int i = 0; i < r.Rows(); ++i) + { + NickCore *nc = findcore(r.Get(i, "display")); + if (!nc) + { + Log() << "MySQL: Got NickAlias for nick " << r.Get(i, "nick") << " with nonexistant core " << r.Get(i, "display"); + continue; + } + + NickAlias *na = new NickAlias(r.Get(i, "nick"), nc); + na->last_quit = r.Get(i, "last_quit"); + na->last_realname = r.Get(i, "last_realname"); + na->last_usermask = r.Get(i, "last_usermask"); + na->time_registered = r.Get(i, "time_registered").is_pos_number_only() ? convertTo(r.Get(i, "time_registered")) : Anope::CurTime; + na->last_seen = r.Get(i, "last_seen").is_pos_number_only() ? convertTo(r.Get(i, "last_seen")) : Anope::CurTime; + + spacesepstream sep(r.Get(i, "flags")); + Anope::string buf; + std::vector flags; + while (sep.GetToken(buf)) + flags.push_back(buf); + + na->FromString(flags); + } + + query = "SELECT * FROM `anope_ns_alias_metadata`"; + r = SQL->RunQuery(query); + for (int i = 0; i < r.Rows(); ++i) + { + NickAlias *na = findnick(r.Get(i, "nick")); + if (!na) + { + Log() << "MySQL: Got metadata for nonexistant nick " << r.Get(i, "nick"); + continue; + } + + EventReturn MOD_RESULT; + std::vector Params = MakeVector(r.Get(i, "value")); + FOREACH_RESULT(I_OnDatabaseReadMetadata, OnDatabaseReadMetadata(na, na->nick, Params)); + } + + query = "SELECT * FROM `anope_hs_core`"; + r = SQL->RunQuery(query); + for (int i = 0; i < r.Rows(); ++i) + { + NickAlias *na = findnick(r.Get(i, "nick")); + if (!na) + { + Log() << "MySQL: Got vhost entry for nonexistant nick " << r.Get(i, "nick"); + continue; + } + + time_t creation = r.Get(i, "time").is_pos_number_only() ? convertTo(r.Get(i, "time")) : Anope::CurTime; + na->hostinfo.SetVhost(r.Get(i, "vident"), r.Get(i, "vhost"), r.Get(i, "creator"), creation); + } + + query = "SELECT * FROM `anope_bs_core`"; + r = SQL->RunQuery(query); + for (int i = 0; i < r.Rows(); ++i) + { + BotInfo *bi = findbot(r.Get(i, "nick")); + if (!bi) + bi = new BotInfo(r.Get(i, "nick"), r.Get(i, "user"), r.Get(i, "host")); + bi->realname = r.Get(i, "rname"); + bi->created = r.Get(i, "created").is_pos_number_only() ? convertTo(r.Get(i, "created")) : Anope::CurTime; + + spacesepstream sep(r.Get(i, "flags")); + Anope::string buf; + std::vector flags; + while (sep.GetToken(buf)) + flags.push_back(buf); + + bi->FromString(flags); + } + + query = "SELECT * FROM `anope_bs_info_metadata`"; + r = SQL->RunQuery(query); + for (int i = 0; i < r.Rows(); ++i) + { + BotInfo *bi = findbot(r.Get(i, "botname")); + if (!bi) + { + Log() << "MySQL: BotInfo metadata for nonexistant bot " << r.Get(i, "botname"); + continue; + } + + EventReturn MOD_RESULT; + std::vector Params = MakeVector(r.Get(i, "value")); + FOREACH_RESULT(I_OnDatabaseReadMetadata, OnDatabaseReadMetadata(bi, bi->nick, Params)); + } + + query = "SELECT * FROM `anope_cs_info`"; + r = SQL->RunQuery(query); + for (int i = 0; i < r.Rows(); ++i) + { + NickCore *nc = NULL; + + if (!r.Get(i, "founder").empty()) + { + nc = findcore(r.Get(i, "founder")); + if (!nc) + { + Log() << "MySQL: Channel " << r.Get(i, "name") << " with nonexistant founder " << r.Get(i, "founder"); + continue; + } + + ChannelInfo *ci = new ChannelInfo(r.Get(i, "name")); + ci->SetFounder(nc); + if (!r.Get(i, "successor").empty()) + ci->successor = findcore(r.Get(i, "successor")); + ci->desc = r.Get(i, "descr"); + ci->time_registered = r.Get(i, "time_registered").is_pos_number_only() ? convertTo(r.Get(i, "time_registered")) : Anope::CurTime; + ci->last_used = r.Get(i, "last_used").is_pos_number_only() ? convertTo(r.Get(i, "last_used")) : Anope::CurTime; + ci->last_topic = r.Get(i, "last_topic"); + ci->last_topic_setter = r.Get(i, "last_topic_setter"); + ci->last_topic_time = r.Get(i, "last_topic_time").is_number_only() ? convertTo(r.Get(i, "last_topic_time")) : Anope::CurTime; + ci->bantype = r.Get(i, "bantype").is_number_only() ? convertTo(r.Get(i, "bantype")) : 2; + ci->memos.memomax = r.Get(i, "memomax").is_number_only() ? convertTo(r.Get(i, "memomax")) : 20; + ci->capsmin = r.Get(i, "capsmin").is_number_only() ? convertTo(r.Get(i, "capsmin")) : 0; + ci->capspercent = r.Get(i, "capspercent").is_number_only() ? convertTo(r.Get(i, "capspercent")) : 0; + ci->floodlines = r.Get(i, "floodlines").is_number_only() ? convertTo(r.Get(i, "floodlines")) : 0; + ci->floodsecs = r.Get(i, "floodsecs").is_number_only() ? convertTo(r.Get(i, "floodsecs")) : 0; + ci->repeattimes = r.Get(i, "repeattimes").is_number_only() ? convertTo(r.Get(i, "repeattimes")) : 0; + ci->bi = findbot(r.Get(i, "botnick")); + if (ci->bi && !r.Get(i, "botflags").empty()) + { + spacesepstream sep(r.Get(i, "botflags")); + Anope::string buf; + std::vector flags; + while (sep.GetToken(buf)) + flags.push_back(buf); + + ci->botflags.FromString(flags); + } + + if (!r.Get(i, "flags").empty()) + { + spacesepstream sep(r.Get(i, "flags")); + Anope::string buf; + std::vector flags; + while (sep.GetToken(buf)) + flags.push_back(buf); + + ci->FromString(flags); + } + } + } + + query = "SELECT * FROM `anope_cs_ttb`"; + r = SQL->RunQuery(query); + for (int i = 0; i < r.Rows(); ++i) + { + ChannelInfo *ci = cs_findchan(r.Get(i, "channel")); + if (!ci) + { + Log() << "MySQL: Channel ttb for nonexistant channel " << r.Get(i, "channel"); + continue; + } + + ci->ttb[atoi(r.Get(i, "ttb_id").c_str())] = atoi(r.Get(i, "value").c_str()); + } + + query = "SELECT * FROM `anope_bs_badwords`"; + r = SQL->RunQuery(query); + for (int i = 0; i < r.Rows(); ++i) + { + ChannelInfo *ci = cs_findchan(r.Get(i, "channel")); + if (!ci) + { + Log() << "MySQL: Channel badwords entry for nonexistant channel " << r.Get(i, "channel"); + continue; + } + + BadWordType BWTYPE = BW_ANY; + if (r.Get(i, "type").equals_cs("SINGLE")) + BWTYPE = BW_SINGLE; + else if (r.Get(i, "type").equals_cs("START")) + BWTYPE = BW_START; + else if (r.Get(i, "type").equals_cs("END")) + BWTYPE = BW_END; + ci->AddBadWord(r.Get(i, "word"), BWTYPE); + } + + query = "SELECT * FROM `anope_cs_access`"; + r = SQL->RunQuery(query); + for (int i = 0; i < r.Rows(); ++i) + { + ChannelInfo *ci = cs_findchan(r.Get(i, "channel")); + if (!ci) + { + Log() << "MySQL: Channel access entry for nonexistant channel " << r.Get(i, "channel"); + continue; + } + + const Anope::string &provider = r.Get(i, "provider"), &data = r.Get(i, "data"); + service_reference ap(provider); + if (!ap) + { + Log() << "MySQL: Access entry for " << ci->name << " using nonexistant provider " << provider; + continue; + } + + ChanAccess *access = ap->Create(); + access->ci = ci; + access->mask = r.Get(i, "mask"); + access->creator = r.Get(i, "creator"); + access->last_seen = r.Get(i, "last_seen").is_pos_number_only() ? convertTo(r.Get(i, "last_seen")) : Anope::CurTime; + access->created = r.Get(i, "created").is_pos_number_only() ? convertTo(r.Get(i, "created")) : Anope::CurTime; + access->Unserialize(data); + ci->AddAccess(access); + } + + query = "SELECT * FROM `anope_cs_akick`"; + r = SQL->RunQuery(query); + for (int i = 0; i < r.Rows(); ++i) + { + ChannelInfo *ci = cs_findchan(r.Get(i, "channel")); + if (!ci) + { + Log() << "MySQL: Channel access entry for nonexistant channel " << r.Get(i, "channel"); + continue; + } + + NickCore *nc = NULL; + spacesepstream sep(r.Get(i, "flags")); + Anope::string flag, mask; + while (sep.GetToken(flag)) + { + if (flag.equals_cs("ISNICK")) + nc = findcore(r.Get(i, "mask")); + + AutoKick *ak; + if (nc) + ak = ci->AddAkick(r.Get(i, "creator"), nc, r.Get(i, "reason"), atol(r.Get(i, "created").c_str()), atol(r.Get(i, "last_used").c_str())); + else + ak = ci->AddAkick(r.Get(i, "creator"), r.Get(i, "mask"), r.Get(i, "reason"), atol(r.Get(i, "created").c_str()), atol(r.Get(i, "last_used").c_str())); + if (nc) + ak->SetFlag(AK_ISNICK); + } + } + + query = "SELECT * FROM `anope_cs_levels`"; + r = SQL->RunQuery(query); + for (int i = 0; i < r.Rows(); ++i) + { + ChannelInfo *ci = cs_findchan(r.Get(i, "channel")); + if (!ci) + { + Log() << "MySQL: Channel level entry for nonexistant channel " << r.Get(i, "channel"); + continue; + } + + ci->levels[atoi(r.Get(i, "position").c_str())] = atoi(r.Get(i, "level").c_str()); + } + + query = "SELECT * FROM `anope_cs_info_metadata`"; + r = SQL->RunQuery(query); + for (int i = 0; i < r.Rows(); ++i) + { + ChannelInfo *ci = cs_findchan(r.Get(i, "channel")); + if (!ci) + { + Log() << "MySQL: Channel metadata for nonexistant channel " << r.Get(i, "channel"); + continue; + } + + EventReturn MOD_RESULT; + std::vector Params = MakeVector(r.Get(i, "value")); + FOREACH_RESULT(I_OnDatabaseReadMetadata, OnDatabaseReadMetadata(ci, ci->name, Params)); + } + + query = "SELECT * FROM `anope_cs_mlock`"; + r = SQL->RunQuery(query); + for (int i = 0; i < r.Rows(); ++i) + { + ChannelInfo *ci = cs_findchan(r.Get(i, "channel")); + if (!ci) + { + Log() << "MySQL: Channel mlock for nonexistant channel " << r.Get(i, "channel"); + continue; + } + + std::vector mlocks; + ci->GetExtRegular("db_mlock", mlocks); + + Anope::string modestring = r.Get(i, "status") + " " + r.Get(i, "mode") + " " + r.Get(i, "setter") + " " + r.Get(i, "created") + " " + r.Get(i, "param"); + + mlocks.push_back(modestring); + + ci->Extend("db_mlock", new ExtensibleItemRegular >(mlocks)); + } + + query = "SELECT * FROM `anope_ms_info`"; + r = SQL->RunQuery(query); + for (int i = 0; i < r.Rows(); ++i) + { + MemoInfo *mi = NULL; + if (r.Get(i, "serv").equals_cs("NICK")) + { + NickCore *nc = findcore(r.Get(i, "receiver")); + if (nc) + mi = &nc->memos; + } + else if (r.Get(i, "serv").equals_cs("CHAN")) + { + ChannelInfo *ci = cs_findchan(r.Get(i, "receiver")); + if (ci) + mi = &ci->memos; + } + if (mi) + { + Memo *m = new Memo(); + mi->memos.push_back(m); + m->sender = r.Get(i, "sender"); + m->time = r.Get(i, "time").is_pos_number_only() ? convertTo(r.Get(i, "time")) : Anope::CurTime; + m->text = r.Get(i, "text"); + + if (!r.Get(i, "flags").empty()) + { + spacesepstream sep(r.Get(i, "flags")); + Anope::string buf; + std::vector flags; + while (sep.GetToken(buf)) + flags.push_back(buf); + + m->FromString(flags); + } + } + } + + for (std::list::iterator it = XLineManager::XLineManagers.begin(), it_end = XLineManager::XLineManagers.end(); it != it_end; ++it) + { + XLineManager *xm = *it; + + query = "SELECT * FROM `anope_os_xlines` WHERE `type` = @type"; + query.setValue("type", Anope::string(xm->Type())); + r = SQL->RunQuery(query); + for (int i = 0; i < r.Rows(); ++i) + { + Anope::string user = r.Get(i, "user"); + Anope::string host = r.Get(i, "host"); + Anope::string by = r.Get(i, "xby"); + Anope::string reason = r.Get(i, "reason"); + time_t seton = r.Get(i, "seton").is_pos_number_only() ? convertTo(r.Get(i, "seton")) : Anope::CurTime; + time_t expires = r.Get(i, "expires").is_pos_number_only() ? convertTo(r.Get(i, "expires")) : Anope::CurTime; + + XLine *x = xm->Add(user + "@" + host, by, expires, reason); + if (x) + x->Created = seton; + } + } + + query = "SELECT * FROM `anope_os_exceptions`"; + r = SQL->RunQuery(query); + for (int i = 0; i < r.Rows(); ++i) + { + Anope::string mask = r.Get(i, "mask"); + unsigned limit = convertTo(r.Get(i, "slimit")); + Anope::string creator = r.Get(i, "who"); + Anope::string reason = r.Get(i, "reason"); + time_t expires = convertTo(r.Get(i, "expires")); + + if (SessionInterface) + { + Exception *e = new Exception(); + e->mask = mask; + e->limit = limit; + e->who = creator; + e->reason = reason; + e->time = expires; + SessionInterface->AddException(e); + } + } + + query = "SELECT * FROM `anope_extra`"; + r = SQL->RunQuery(query); + for (int i = 0; i < r.Rows(); ++i) + { + std::vector params = MakeVector(r.Get(i, "data")); + EventReturn MOD_RESULT; + FOREACH_RESULT(I_OnDatabaseRead, OnDatabaseRead(params)); + } + + query = "SELECT * FROM `anope_ns_core_metadata`"; + r = SQL->RunQuery(query); + for (int i = 0; i < r.Rows(); ++i) + { + NickCore *nc = findcore(r.Get(i, "nick")); + if (!nc) + continue; + if (r.Get(i, "name") == "MEMO_IGNORE") + nc->memos.ignores.push_back(r.Get(i, "value").ci_str()); + } + + query = "SELECT * FROM `anope_cs_info_metadata`"; + r = SQL->RunQuery(query); + for (int i = 0; i < r.Rows(); ++i) + { + ChannelInfo *ci = cs_findchan(r.Get(i, "channel")); + if (!ci) + continue; + if (r.Get(i, "name") == "MEMO_IGNORE") + ci->memos.ignores.push_back(r.Get(i, "value").ci_str()); + } + + return EVENT_STOP; + } + + EventReturn OnSaveDatabase() + { + SQLQuery query; + + query = "TRUNCATE TABLE `anope_os_core`"; + this->RunQuery(query); + + query = "INSERT INTO `anope_os_core` (maxusercnt, maxusertime, akills_count, snlines_count, sqlines_count, szlines_count) VALUES(@maxusercnt, @maxusertime, @akills_count, @snlines_count, @sqlines_count, @szlines_count)"; + query.setValue("maxusercnt", maxusercnt); + query.setValue("maxusertime", maxusertime); + this->RunQuery(query); + + for (nickcore_map::const_iterator it = NickCoreList.begin(), it_end = NickCoreList.end(); it != it_end; ++it) + { + CurCore = it->second; + FOREACH_MOD(I_OnDatabaseWriteMetadata, OnDatabaseWriteMetadata(WriteCoreMetadata, CurCore)); + } + + for (nickalias_map::const_iterator it = NickAliasList.begin(), it_end = NickAliasList.end(); it != it_end; ++it) + { + CurNick = it->second; + FOREACH_MOD(I_OnDatabaseWriteMetadata, OnDatabaseWriteMetadata(WriteNickMetadata, CurNick)); + } + + for (registered_channel_map::const_iterator it = RegisteredChannelList.begin(), it_end = RegisteredChannelList.end(); it != it_end; ++it) + { + CurChannel = it->second; + FOREACH_MOD(I_OnDatabaseWriteMetadata, OnDatabaseWriteMetadata(WriteChannelMetadata, CurChannel)); + } + + for (Anope::insensitive_map::const_iterator it = BotListByNick.begin(), it_end = BotListByNick.end(); it != it_end; ++it) + { + if (it->second->HasFlag(BI_CONF)) + continue; + + CurBot = it->second; + FOREACH_MOD(I_OnDatabaseWriteMetadata, OnDatabaseWriteMetadata(WriteBotMetadata, CurBot)); + + query = "INSERT INTO `anope_bs_core` (nick, user, host, rname, flags, created, chancount) VALUES(@nick, @user, @host, @rname, @flags, @created, @chancount) ON DUPLICATE KEY UPDATE nick=VALUES(nick), user=VALUES(user), host=VALUES(host), rname=VALUES(rname), flags=VALUES(flags), created=VALUES(created), chancount=VALUES(chancount)"; + query.setValue("nick", CurBot->nick); + query.setValue("user", CurBot->GetIdent()); + query.setValue("host", CurBot->host); + query.setValue("rname", CurBot->realname); + query.setValue("flags", ToString(CurBot->ToString())); + query.setValue("created", CurBot->created); + query.setValue("chancount", CurBot->chancount); + this->RunQuery(query); + } + + query = "TRUNCATE TABLE `anope_extra`"; + this->RunQuery(query); + FOREACH_MOD(I_OnDatabaseWrite, OnDatabaseWrite(Write)); + + return EVENT_CONTINUE; + } + + void OnPostCommand(CommandSource &source, Command *command, const std::vector ¶ms) + { + User *u = source.u; + + if (command->name.find("nickserv/set/") == 0 || command->name.find("nickserv/saset/") == 0) + { + NickAlias *na = findnick(command->name.find("nickserv/set/") == 0 ? source.u->nick : params[1]); + if (!na) + return; + + if (command->name == "nickserv/set/password" || command->name == "nickserv/saset/password") + { + SQLQuery query("UPDATE `anope_ns_core` SET `pass` = @pass WHERE `display` = @display"); + query.setValue("pass", na->nc->pass); + query.setValue("display", na->nc->display); + this->RunQuery(query); + } + else if (command->name == "nickserv/set/language" || command->name == "nickserv/saset/language") + { + SQLQuery query("UPDATE `anope_ns_core` SET `language` = @language WHERE `display` = @display"); + query.setValue("language", na->nc->language); + query.setValue("display", na->nc->display); + this->RunQuery(query); + } + else if (command->name == "nickserv/set/email" || command->name == "nickserv/saset/email") + { + SQLQuery query("UPDATE `anope_ns_core` SET `email` = @email WHERE `display` = @display"); + query.setValue("email", na->nc->email); + query.setValue("display", na->nc->display); + this->RunQuery(query); + } + else if (command->name == "nickserv/set/greet" || command->name == "nickserv/saset/greet") + { + SQLQuery query("UPDATE `anope_ns_core` SET `greet` = @greet WHERE `display` = @display"); + query.setValue("greet", na->nc->greet); + query.setValue("display", na->nc->display); + this->RunQuery(query); + } + else + { + SQLQuery query("UPDATE `anope_ns_core` SET `flags` = @flags WHERE `display` = @display"); + query.setValue("flags", ToString(na->nc->ToString())); + query.setValue("display", na->nc->display); + this->RunQuery(query); + } + } + else if (command->name.find("chanserv/set") == 0 || command->name.find("chanserv/saset") == 0) + { + ChannelInfo *ci = params.size() > 0 ? cs_findchan(params[0]) : NULL; + if (!ci) + return; + + if (command->name == "chanserv/set/founder" || command->name == "chanserv/saset/founder") + { + SQLQuery query("UPDATE `anope_cs_info` SET `founder` = @founder WHERE `name` = @name"); + query.setValue("founder", ci->GetFounder() ? ci->GetFounder()->display : ""); + query.setValue("name", ci->name); + this->RunQuery(query); + } + else if (command->name == "chanserv/set/successor" || command->name == "chanserv/saset/successor") + { + SQLQuery query("UPDATE `anope_cs_info` SET `successor` = @successor WHERE `name` = @name"); + query.setValue("successor", ci->successor ? ci->successor->display : ""); + query.setValue("name", ci->name); + this->RunQuery(query); + } + else if (command->name == "chanserv/set/desc" || command->name == "chanserv/saset/desc") + { + SQLQuery query("UPDATE `anope_cs_info` SET `descr` = @descr WHERE `name` = @name"); + query.setValue("descr", ci->desc); + query.setValue("name", ci->name); + this->RunQuery(query); + } + else if (command->name == "chanserv/set/bantype" || command->name == "chanserv/saset/bantype") + { + SQLQuery query("UPDATE `anope_cs_info` SET `bantype` = @bantype WHERE `name` = @name"); + query.setValue("bantype", ci->bantype); + query.setValue("name", ci->name); + this->RunQuery(query); + } + else + { + SQLQuery query("UPDATE `anope_cs_info` SET `flags` = @flags WHERE `name` = @name"); + query.setValue("flags", ToString(ci->ToString())); + query.setValue("name", ci->name); + this->RunQuery(query); + } + } + else if (command->name == "botserv/kick" && params.size() > 2) + { + ChannelInfo *ci = cs_findchan(params[0]); + if (!ci) + return; + if (!ci->HasPriv(u, CA_SET) && !u->HasPriv("botserv/administration")) + return; + if (params[1].equals_ci("BADWORDS") || params[1].equals_ci("BOLDS") || params[1].equals_ci("CAPS") || params[1].equals_ci("COLORS") || params[1].equals_ci("FLOOD") || params[1].equals_ci("REPEAT") || params[1].equals_ci("REVERSES") || params[1].equals_ci("UNDERLINES")) + { + if (params[2].equals_ci("ON") || params[2].equals_ci("OFF")) + { + for (int i = 0; i < TTB_SIZE; ++i) + { + SQLQuery query("INSERT INTO `anope_cs_ttb` (channel, ttb_id, value) VALUES(@channel, @ttb_id, @value) ON DUPLICATE KEY UPDATE channel=VALUES(channel), ttb_id=VALUES(ttb_id), value=VALUES(value)"); + query.setValue("channel", ci->name); + query.setValue("ttb_id", i); + query.setValue("value", ci->ttb[i]); + this->RunQuery(query); + } + + { + SQLQuery query("UPDATE `anope_cs_info` SET `botflags` = @botflags WHERE `name` = @name"); + query.setValue("botflags", ToString(ci->botflags.ToString())); + query.setValue("name", ci->name); + this->RunQuery(query); + } + + if (params[1].equals_ci("CAPS")) + { + SQLQuery query("UPDATE `anope_cs_info` SET `capsmin` = @capsmin, `capspercent` = @capspercent WHERE `name` = @name"); + query.setValue("capsmin", ci->capsmin); + query.setValue("capspercent", ci->capspercent); + query.setValue("name", ci->name); + this->RunQuery(query); + } + else if (params[1].equals_ci("FLOOD")) + { + SQLQuery query("UPDATE `anope_cs_info` SET `floodlines` = @floodlines, `floodsecs` = @floodsecs WHERE `name` = @name"); + query.setValue("floodlines", ci->floodlines); + query.setValue("floodsecs", ci->floodsecs); + query.setValue("name", ci->name); + this->RunQuery(query); + } + else if (params[1].equals_ci("REPEAT")) + { + SQLQuery query("UPDATE `anope_cs_info` SET `repeattimes` = @ WHERE `name` = @"); + query.setValue("repeattimes", ci->repeattimes); + query.setValue("name", ci->name); + this->RunQuery(query); + } + } + } + } + else if (command->name == "botserv/set" && params.size() > 1) + { + ChannelInfo *ci = cs_findchan(params[0]); + if (ci && !ci->HasPriv(u, CA_SET) && !u->HasPriv("botserv/administration")) + return; + BotInfo *bi = NULL; + if (!ci) + bi = findbot(params[0]); + if (bi && params[1].equals_ci("PRIVATE") && u->HasPriv("botserv/set/private")) + { + SQLQuery query("UPDATE `anope_bs_core` SET `flags` = @ WHERE `nick` = @"); + query.setValue("flags", ToString(bi->ToString())); + query.setValue("nick", bi->nick); + this->RunQuery(query); + } + else if (!ci) + return; + else if (params[1].equals_ci("DONTKICKOPS") || params[1].equals_ci("DONTKICKVOICES") || params[1].equals_ci("FANTASY") || params[1].equals_ci("GREET") || params[1].equals_ci("SYMBIOSIS") || params[1].equals_ci("NOBOT")) + { + SQLQuery query("UPDATE `anope_cs_info` SET `botflags` = @ WHERE `name` = @"); + query.setValue("botflags", ToString(ci->botflags.ToString())); + query.setValue("name", ci->name); + this->RunQuery(query); + } + } + else if (command->name == "memoserv/ignore" && params.size() > 0) + { + Anope::string target = params[0]; + NickCore *nc = NULL; + ChannelInfo *ci = NULL; + if (target[0] != '#') + { + target = u->nick; + nc = u->Account(); + if (!nc) + return; + } + else + { + ci = cs_findchan(target); + if (!ci || !ci->HasPriv(u, CA_MEMO)) + return; + } + + MemoInfo *mi = ci ? &ci->memos : &nc->memos; + Anope::string table = ci ? "anope_cs_info_metadata" : "anope_ns_core_metadata"; + Anope::string ename = ci ? "channel" : "nick"; + + SQLQuery query("DELETE FROM `" + table + "` WHERE `" + ename + "` = @target AND `name` = 'MEMO_IGNORE'"); + query.setValue("target", target); + this->RunQuery(query); + + query = "INSERT INTO `" + table + "` VALUES(" + ename + ", name, value) (@target, 'MEMO_IGNORE, @ignore)"; + query.setValue("target", target); + for (unsigned j = 0; j < mi->ignores.size(); ++j) + { + query.setValue("ignore", mi->ignores[j]); + this->RunQuery(query); + } + } + } + + void OnNickAddAccess(NickCore *nc, const Anope::string &entry) + { + SQLQuery query("INSERT INTO `anope_ns_access` (display, access) VALUES(@display, @access)"); + query.setValue("display", nc->display); + query.setValue("access", entry); + this->RunQuery(query); + } + + void OnNickEraseAccess(NickCore *nc, const Anope::string &entry) + { + SQLQuery query("DELETE FROM `anope_ns_access` WHERE `display` = @display AND `access` = @access"); + query.setValue("display", nc->display); + query.setValue("access", entry); + this->RunQuery(query); + } + + void OnNickClearAccess(NickCore *nc) + { + SQLQuery query("DELETE FROM `anope_ns_access` WHERE `display` = @display"); + query.setValue("display", nc->display); + this->RunQuery(query); + } + + void OnDelCore(NickCore *nc) + { + SQLQuery query("DELETE FROM `anope_ns_core` WHERE `display` = @display"); + query.setValue("display", nc->display); + this->RunQuery(query); + } + + void OnNickForbidden(NickAlias *na) + { + SQLQuery query("UPDATE `anope_ns_alias` SET `flags` = @flags WHERE `nick` = @nick"); + query.setValue("flags", ToString(na->ToString())); + query.setValue("nick", na->nick); + this->RunQuery(query); + } + + void OnNickGroup(User *u, NickAlias *) + { + OnNickRegister(findnick(u->nick)); + } + + void InsertAlias(NickAlias *na) + { + SQLQuery query("INSERT INTO `anope_ns_alias` (nick, last_quit, last_realname, last_usermask, time_registered, last_seen, flags, display) VALUES(@nick, @last_quit, @last_realname, @last_usermask, @time_registered, @last_seen, @flags, @display) ON DUPLICATE KEY UPDATE last_quit=VALUES(last_quit), last_realname=VALUES(last_realname), last_usermask=VALUES(last_usermask), time_registered=VALUES(time_registered), last_seen=VALUES(last_seen), flags=VALUES(flags), display=VALUES(display)"); + query.setValue("nick", na->nick); + query.setValue("last_quit", na->last_quit); + query.setValue("last_realname", na->last_realname); + query.setValue("last_usermask", na->last_usermask); + query.setValue("time_registered", na->time_registered); + query.setValue("last_seen", na->last_seen); + query.setValue("flags", ToString(na->ToString())); + query.setValue("display", na->nc->display); + this->RunQuery(query); + } + + void InsertCore(NickCore *nc) + { + SQLQuery query("INSERT INTO `anope_ns_core` (display, pass, email, greet, flags, language, memomax) VALUES(@display, @pass, @email, @greet, @flags, @language, @memomax) ON DUPLICATE KEY UPDATE pass=VALUES(pass), email=VALUES(email), greet=VALUES(greet), flags=VALUES(flags), language=VALUES(language), memomax=VALUES(memomax)"); + query.setValue("display", nc->display); + query.setValue("pass", nc->pass); + query.setValue("email", nc->email); + query.setValue("greet", nc->greet); + query.setValue("flags", ToString(nc->ToString())); + query.setValue("language", nc->language); + query.setValue("memomax", nc->memos.memomax); + this->RunQuery(query); + } + + void OnNickRegister(NickAlias *na) + { + this->InsertCore(na->nc); + this->InsertAlias(na); + } + + void OnChangeCoreDisplay(NickCore *nc, const Anope::string &newdisplay) + { + SQLQuery query("UPDATE `anope_ns_core` SET `display` = @newdisplay WHERE `display` = @olddisplay"); + query.setValue("newdisplay", newdisplay); + query.setValue("olddisplay", nc->display); + this->RunQuery(query); + } + + void OnNickSuspend(NickAlias *na) + { + SQLQuery query("UPDATE `anope_ns_core` SET `flags` = @flags WHERE `display` = @display"); + query.setValue("flags", ToString(na->nc->ToString())); + query.setValue("display", na->nc->display); + this->RunQuery(query); + } + + void OnDelNick(NickAlias *na) + { + SQLQuery query("DELETE FROM `anope_ns_alias` WHERE `nick` = @nick"); + query.setValue("nick", na->nick); + this->RunQuery(query); + } + + void OnAccessAdd(ChannelInfo *ci, User *, ChanAccess *access) + { + SQLQuery query("INSERT INTO `anope_cs_access` (provider, data, mask, channel, last_seen, creator) VALUES (@provider, @data, @mask, @channel, @last_seen, @creator) ON DUPLICATE KEY UPDATE level=VALUES(level), display=VALUES(display), channel=VALUES(channel), last_seen=VALUES(last_seen), creator=VALUES(creator)"); + query.setValue("provider", access->provider->name); + query.setValue("data", access->Serialize()); + query.setValue("mask", access->mask); + query.setValue("channel", ci->name); + query.setValue("last_seen", access->last_seen); + query.setValue("creator", access->creator); + this->RunQuery(query); + } + + void OnAccessDel(ChannelInfo *ci, User *u, ChanAccess *access) + { + SQLQuery query("DELETE FROM `anope_cs_access` WHERE `mask` = @mask AND `channel` = @channel"); + query.setValue("mask", access->mask); + query.setValue("channel", ci->name); + this->RunQuery(query); + } + + void OnAccessClear(ChannelInfo *ci, User *u) + { + SQLQuery query("DELETE FROM `anope_cs_access` WHERE `channel` = @channel"); + query.setValue("channel", ci->name); + this->RunQuery(query); + } + + void OnLevelChange(User *u, ChannelInfo *ci, int pos, int what) + { + SQLQuery query("UPDATE `anope_cs_levels` SET `level` = @level WHERE `channel` = @channel AND `position` = @pos ON DUPLICATE KEY UPDATE level=VALUES(level), position=VALUES(position)"); + if (pos >= 0) + { + query.setValue("level", what); + query.setValue("channel", ci->name); + query.setValue("pos", pos); + this->RunQuery(query); + } + else + { + query.setValue("channel", ci->name); + for (int i = 0; i < CA_SIZE; ++i) + { + query.setValue("level", ci->levels[i]); + query.setValue("pos", i); + this->RunQuery(query); + } + } + } + + void OnChanForbidden(ChannelInfo *ci) + { + SQLQuery query("INSERT INTO `anope_cs_info` (name, time_registered, last_used, flags) VALUES (@name, @time_registered, @last_used, @flags)"); + query.setValue("name", ci->name); + query.setValue("time_registered", ci->time_registered); + query.setValue("last_used", ci->last_used); + query.setValue("flags", ToString(ci->ToString())); + this->RunQuery(query); + } + + void OnDelChan(ChannelInfo *ci) + { + SQLQuery query("DELETE FROM `anope_cs_info` WHERE `name` = @name"); + query.setValue("name", ci->name); + this->RunQuery(query); + } + + void OnChanRegistered(ChannelInfo *ci) + { + SQLQuery query("INSERT INTO `anope_cs_info` (name, founder, successor, descr, time_registered, last_used, last_topic, last_topic_setter, last_topic_time, flags, bantype, memomax, botnick, botflags, capsmin, capspercent, floodlines, floodsecs, repeattimes) VALUES(@name, @founder, @successor, @descr, @time_registered, @last_used, @last_topic_text, @last_topic_setter, @last_topic_time, @flags, @bantype, @memomax, @botnick, @botflags, @capsmin, @capspercent, @floodlines, @floodsecs, @repeattimes) ON DUPLICATE KEY UPDATE founder=VALUES(founder), successor=VALUES(successor), descr=VALUES(descr), time_registered=VALUES(time_registered), last_used=VALUES(last_used), last_topic=VALUES(last_topic), last_topic_setter=VALUES(last_topic_setter), last_topic_time=VALUES(last_topic_time), flags=VALUES(flags), bantype=VALUES(bantype), memomax=VALUES(memomax), botnick=VALUES(botnick), botflags=VALUES(botflags), capsmin=VALUES(capsmin), capspercent=VALUES(capspercent), floodlines=VALUES(floodlines), floodsecs=VALUES(floodsecs), repeattimes=VALUES(repeattimes)"); + query.setValue("name", ci->name); + query.setValue("founder", ci->GetFounder() ? ci->GetFounder()->display : ""); + query.setValue("successor", ci->successor ? ci->successor->display : ""); + query.setValue("descr", ci->desc); + query.setValue("time_registered", ci->time_registered); + query.setValue("last_used", ci->last_used); + query.setValue("last_topic_text", ci->last_topic); + query.setValue("last_topic_setter", ci->last_topic_setter); + query.setValue("last_topic_time", ci->last_topic_time); + query.setValue("flags", ToString(ci->ToString())); + query.setValue("bantype", ci->bantype); + query.setValue("memomax", ci->memos.memomax); + query.setValue("botnick", ci->bi ? ci->bi->nick : ""); + query.setValue("botflags", ToString(ci->botflags.ToString())); + query.setValue("capsmin", ci->capsmin); + query.setValue("capspercent", ci->capspercent); + query.setValue("floodlines", ci->floodlines); + query.setValue("floodsecs", ci->floodsecs); + query.setValue("repeattimes", ci->repeattimes); + this->RunQuery(query); + + query = "DELETE from `anope_cs_mlock` WHERE `channel` = @name"; + query.setValue("name", ci->name); + this->RunQuery(query); + for (std::multimap::const_iterator it = ci->GetMLock().begin(), it_end = ci->GetMLock().end(); it != it_end; ++it) + { + const ModeLock &ml = it->second; + ChannelMode *cm = ModeManager::FindChannelModeByName(ml.name); + + if (cm != NULL) + { + query = "INSERT INTO `anope_cs_mlock` (channel, mode, status, setter, created, param) VALUES(@channel, @mode, @status, @setter, @created, @param)"; + query.setValue("channel", ci->name); + query.setValue("mode", cm->NameAsString()); + query.setValue("status", ml.set ? 1 : 0); + query.setValue("setter", ml.setter); + query.setValue("created", ml.created); + query.setValue("param", ml.param); + this->RunQuery(query); + } + } + } + + void OnChanSuspend(ChannelInfo *ci) + { + SQLQuery query("UPDATE `anope_cs_info` SET `flags` = @flags WHERE `name` = @name"); + query.setValue("flags", ToString(ci->ToString())); + query.setValue("name", ci->name); + this->RunQuery(query); + } + + void OnAkickAdd(ChannelInfo *ci, AutoKick *ak) + { + SQLQuery query("INSERT INTO `anope_cs_akick` (channel, flags, mask, reason, creator, created, last_used) VALUES(@channel, @flags, @mask, @reason, @creator, @created, @last_used)"); + query.setValue("channel", ci->name); + query.setValue("flags", ak->HasFlag(AK_ISNICK) ? "ISNICK" : ""); + query.setValue("mask", ak->HasFlag(AK_ISNICK) ? ak->nc->display : ak->mask); + query.setValue("reason", ak->reason); + query.setValue("creator", ak->creator); + query.setValue("created", ak->addtime); + query.setValue("last_used", ak->last_used); + this->RunQuery(query); + } + + void OnAkickDel(ChannelInfo *ci, AutoKick *ak) + { + SQLQuery query("DELETE FROM `anope_cs_akick` WHERE `channel`= @mask AND `mask` = @mask"); + query.setValue("channel", ci->name); + query.setValue("mask", ak->HasFlag(AK_ISNICK) ? ak->nc->display : ak->mask); + this->RunQuery(query); + } + + EventReturn OnMLock(ChannelInfo *ci, ModeLock *lock) + { + ChannelMode *cm = ModeManager::FindChannelModeByName(lock->name); + if (cm != NULL) + { + SQLQuery query("INSERT INTO `anope_cs_mlock` (channel, mode, status, setter, created, param) VALUES(@channel, @mode, @status, @setter, @created, @param)"); + query.setValue("channel", ci->name); + query.setValue("mode", cm->NameAsString()); + query.setValue("status", lock->set ? 1 : 0); + query.setValue("setter", lock->setter); + query.setValue("created", lock->created); + query.setValue("param", lock->param); + this->RunQuery(query); + } + return EVENT_CONTINUE; + } + + EventReturn OnUnMLock(ChannelInfo *ci, ChannelMode *mode, const Anope::string ¶m) + { + ChannelMode *cm = ModeManager::FindChannelModeByName(mode->Name); + if (cm != NULL) + { + SQLQuery query("DELETE FROM `anope_cs_mlock` WHERE `channel` = @channel AND `mode` = @mode AND `param` = @param"); + query.setValue("channel", ci->name); + query.setValue("mode", mode->NameAsString()); + query.setValue("param", param); + this->RunQuery(query); + } + return EVENT_CONTINUE; + } + + void OnBotCreate(BotInfo *bi) + { + SQLQuery query("INSERT INTO `anope_bs_core` (nick, user, host, rname, flags, created, chancount) VALUES(@nick, @user, @host, @rname, @flags, @created, @chancuont) ON DUPLICATE KEY UPDATE nick=VALUES(nick), user=VALUES(user), host=VALUES(host), rname=VALUES(rname), flags=VALUES(flags), created=VALUES(created), chancount=VALUES(chancount)"); + query.setValue("nick", bi->nick); + query.setValue("user", bi->GetIdent()); + query.setValue("host", bi->host); + query.setValue("rname", bi->realname); + query.setValue("flags", ToString(bi->ToString())); + query.setValue("created", bi->created); + query.setValue("chancount", bi->chancount); + this->RunQuery(query); + } + + void OnBotChange(BotInfo *bi) + { + OnBotCreate(bi); + } + + void OnBotDelete(BotInfo *bi) + { + SQLQuery query("UPDATE `anope_cs_info` SET `botnick` = '' WHERE `botnick` = @botnick"); + query.setValue("botnick", bi->nick); + this->RunQuery(query); + } + + EventReturn OnBotAssign(User *sender, ChannelInfo *ci, BotInfo *bi) + { + SQLQuery query("UPDATE `anope_cs_info` SET `botnick` = @botnick WHERE `name` = @channel"); + query.setValue("botnick", bi->nick); + query.setValue("channel", ci->name); + this->RunQuery(query); + return EVENT_CONTINUE; + } + + EventReturn OnBotUnAssign(User *sender, ChannelInfo *ci) + { + SQLQuery query("UPDATE `anope_cs_info` SET `botnick` = '' WHERE `name` = @channel"); + query.setValue("channel", ci->name); + this->RunQuery(query); + return EVENT_CONTINUE; + } + + void OnBadWordAdd(ChannelInfo *ci, BadWord *bw) + { + SQLQuery query("INSERT INTO `anope_bs_badwords` (channel, word, type) VALUES(@channel, @word, @type) ON DUPLICATE KEY UPDATE channel=VALUES(channel), word=VALUES(word), type=VALUES(type)"); + query.setValue("channel", ci->name); + query.setValue("word", bw->word); + switch (bw->type) + { + case BW_SINGLE: + query.setValue("type", "SINGLE"); + break; + case BW_START: + query.setValue("type", "START"); + break; + case BW_END: + query.setValue("type", "END"); + break; + default: + query.setValue("type", "ANY"); + } + this->RunQuery(query); + } + + void OnBadWordDel(ChannelInfo *ci, BadWord *bw) + { + SQLQuery query("DELETE FROM `anope_bs_badwords` WHERE `channel` = @channel AND `word` = @word AND `type` = @type"); + query.setValue("channel", ci->name); + query.setValue("word", bw->word); + switch (bw->type) + { + case BW_SINGLE: + query.setValue("type", "SINGLE"); + break; + case BW_START: + query.setValue("type", "START"); + break; + case BW_END: + query.setValue("type", "END"); + break; + default: + query.setValue("type", "ANY"); + } + this->RunQuery(query); + } + + void OnMemoSend(const Anope::string &source, const Anope::string &target, MemoInfo *mi, Memo *m) + { + const Anope::string &mtype = (!target.empty() && target[0] == '#' ? "CHAN" : "NICK"); + SQLQuery query("INSERT INTO `anope_ms_info` (receiver, flags, time, sender, text, serv) VALUES(@receiver, @flags, @time, @sender, @text, @serv)"); + query.setValue("receiver", target); + query.setValue("flags", ToString(m->ToString())); + query.setValue("time", m->time); + query.setValue("sender", source); + query.setValue("text", m->text); + query.setValue("serv", mtype); + this->RunQuery(query); + } + + void OnMemoDel(const NickCore *nc, MemoInfo *mi, Memo *m) + { + SQLQuery query; + + if (m) + { + query = "DELETE FROM `anope_ms_info` WHERE `receiver` = @receiver AND `time` = @time"; + query.setValue("receiver", nc->display); + query.setValue("time", m->time); + } + else + { + query = "DELETE FROM `anope_ms_info` WHERE `receiver` = @receiver"; + query.setValue("receiver", nc->display); + } + + this->RunQuery(query); + } + + void OnMemoDel(ChannelInfo *ci, MemoInfo *mi, Memo *m) + { + SQLQuery query; + + if (m) + { + query = "DELETE FROM `anope_ms_info` WHERE `receiver` = @receiver AND `time` = @time"; + query.setValue("receiver", ci->name); + query.setValue("time", m->time); + } + else + { + query = "DELETE FROM `anope_ms_info` WHERE `receiver` = @receiver"; + query.setValue("receiver", ci->name); + } + + this->RunQuery(query); + } + + EventReturn OnExceptionAdd(Exception *ex) + { + SQLQuery query("INSERT INTO `anope_os_exceptions` (mask, slimit, who, reason, time, expires) VALUES(@mask, @slimit, @who, @reason, @time, @expires)"); + query.setValue("mask", ex->mask); + query.setValue("slimit", ex->limit); + query.setValue("who", ex->who); + query.setValue("reason", ex->reason); + query.setValue("time", ex->time); + query.setValue("expires", ex->expires); + return EVENT_CONTINUE; + } + + void OnExceptionDel(User *, Exception *ex) + { + SQLQuery query("DELETE FROM `anope_os_exceptions` WHERE `mask` = @mask"); + query.setValue("mask", ex->mask); + this->RunQuery(query); + } + + EventReturn OnAddXLine(XLine *x, XLineManager *xlm) + { + SQLQuery query("INSERT INTO `anope_os_xlines` (type, mask, xby, reason, seton, expire) VALUES(@type, @mask, @xby, @reason, @seton, @expire)"); + query.setValue("type", Anope::string(xlm->Type())); + query.setValue("mask", x->Mask); + query.setValue("xby", x->By); + query.setValue("reason", x->Reason); + query.setValue("seton", x->Created); + query.setValue("expire", x->Expires); + this->RunQuery(query); + return EVENT_CONTINUE; + } + + void OnDelXLine(User *, XLine *x, XLineManager *xlm) + { + SQLQuery query; + + if (x) + { + query = "DELETE FROM `anope_os_xlines` WHERE `mask` = @mask AND `type` = @type"; + query.setValue("mask", x->Mask); + query.setValue("type", Anope::string(xlm->Type())); + } + else + { + query = "DELETE FROM `anope_os_xlines` WHERE `type` = @type"; + query.setValue("type", Anope::string(xlm->Type())); + } + + this->RunQuery(query); + } + + void OnDeleteVhost(NickAlias *na) + { + SQLQuery query("DELETE FROM `anope_hs_core` WHERE `nick` = @nick"); + query.setValue("nick", na->nick); + this->RunQuery(query); + } + + void OnSetVhost(NickAlias *na) + { + SQLQuery query("INSERT INTO `anope_hs_core` (nick, vident, vhost, creator, time) VALUES(@nick, @vident, @vhost, @creator, @time)"); + query.setValue("nick", na->nick); + query.setValue("vident", na->hostinfo.GetIdent()); + query.setValue("vhost", na->hostinfo.GetHost()); + query.setValue("creator", na->hostinfo.GetCreator()); + query.setValue("time", na->hostinfo.GetTime()); + this->RunQuery(query); + } +}; + +void MySQLInterface::OnResult(const SQLResult &r) +{ + Log(LOG_DEBUG) << "MySQL successfully executed query: " << r.finished_query; +} + +void MySQLInterface::OnError(const SQLResult &r) +{ + 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(); +} + + +static void Write(const Anope::string &data) +{ + SQLQuery query("INSERT INTO `anope_extra` (data) VALUES(@data)"); + query.setValue("data", data); + me->RunQuery(data); +} + +static void WriteNickMetadata(const Anope::string &key, const Anope::string &data) +{ + if (!CurNick) + throw CoreException("WriteNickMetadata without a nick to write"); + + SQLQuery query("INSERT INTO `anope_ns_alias_metadata` (nick, name, value) VALUES(@nick, @name, @value)"); + query.setValue("nick", CurNick->nick); + query.setValue("name", key); + query.setValue("value", data); + me->RunQuery(query); +} + +static void WriteCoreMetadata(const Anope::string &key, const Anope::string &data) +{ + if (!CurCore) + throw CoreException("WritCoreMetadata without a core to write"); + + SQLQuery query("INSERT INTO `anope_ns_core_metadata` (nick, name, value) VALUES(@nick, @name, @value)"); + query.setValue("nick", CurCore->display); + query.setValue("name", key); + query.setValue("value", data); + me->RunQuery(query); +} + +static void WriteChannelMetadata(const Anope::string &key, const Anope::string &data) +{ + if (!CurChannel) + throw CoreException("WriteChannelMetadata without a channel to write"); + + SQLQuery query("INSERT INTO `anope_cs_info_metadata` (channel, name, value) VALUES(@channel, @name, @value)"); + query.setValue("channel", CurChannel->name); + query.setValue("name", key); + query.setValue("value", data); + me->RunQuery(query); +} + +static void WriteBotMetadata(const Anope::string &key, const Anope::string &data) +{ + if (!CurBot) + throw CoreException("WriteBotMetadata without a bot to write"); + + SQLQuery query("INSERT INTO `anope_bs_info_metadata` (botname, name, value) VALUES(@botname, @name, @value)"); + query.setValue("botname", CurBot->nick); + query.setValue("name", key); + query.setValue("value", data); + me->RunQuery(query); +} + +static void SaveDatabases() +{ + SQLQuery query; + + query = "TRUNCATE TABLE `anope_ns_core`"; + me->RunQuery(query); + + query = "TRUNCATE TABLE `anope_ms_info`"; + me->RunQuery(query); + + query = "TRUNCATE TABLE `anope_ns_alias`"; + me->RunQuery(query); + + for (nickcore_map::const_iterator nit = NickCoreList.begin(), nit_end = NickCoreList.end(); nit != nit_end; ++nit) + { + NickCore *nc = nit->second; + + me->InsertCore(nc); + + for (std::list::iterator it = nc->aliases.begin(), it_end = nc->aliases.end(); it != it_end; ++it) + { + me->InsertAlias(*it); + if ((*it)->hostinfo.HasVhost()) + me->OnSetVhost(*it); + } + + for (std::vector::iterator it = nc->access.begin(), it_end = nc->access.end(); it != it_end; ++it) + { + query = "INSERT INTO `anope_ns_access` (display, access) VALUES(@display, @access)"; + query.setValue("display", nc->display); + query.setValue("access", *it); + me->RunQuery(query); + } + + for (unsigned j = 0, end = nc->memos.memos.size(); j < end; ++j) + { + Memo *m = nc->memos.memos[j]; + + me->OnMemoSend(m->sender, nc->display, &nc->memos, m); + } + } + + query = "TRUNCATE TABLE `anope_bs_core`"; + me->RunQuery(query); + + for (Anope::insensitive_map::const_iterator it = BotListByNick.begin(), it_end = BotListByNick.end(); it != it_end; ++it) + me->OnBotCreate(it->second); + + query = "TRUNCATE TABLE `anope_cs_info`"; + me->RunQuery(query); + + query = "TRUNCATE TABLE `anope_bs_badwords`"; + me->RunQuery(query); + + query = "TRUNCATE TABLE `anope_cs_access`"; + me->RunQuery(query); + + query = "TRUNCATE TABLE `anope_cs_akick`"; + me->RunQuery(query); + + query = "TRUNCATE TABLE `anope_cs_levels`"; + me->RunQuery(query); + + for (registered_channel_map::const_iterator it = RegisteredChannelList.begin(), it_end = RegisteredChannelList.end(); it != it_end; ++it) + { + ChannelInfo *ci = it->second; + + me->OnChanRegistered(ci); + + for (unsigned j = 0, end = ci->GetBadWordCount(); j < end; ++j) + { + BadWord *bw = ci->GetBadWord(j); + + me->OnBadWordAdd(ci, bw); + } + + for (unsigned j = 0, end = ci->GetAccessCount(); j < end; ++j) + { + ChanAccess *access = ci->GetAccess(j); + + me->OnAccessAdd(ci, NULL, access); + } + + for (unsigned j = 0, end = ci->GetAkickCount(); j < end; ++j) + { + AutoKick *ak = ci->GetAkick(j); + + me->OnAkickAdd(ci, ak); + } + + for (int k = 0; k < CA_SIZE; ++k) + me->OnLevelChange(NULL, ci, -1, -1); + + for (unsigned j = 0, end = ci->memos.memos.size(); j < end; ++j) + { + Memo *m = ci->memos.memos[j]; + + me->OnMemoSend(m->sender, ci->name, &ci->memos, m); + } + } + + for (std::list::iterator it = XLineManager::XLineManagers.begin(), it_end = XLineManager::XLineManagers.end(); it != it_end; ++it) + for (unsigned i = 0, end = (*it)->GetCount(); i < end; ++i) + me->OnAddXLine((*it)->GetEntry(i), *it); + + if (me->SessionInterface) + for (SessionService::ExceptionVector::iterator it = me->SessionInterface->GetExceptions().begin(); it != me->SessionInterface->GetExceptions().end(); ++it) + me->OnExceptionAdd(*it); +} + +void CommandSQLSync::Execute(CommandSource &source, const std::vector ¶ms) +{ + SaveDatabases(); + source.Reply(_("Updating MySQL.")); + return; +} + +MODULE_INIT(DBMySQL) + diff --git a/modules/database/db_mysql_live.cpp b/modules/database/db_mysql_live.cpp new file mode 100644 index 000000000..ecfdc70d1 --- /dev/null +++ b/modules/database/db_mysql_live.cpp @@ -0,0 +1,287 @@ +#include "module.h" +#include "../extra/async_commands.h" +#include "../extra/sql.h" + +class SQLCache : public Timer +{ + typedef std::map > cache_map; + cache_map cache; + public: + + SQLCache() : Timer(300, Anope::CurTime, true) { } + + bool Check(const Anope::string &item) + { + cache_map::iterator it = this->cache.find(item); + if (it != this->cache.end() && Anope::CurTime - it->second < 5) + return true; + + this->cache[item] = Anope::CurTime; + return false; + } + + void Tick(time_t) + { + for (cache_map::iterator it = this->cache.begin(), next_it; it != this->cache.end(); it = next_it) + { + next_it = it; + ++next_it; + + if (Anope::CurTime - it->second > 5) + this->cache.erase(it); + } + } +}; + +static void ChanInfoUpdate(const SQLResult &res) +{ + try + { + ChannelInfo *ci = cs_findchan(res.Get(0, "name")); + if (!ci) + ci = new ChannelInfo(res.Get(0, "name")); + ci->SetFounder(findcore(res.Get(0, "founder"))); + ci->successor = findcore(res.Get(0, "successor")); + ci->desc = res.Get(0, "descr"); + ci->time_registered = convertTo(res.Get(0, "time_registered")); + ci->ClearFlags(); + ci->FromString(BuildStringVector(res.Get(0, "flags"))); + ci->bantype = convertTo(res.Get(0, "bantype")); + ci->memos.memomax = convertTo(res.Get(0, "memomax")); + + if (res.Get(0, "botnick").equals_cs(ci->bi ? ci->bi->nick : "") == false) + { + if (ci->bi) + ci->bi->UnAssign(NULL, ci); + BotInfo *bi = findbot(res.Get(0, "botnick")); + if (bi) + bi->Assign(NULL, ci); + } + + ci->capsmin = convertTo(res.Get(0, "capsmin")); + ci->capspercent = convertTo(res.Get(0, "capspercent")); + ci->floodlines = convertTo(res.Get(0, "floodlines")); + ci->floodsecs = convertTo(res.Get(0, "floodsecs")); + ci->repeattimes = convertTo(res.Get(0, "repeattimes")); + + if (ci->c) + check_modes(ci->c); + } + catch (const SQLException &ex) + { + Log(LOG_DEBUG) << ex.GetReason(); + } + catch (const ConvertException &) { } +} + +static void NickInfoUpdate(const SQLResult &res) +{ + try + { + NickCore *nc = findcore(res.Get(0, "display")); + if (!nc) + return; + NickAlias *na = findnick(res.Get(0, "nick")); + if (!na) + na = new NickAlias(res.Get(0, "nick"), nc); + + na->last_quit = res.Get(0, "last_quit"); + na->last_realname = res.Get(0, "last_realname"); + na->last_usermask = res.Get(0, "last_usermask"); + na->time_registered = convertTo(res.Get(0, "time_registered")); + na->last_seen = convertTo(res.Get(0, "last_seen")); + na->ClearFlags(); + na->FromString(BuildStringVector(res.Get(0, "flags"))); + + if (na->nc != nc) + { + std::list::iterator it = std::find(na->nc->aliases.begin(), na->nc->aliases.end(), na); + if (it != na->nc->aliases.end()) + na->nc->aliases.erase(it); + + na->nc = nc; + na->nc->aliases.push_back(na); + } + } + catch (const SQLException &ex) + { + Log(LOG_DEBUG) << ex.GetReason(); + } + catch (const ConvertException &) { } +} + +static void NickCoreUpdate(const SQLResult &res) +{ + try + { + NickCore *nc = findcore(res.Get(0, "display")); + if (!nc) + nc = new NickCore(res.Get(0, "display")); + + nc->pass = res.Get(0, "pass"); + nc->email = res.Get(0, "email"); + nc->greet = res.Get(0, "greet"); + nc->ClearFlags(); + nc->FromString(BuildStringVector(res.Get(0, "flags"))); + nc->language = res.Get(0, "language"); + } + catch (const SQLException &ex) + { + Log(LOG_DEBUG) << ex.GetReason(); + } + catch (const ConvertException &) { } +} + +class MySQLLiveModule : public Module +{ + service_reference SQL; + service_reference ACS; + + SQLCache chan_cache, nick_cache, core_cache; + + SQLResult RunQuery(const SQLQuery &query) + { + if (!this->SQL) + throw SQLException("Unable to locate SQL reference, is m_mysql loaded and configured correctly?"); + + SQLResult res = SQL->RunQuery(query); + if (!res.GetError().empty()) + throw SQLException(res.GetError()); + return res; + } + + CommandMutex *CurrentCommand() + { + if (this->ACS) + return this->ACS->CurrentCommand(); + return NULL; + } + + public: + MySQLLiveModule(const Anope::string &modname, const Anope::string &creator) : + Module(modname, creator, DATABASE), SQL("mysql/main"), ACS("asynch_commands") + { + Implementation i[] = { I_OnFindChan, I_OnFindNick, I_OnFindCore, I_OnShutdown }; + ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); + } + + void OnShutdown() + { + Implementation i[] = { I_OnFindChan, I_OnFindNick, I_OnFindCore }; + for (size_t j = 0; j < 3; ++j) + ModuleManager::Detach(i[j], this); + } + + void OnFindChan(const Anope::string &chname) + { + if (chan_cache.Check(chname)) + return; + + try + { + SQLQuery query("SELECT * FROM `anope_cs_info` WHERE `name` = @name"); + query.setValue("name", chname); + CommandMutex *current_command = this->CurrentCommand(); + if (current_command) + { + current_command->Unlock(); + try + { + SQLResult res = this->RunQuery(query); + current_command->Lock(); + ChanInfoUpdate(res); + } + catch (const SQLException &) + { + current_command->Lock(); + throw; + } + } + else + { + SQLResult res = this->RunQuery(query); + ChanInfoUpdate(res); + } + } + catch (const SQLException &ex) + { + Log(LOG_DEBUG) << "OnFindChan: " << ex.GetReason(); + } + } + + void OnFindNick(const Anope::string &nick) + { + if (nick_cache.Check(nick)) + return; + + try + { + SQLQuery query("SELECT * FROM `anope_ns_alias` WHERE `nick` = @nick"); + query.setValue("nick", nick); + CommandMutex *current_command = this->CurrentCommand(); + if (current_command) + { + current_command->Unlock(); + try + { + SQLResult res = this->RunQuery(query); + current_command->Lock(); + NickInfoUpdate(res); + } + catch (const SQLException &) + { + current_command->Lock(); + throw; + } + } + else + { + SQLResult res = this->RunQuery(query); + NickInfoUpdate(res); + } + } + catch (const SQLException &ex) + { + Log(LOG_DEBUG) << "OnFindNick: " << ex.GetReason(); + } + } + + void OnFindCore(const Anope::string &nick) + { + if (core_cache.Check(nick)) + return; + + try + { + SQLQuery query("SELECT * FROM `anope_ns_core` WHERE `display` = @display"); + query.setValue("display", nick); + CommandMutex *current_command = this->CurrentCommand(); + if (current_command) + { + current_command->Unlock(); + try + { + SQLResult res = this->RunQuery(query); + current_command->Lock(); + NickCoreUpdate(res); + } + catch (const SQLException &) + { + current_command->Lock(); + throw; + } + } + else + { + SQLResult res = this->RunQuery(query); + NickCoreUpdate(res); + } + } + catch (const SQLException &ex) + { + Log(LOG_DEBUG) << "OnFindCore: " << ex.GetReason(); + } + } +}; + +MODULE_INIT(MySQLLiveModule) diff --git a/modules/database/db_plain.cpp b/modules/database/db_plain.cpp new file mode 100644 index 000000000..e2ade7974 --- /dev/null +++ b/modules/database/db_plain.cpp @@ -0,0 +1,908 @@ +/* + * (C) 2003-2011 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 "../commands/os_session.h" + +Anope::string DatabaseFile; +std::stringstream db_buffer; +service_reference SessionInterface("session"); + +/** Enum used for what METADATA type we are reading + */ +enum MDType +{ + MD_NONE, + MD_NC, + MD_NA, + MD_BI, + MD_CH +}; + +/* Character for newlines, in my research I have concluded it is significantly faster + * to use \n instead of std::endl because std::endl also flushes the buffer, which + * is not necessary here + */ +static const char endl = '\n'; + +/** Read from the database and call the events + * @param m If specified it only calls the read events for the specific module + */ +static void ReadDatabase(Module *m = NULL) +{ + EventReturn MOD_RESULT; + MDType Type = MD_NONE; + + std::fstream db; + db.open(DatabaseFile.c_str(), std::ios_base::in); + + if (!db.is_open()) + { + Log() << "Unable to open " << DatabaseFile << " for reading!"; + return; + } + + NickCore *nc = NULL; + NickAlias *na = NULL; + BotInfo *bi = NULL; + ChannelInfo *ci = NULL; + + Anope::string buf; + while (std::getline(db, buf.str())) + { + if (buf.empty()) + continue; + + spacesepstream sep(buf); + std::vector params; + while (sep.GetToken(buf)) + { + if (buf[0] == ':') + { + buf.erase(buf.begin()); + if (!buf.empty() && !sep.StreamEnd()) + params.push_back(buf + " " + sep.GetRemaining()); + else if (!sep.StreamEnd()) + params.push_back(sep.GetRemaining()); + else if (!buf.empty()) + params.push_back(buf); + break; + } + else + params.push_back(buf); + } + + if (m) + MOD_RESULT = m->OnDatabaseRead(params); + else + FOREACH_RESULT(I_OnDatabaseRead, OnDatabaseRead(params)); + if (MOD_RESULT == EVENT_STOP) + continue; + + if (!params.empty()) + { + if (params[0].equals_ci("NC")) + { + nc = findcore(params[1]); + Type = MD_NC; + } + else if (params[0].equals_ci("NA")) + { + na = findnick(params[2]); + Type = MD_NA; + } + else if (params[0].equals_ci("BI")) + { + bi = findbot(params[1]); + Type = MD_BI; + } + else if (params[0].equals_ci("CH")) + { + ci = cs_findchan(params[1]); + Type = MD_CH; + } + else if (params[0].equals_ci("MD")) + { + Anope::string key = params[1]; + params.erase(params.begin()); + params.erase(params.begin()); + + if (Type == MD_NC && nc) + { + try + { + if (m) + m->OnDatabaseReadMetadata(nc, key, params); + else + FOREACH_RESULT(I_OnDatabaseReadMetadata, OnDatabaseReadMetadata(nc, key, params)); + } + catch (const DatabaseException &ex) + { + Log() << "[db_plain]: " << ex.GetReason(); + } + } + else if (Type == MD_NA && na) + { + try + { + if (m) + m->OnDatabaseReadMetadata(na, key, params); + else + FOREACH_RESULT(I_OnDatabaseReadMetadata, OnDatabaseReadMetadata(na, key, params)); + } + catch (const DatabaseException &ex) + { + Log() << "[db_plain]: " << ex.GetReason(); + } + } + else if (Type == MD_BI && bi) + { + try + { + if (m) + m->OnDatabaseReadMetadata(bi, key, params); + else + FOREACH_RESULT(I_OnDatabaseReadMetadata, OnDatabaseReadMetadata(bi, key, params)); + } + catch (const DatabaseException &ex) + { + Log() << "[db_plain]: " << ex.GetReason(); + } + } + else if (Type == MD_CH && ci) + { + try + { + if (m) + m->OnDatabaseReadMetadata(ci, key, params); + else + FOREACH_RESULT(I_OnDatabaseReadMetadata, OnDatabaseReadMetadata(ci, key, params)); + } + catch (const DatabaseException &ex) + { + Log() << "[db_plain]: " << ex.GetReason(); + } + } + } + } + } + + db.close(); +} + +struct ChannelLevel +{ + ChannelAccess Level; + Anope::string Name; +}; + +ChannelLevel ChannelLevels[] = { + { CA_ACCESS_LIST, "ACCESS_LIST" }, + { CA_NOKICK, "NOKICK" }, + { CA_FANTASIA, "FANTASIA" }, + { CA_GREET, "GREET" }, + { CA_AUTOVOICE, "AUTOVOICE" }, + { CA_VOICEME, "VOICEME" }, + { CA_VOICE, "VOICE" }, + { CA_INFO, "INFO" }, + { CA_SAY, "SAY" }, + { CA_AUTOHALFOP, "AUTOHALFOP" }, + { CA_HALFOPME, "HALFOPME" }, + { CA_HALFOP, "HALFOP" }, + { CA_KICK, "KICK" }, + { CA_SIGNKICK, "SIGNKICK" }, + { CA_BAN, "BAN" }, + { CA_TOPIC, "TOPIC" }, + { CA_MODE, "MODE" }, + { CA_GETKEY, "GETKEY" }, + { CA_INVITE, "INVITE" }, + { CA_UNBAN, "UNBAN" }, + { CA_AUTOOP, "AUTOOP" }, + { CA_OPDEOPME, "OPDEOPME" }, + { CA_OPDEOP, "OPDEOP" }, + { CA_AUTOPROTECT, "AUTOPROTECT" }, + { CA_AKICK, "AKICK" }, + { CA_BADWORDS, "BADWORDS" }, + { CA_ASSIGN, "ASSIGN" }, + { CA_MEMO, "MEMO" }, + { CA_ACCESS_CHANGE, "ACCESS_CHANGE" }, + { CA_PROTECTME, "PROTECTME" }, + { CA_PROTECT, "PROTECT" }, + { CA_SET, "SET" }, + { CA_AUTOOWNER, "AUTOPROTECT" }, + { CA_OWNERME, "OWNERME" }, + { CA_OWNER, "OWNER" }, + { CA_FOUNDER, "FOUNDER" }, + { CA_SIZE, "" } +}; + +static Anope::string ToString(const std::vector &strings) +{ + Anope::string ret; + + for (unsigned i = 0; i < strings.size(); ++i) + ret += " " + strings[i]; + + if (!ret.empty()) + ret.erase(ret.begin()); + + return ret; +} + +static void LoadNickCore(const std::vector ¶ms) +{ + NickCore *nc = new NickCore(params[0]); + /* Clear default flags */ + nc->ClearFlags(); + + nc->pass = params.size() > 1 ? params[1] : ""; + + Log(LOG_DEBUG_2) << "[db_plain]: Loaded NickCore " << nc->display; +} + +static void LoadNickAlias(const std::vector ¶ms) +{ + NickCore *nc = findcore(params[0]); + if (!nc) + { + Log() << "[db_plain]: Unable to find core " << params[0]; + return; + } + + NickAlias *na = new NickAlias(params[1], nc); + + na->time_registered = params[2].is_pos_number_only() ? convertTo(params[2]) : 0; + + na->last_seen = params[3].is_pos_number_only() ? convertTo(params[3]) : 0; + + Log(LOG_DEBUG_2) << "[db_plain}: Loaded nickalias for " << na->nick; +} + +static void LoadBotInfo(const std::vector ¶ms) +{ + BotInfo *bi = findbot(params[0]); + if (!bi) + bi = new BotInfo(params[0], params[1], params[2]); + + bi->created = params[3].is_pos_number_only() ? convertTo(params[3]) : 0; + bi->chancount = params[4].is_pos_number_only() ? convertTo(params[4]) : 0; + bi->realname = params[5]; + + Log(LOG_DEBUG_2) << "[db_plain]: Loaded botinfo for " << bi->nick; +} + +static void LoadChanInfo(const std::vector ¶ms) +{ + ChannelInfo *ci = new ChannelInfo(params[0]); + /* CLear default mlock */ + ci->ClearMLock(); + /* Remove default channel flags */ + ci->ClearFlags(); + ci->botflags.ClearFlags(); + + ci->time_registered = params[1].is_pos_number_only() ? convertTo(params[1]) : 0; + + ci->last_used = params[2].is_pos_number_only() ? convertTo(params[2]) : 0; + + Log(LOG_DEBUG_2) << "[db_plain]: loaded channel " << ci->name; +} + +static void LoadOperInfo(const std::vector ¶ms) +{ + if (params[0].equals_ci("STATS")) + { + maxusercnt = params[1].is_pos_number_only() ? convertTo(params[1]) : 0; + maxusertime = params[2].is_pos_number_only() ? convertTo(params[2]) : 0; + } + else if (params[0].equals_ci("SXLINE")) + { + char type = params[1][0]; + for (std::list::iterator it = XLineManager::XLineManagers.begin(), it_end = XLineManager::XLineManagers.end(); it != it_end; ++it) + { + XLineManager *xl = *it; + if (xl->Type() == type) + { + Anope::string mask = params[2]; + Anope::string by = params[3]; + time_t seton = params[4].is_pos_number_only() ? convertTo(params[4]) : 0; + time_t expires = params[5].is_pos_number_only() ? convertTo(params[5]) : 0; + Anope::string reason = params[6]; + + XLine *x = xl->Add(mask, by, expires, reason); + if (x) + x->Created = seton; + break; + } + } + } + else if (params[0].equals_ci("EXCEPTION") && SessionInterface) + { + Exception *exception = new Exception(); + exception->mask = params[1]; + exception->limit = params[2].is_pos_number_only() ? convertTo(params[2]) : 1; + exception->who = params[3]; + exception->time = params[4].is_pos_number_only() ? convertTo(params[4]) : 0; + exception->expires = params[5].is_pos_number_only() ? convertTo(params[5]) : 0; + exception->reason = params[6]; + SessionInterface->AddException(exception); + } +} + +void Write(const Anope::string &buf) +{ + db_buffer << buf << endl; +} + +void WriteMetadata(const Anope::string &key, const Anope::string &data) +{ + Write("MD " + key + " " + data); +} + +class DBPlain : public Module +{ + /* Day the last backup was on */ + int LastDay; + /* Backup file names */ + std::list Backups; + public: + DBPlain(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, DATABASE) + { + this->SetAuthor("Anope"); + + Implementation i[] = { I_OnReload, I_OnDatabaseRead, I_OnLoadDatabase, I_OnDatabaseReadMetadata, I_OnSaveDatabase, I_OnModuleLoad }; + ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); + + OnReload(); + + LastDay = 0; + } + + ~DBPlain() + { + } + + void BackupDatabase() + { + /* Do not backup a database that doesn't exist */ + if (!IsFile(DatabaseFile)) + return; + + time_t now = Anope::CurTime; + tm *tm = localtime(&now); + + if (tm->tm_mday != LastDay) + { + LastDay = tm->tm_mday; + Anope::string newname = "backups/" + DatabaseFile + "." + stringify(tm->tm_year) + stringify(tm->tm_mon) + stringify(tm->tm_mday); + + /* Backup already exists */ + if (IsFile(newname)) + return; + + Log(LOG_DEBUG) << "db_plain: Attemping to rename " << DatabaseFile << " to " << newname; + if (rename(DatabaseFile.c_str(), newname.c_str())) + { + Log() << "Unable to back up database!"; + + if (!Config->NoBackupOkay) + quitting = true; + + return; + } + + Backups.push_back(newname); + + unsigned KeepBackups = Config->KeepBackups; + if (KeepBackups && Backups.size() > KeepBackups) + { + DeleteFile(Backups.front().c_str()); + Backups.pop_front(); + } + } + } + + void OnReload() + { + ConfigReader config; + DatabaseFile = config.ReadValue("db_plain", "database", "anope.db", 0); + } + + EventReturn OnDatabaseRead(const std::vector ¶ms) + { + Anope::string key = params[0]; + std::vector otherparams = params; + otherparams.erase(otherparams.begin()); + + if (key.equals_ci("NC")) + LoadNickCore(otherparams); + else if (key.equals_ci("NA")) + LoadNickAlias(otherparams); + else if (key.equals_ci("BI")) + LoadBotInfo(otherparams); + else if (key.equals_ci("CH")) + LoadChanInfo(otherparams); + else if (key.equals_ci("OS")) + LoadOperInfo(otherparams); + + return EVENT_CONTINUE; + } + + EventReturn OnLoadDatabase() + { + ReadDatabase(); + + /* No need to ever reload this again, although this should never be trigged again */ + ModuleManager::Detach(I_OnLoadDatabase, this); + ModuleManager::Detach(I_OnDatabaseReadMetadata, this); + + return EVENT_STOP; + } + + EventReturn OnDatabaseReadMetadata(NickCore *nc, const Anope::string &key, const std::vector ¶ms) + { + try + { + if (key.equals_ci("LANGUAGE")) + nc->language = params[0]; + else if (key.equals_ci("MEMOMAX")) + nc->memos.memomax = params[0].is_pos_number_only() ? convertTo(params[0]) : -1; + else if (key.equals_ci("EMAIL")) + nc->email = params[0]; + else if (key.equals_ci("GREET")) + nc->greet = params[0]; + else if (key.equals_ci("ACCESS")) + nc->AddAccess(params[0]); + else if (key.equals_ci("CERT")) + nc->AddCert(params[0]); + else if (key.equals_ci("FLAGS")) + nc->FromString(params); + else if (key.equals_ci("MI")) + { + Memo *m = new Memo; + m->time = params[0].is_pos_number_only() ? convertTo(params[0]) : 0; + m->sender = params[1]; + for (unsigned j = 2; params[j].equals_ci("UNREAD") || params[j].equals_ci("RECEIPT"); ++j) + { + if (params[j].equals_ci("UNREAD")) + m->SetFlag(MF_UNREAD); + else if (params[j].equals_ci("RECEIPT")) + m->SetFlag(MF_RECEIPT); + } + m->text = params[params.size() - 1]; + nc->memos.memos.push_back(m); + } + else if (key.equals_ci("MIG")) + nc->memos.ignores.push_back(params[0].ci_str()); + } + catch (const ConvertException &ex) + { + throw DatabaseException(ex.GetReason()); + } + + return EVENT_CONTINUE; + } + + EventReturn OnDatabaseReadMetadata(NickAlias *na, const Anope::string &key, const std::vector ¶ms) + { + if (key.equals_ci("LAST_USERMASK")) + na->last_usermask = params[0]; + else if (key.equals_ci("LAST_REALNAME")) + na->last_realname = params[0]; + else if (key.equals_ci("LAST_QUIT")) + na->last_quit = params[0]; + else if (key.equals_ci("FLAGS")) + na->FromString(params); + else if (key.equals_ci("VHOST")) + na->hostinfo.SetVhost(params.size() > 3 ? params[3] : "", params[2], params[0], params[1].is_pos_number_only() ? convertTo(params[1]) : 0); + + return EVENT_CONTINUE; + } + + EventReturn OnDatabaseReadMetadata(BotInfo *bi, const Anope::string &key, const std::vector ¶ms) + { + if (key.equals_ci("FLAGS")) + bi->FromString(params); + + return EVENT_CONTINUE; + } + + EventReturn OnDatabaseReadMetadata(ChannelInfo *ci, const Anope::string &key, const std::vector ¶ms) + { + try + { + if (key.equals_ci("BANTYPE")) + ci->bantype = params[0].is_pos_number_only() ? convertTo(params[0]) : Config->CSDefBantype; + else if (key.equals_ci("MEMOMAX")) + ci->memos.memomax = params[0].is_pos_number_only() ? convertTo(params[0]) : -1; + else if (key.equals_ci("FOUNDER")) + ci->SetFounder(findcore(params[0])); + else if (key.equals_ci("SUCCESSOR")) + ci->successor = findcore(params[0]); + else if (key.equals_ci("LEVELS")) + { + for (unsigned j = 0, end = params.size(); j < end; j += 2) + for (int i = 0; ChannelLevels[i].Level != CA_SIZE; ++i) + if (params[j].equals_ci(ChannelLevels[i].Name)) + ci->levels[ChannelLevels[i].Level] = params[j + 1].is_number_only() ? convertTo(params[j + 1]) : 0; + } + else if (key.equals_ci("FLAGS")) + ci->FromString(params); + else if (key.equals_ci("DESC")) + ci->desc = params[0]; + else if (key.equals_ci("TOPIC")) + { + ci->last_topic_setter = params[0]; + ci->last_topic_time = params[1].is_pos_number_only() ? convertTo(params[1]) : 0; + ci->last_topic = params[2]; + } + else if (key.equals_ci("SUSPEND")) + { + ci->Extend("suspend_by", new ExtensibleItemRegular(params[0])); + ci->Extend("suspend_reason", new ExtensibleItemRegular(params[1])); + } + else if (key.equals_ci("ACCESS")) + { + service_reference provider(params[0]); + if (!provider) + throw DatabaseException("Access entry for nonexistant provider " + params[0]); + + ChanAccess *access = provider->Create(); + access->ci = ci; + access->mask = params[1]; + access->Unserialize(params[2]); + access->last_seen = params[3].is_pos_number_only() ? convertTo(params[3]) : 0; + access->creator = params[4]; + access->created = params.size() > 5 && params[5].is_pos_number_only() ? convertTo(params[5]) : Anope::CurTime; + + ci->AddAccess(access); + } + else if (key.equals_ci("AKICK")) + { + // 0 is the old stuck + bool Nick = params[1].equals_ci("NICK"); + NickCore *nc = NULL; + if (Nick) + { + nc = findcore(params[2]); + if (!nc) + throw DatabaseException("Akick for nonexistant core " + params[2] + " on " + ci->name); + } + + AutoKick *ak; + if (Nick) + ak = ci->AddAkick(params[3], nc, params.size() > 6 ? params[6] : "", params[4].is_pos_number_only() ? convertTo(params[4]) : 0, params[5].is_pos_number_only() ? convertTo(params[5]) : 0); + else + ak = ci->AddAkick(params[3], params[2], params.size() > 6 ? params[6] : "", params[4].is_pos_number_only() ? convertTo(params[4]) : 0, params[5].is_pos_number_only() ? convertTo(params[5]) : 0); + if (Nick) + ak->SetFlag(AK_ISNICK); + + } + else if (key.equals_ci("MLOCK")) + { + std::vector mlocks; + ci->GetExtRegular("db_mlock", mlocks); + + Anope::string mlock_string = params[0] + " " + params[1] + " " + params[2] + " " + params[3]; + if (params.size() > 4) + mlock_string += " " + params[4]; + + mlocks.push_back(mlock_string); + ci->Extend("db_mlock", new ExtensibleItemRegular >(mlocks)); + } + else if (key.equals_ci("MI")) + { + Memo *m = new Memo; + m->time = params[0].is_pos_number_only() ? convertTo(params[0]) : 0; + m->sender = params[1]; + for (unsigned j = 2; params[j].equals_ci("UNREAD") || params[j].equals_ci("RECEIPT"); ++j) + { + if (params[j].equals_ci("UNREAD")) + m->SetFlag(MF_UNREAD); + else if (params[j].equals_ci("RECEIPT")) + m->SetFlag(MF_RECEIPT); + } + m->text = params[params.size() - 1]; + ci->memos.memos.push_back(m); + } + else if (key.equals_ci("MIG")) + ci->memos.ignores.push_back(params[0].ci_str()); + else if (key.equals_ci("BI")) + { + if (params[0].equals_ci("NAME")) + ci->bi = findbot(params[1]); + else if (params[0].equals_ci("FLAGS")) + ci->botflags.FromString(params); + else if (params[0].equals_ci("TTB")) + { + for (unsigned j = 1, end = params.size(); j < end; j += 2) + { + if (params[j].equals_ci("BOLDS")) + ci->ttb[0] = params[j + 1].is_pos_number_only() ? convertTo(params[j + 1]) : 0; + else if (params[j].equals_ci("COLORS")) + ci->ttb[1] = params[j + 1].is_pos_number_only() ? convertTo(params[j + 1]) : 0; + else if (params[j].equals_ci("REVERSES")) + ci->ttb[2] = params[j + 1].is_pos_number_only() ? convertTo(params[j + 1]) : 0; + else if (params[j].equals_ci("UNDERLINES")) + ci->ttb[3] = params[j + 1].is_pos_number_only() ? convertTo(params[j + 1]) : 0; + else if (params[j].equals_ci("BADWORDS")) + ci->ttb[4] = params[j + 1].is_pos_number_only() ? convertTo(params[j + 1]) : 0; + else if (params[j].equals_ci("CAPS")) + ci->ttb[5] = params[j + 1].is_pos_number_only() ? convertTo(params[j + 1]) : 0; + else if (params[j].equals_ci("FLOOD")) + ci->ttb[6] = params[j + 1].is_pos_number_only() ? convertTo(params[j + 1]) : 0; + else if (params[j].equals_ci("REPEAT")) + ci->ttb[7] = params[j + 1].is_pos_number_only() ? convertTo(params[j + 1]) : 0; + else if (params[j].equals_ci("ITALICS")) + ci->ttb[8] = params[j + 1].is_pos_number_only() ? convertTo(params[j + 1]) : 0; + else if (params[j].equals_ci("AMSGS")) + ci->ttb[9] = params[j + 1].is_pos_number_only() ? convertTo(params[j + 1]) : 0; + } + } + else if (params[0].equals_ci("CAPSMIN")) + ci->capsmin = params[1].is_pos_number_only() ? convertTo(params[1]) : 0; + else if (params[0].equals_ci("CAPSPERCENT")) + ci->capspercent = params[1].is_pos_number_only() ? convertTo(params[1]) : 0; + else if (params[0].equals_ci("FLOODLINES")) + ci->floodlines = params[1].is_pos_number_only() ? convertTo(params[1]) : 0; + else if (params[0].equals_ci("FLOODSECS")) + ci->floodsecs = params[1].is_pos_number_only() ? convertTo(params[1]) : 0; + else if (params[0].equals_ci("REPEATTIMES")) + ci->repeattimes = params[1].is_pos_number_only() ? convertTo(params[1]) : 0; + else if (params[0].equals_ci("BADWORD")) + { + BadWordType Type; + if (params[1].equals_ci("SINGLE")) + Type = BW_SINGLE; + else if (params[1].equals_ci("START")) + Type = BW_START; + else if (params[1].equals_ci("END")) + Type = BW_END; + else + Type = BW_ANY; + ci->AddBadWord(params[2], Type); + } + } + } + catch (const ConvertException &ex) + { + throw DatabaseException(ex.GetReason()); + } + + return EVENT_CONTINUE; + } + + EventReturn OnSaveDatabase() + { + BackupDatabase(); + + db_buffer << "VER 2" << endl; + + for (nickcore_map::const_iterator nit = NickCoreList.begin(), nit_end = NickCoreList.end(); nit != nit_end; ++nit) + { + NickCore *nc = nit->second; + + db_buffer << "NC " << nc->display << " " << nc->pass << endl; + + db_buffer << "MD MEMOMAX " << nc->memos.memomax << endl; + + if (!nc->language.empty()) + db_buffer << "MD LANGUAGE " << nc->language << endl; + if (!nc->email.empty()) + db_buffer << "MD EMAIL " << nc->email << endl; + if (!nc->greet.empty()) + db_buffer << "MD GREET :" << nc->greet << endl; + + if (!nc->access.empty()) + { + for (std::vector::iterator it = nc->access.begin(), it_end = nc->access.end(); it != it_end; ++it) + db_buffer << "MD ACCESS " << *it << endl; + } + if (!nc->cert.empty()) + { + for (std::vector::iterator it = nc->cert.begin(), it_end = nc->cert.end(); it != it_end; ++it) + db_buffer << "MD CERT " << *it << endl; + } + if (nc->FlagCount()) + db_buffer << "MD FLAGS " << ToString(nc->ToString()) << endl; + MemoInfo *mi = &nc->memos; + for (unsigned k = 0, end = mi->memos.size(); k < end; ++k) + { + db_buffer << "MD MI " << mi->memos[k]->time << " " << mi->memos[k]->sender; + if (mi->memos[k]->HasFlag(MF_UNREAD)) + db_buffer << " UNREAD"; + if (mi->memos[k]->HasFlag(MF_RECEIPT)) + db_buffer << " RECEIPT"; + db_buffer << " :" << mi->memos[k]->text << endl; + } + for (unsigned k = 0, end = mi->ignores.size(); k < end; ++k) + db_buffer << "MD MIG " << Anope::string(mi->ignores[k]) << endl; + FOREACH_MOD(I_OnDatabaseWriteMetadata, OnDatabaseWriteMetadata(WriteMetadata, nc)); + } + + for (nickalias_map::const_iterator it = NickAliasList.begin(), it_end = NickAliasList.end(); it != it_end; ++it) + { + NickAlias *na = it->second; + + db_buffer << "NA " << na->nc->display << " " << na->nick << " " << na->time_registered << " " << na->last_seen << endl; + if (!na->last_usermask.empty()) + db_buffer << "MD LAST_USERMASK " << na->last_usermask << endl; + if (!na->last_realname.empty()) + db_buffer << "MD LAST_REALNAME :" << na->last_realname << endl; + if (!na->last_quit.empty()) + db_buffer << "MD LAST_QUIT :" << na->last_quit << endl; + if (na->FlagCount()) + db_buffer << "MD FLAGS " << ToString(na->ToString()) << endl; + if (na->hostinfo.HasVhost()) + db_buffer << "MD VHOST " << na->hostinfo.GetCreator() << " " << na->hostinfo.GetTime() << " " << na->hostinfo.GetHost() << " :" << na->hostinfo.GetIdent() << endl; + + FOREACH_MOD(I_OnDatabaseWriteMetadata, OnDatabaseWriteMetadata(WriteMetadata, na)); + } + + for (Anope::insensitive_map::const_iterator it = BotListByNick.begin(), it_end = BotListByNick.end(); it != it_end; ++it) + { + BotInfo *bi = it->second; + + if (bi->HasFlag(BI_CONF)) + continue; + + db_buffer << "BI " << bi->nick << " " << bi->GetIdent() << " " << bi->host << " " << bi->created << " " << bi->chancount << " :" << bi->realname << endl; + if (bi->FlagCount()) + db_buffer << "MD FLAGS " << ToString(bi->ToString()) << endl; + } + + for (registered_channel_map::const_iterator cit = RegisteredChannelList.begin(), cit_end = RegisteredChannelList.end(); cit != cit_end; ++cit) + { + ChannelInfo *ci = cit->second; + + db_buffer << "CH " << ci->name << " " << ci->time_registered << " " << ci->last_used << endl; + db_buffer << "MD BANTYPE " << ci->bantype << endl; + db_buffer << "MD MEMOMAX " << ci->memos.memomax << endl; + if (ci->GetFounder()) + db_buffer << "MD FOUNDER " << ci->GetFounder()->display << endl; + if (ci->successor) + db_buffer << "MD SUCCESSOR " << ci->successor->display << endl; + if (!ci->desc.empty()) + db_buffer << "MD DESC :" << ci->desc << endl; + if (!ci->last_topic.empty()) + db_buffer << "MD TOPIC " << ci->last_topic_setter << " " << ci->last_topic_time << " :" << ci->last_topic << endl; + db_buffer << "MD LEVELS"; + for (int j = 0; ChannelLevels[j].Level != CA_SIZE; ++j) + db_buffer << " " << ChannelLevels[j].Name << " " << ci->levels[ChannelLevels[j].Level]; + db_buffer << endl; + if (ci->FlagCount()) + db_buffer << "MD FLAGS " << ToString(ci->ToString()) << endl; + if (ci->HasFlag(CI_SUSPENDED)) + { + Anope::string by, reason; + ci->GetExtRegular("suspend_by", by); + ci->GetExtRegular("suspend_reason", reason); + db_buffer << "MD SUSPEND " << by << " :" << reason << endl; + } + for (unsigned k = 0, end = ci->GetAccessCount(); k < end; ++k) + { + ChanAccess *access = ci->GetAccess(k); + db_buffer << "MD ACCESS " << access->provider->name << " " << access->mask << " " << access->Serialize() << " " << access->last_seen << " " << access->creator << " " << access->created << endl; + } + for (unsigned k = 0, end = ci->GetAkickCount(); k < end; ++k) + { + db_buffer << "MD AKICK 0 " << (ci->GetAkick(k)->HasFlag(AK_ISNICK) ? "NICK " : "MASK ") << + (ci->GetAkick(k)->HasFlag(AK_ISNICK) ? ci->GetAkick(k)->nc->display : ci->GetAkick(k)->mask) << " " << ci->GetAkick(k)->creator << " " << ci->GetAkick(k)->addtime << " " << ci->last_used << " :"; + if (!ci->GetAkick(k)->reason.empty()) + db_buffer << ci->GetAkick(k)->reason; + db_buffer << endl; + } + { + std::vector mlocks; + if (ci->GetExtRegular("db_mlock", mlocks)) + for (unsigned i = 0; i < mlocks.size(); ++i) + db_buffer << mlocks[i] << endl; + else + { + for (std::multimap::const_iterator it = ci->GetMLock().begin(), it_end = ci->GetMLock().end(); it != it_end; ++it) + { + const ModeLock &ml = it->second; + ChannelMode *cm = ModeManager::FindChannelModeByName(ml.name); + if (cm != NULL) + db_buffer << "MD MLOCK " << (ml.set ? 1 : 0) << " " << cm->NameAsString() << " " << ml.setter << " " << ml.created << " " << ml.param << endl; + } + } + } + MemoInfo *memos = &ci->memos; + for (unsigned k = 0, end = memos->memos.size(); k < end; ++k) + { + db_buffer << "MD MI " << memos->memos[k]->time << " " << memos->memos[k]->sender; + if (memos->memos[k]->HasFlag(MF_UNREAD)) + db_buffer << " UNREAD"; + if (memos->memos[k]->HasFlag(MF_RECEIPT)) + db_buffer << " RECEIPT"; + db_buffer << " :" << memos->memos[k]->text << endl; + } + for (unsigned k = 0, end = memos->ignores.size(); k < end; ++k) + db_buffer << "MD MIG " << Anope::string(memos->ignores[k]) << endl; + if (ci->bi) + db_buffer << "MD BI NAME " << ci->bi->nick << endl; + if (ci->botflags.FlagCount()) + db_buffer << "MD BI FLAGS " << ToString(ci->botflags.ToString()) << endl; + db_buffer << "MD BI TTB BOLDS " << ci->ttb[0] << " COLORS " << ci->ttb[1] << " REVERSES " << ci->ttb[2] << " UNDERLINES " << ci->ttb[3] << " BADWORDS " << ci->ttb[4] << " CAPS " << ci->ttb[5] << " FLOOD " << ci->ttb[6] << " REPEAT " << ci->ttb[7] << " ITALICS " << ci->ttb[8] << " AMSGS " << ci->ttb[9] << endl; + if (ci->capsmin) + db_buffer << "MD BI CAPSMIN " << ci->capsmin << endl; + if (ci->capspercent) + db_buffer << "MD BI CAPSPERCENT " << ci->capspercent << endl; + if (ci->floodlines) + db_buffer << "MD BI FLOODLINES " << ci->floodlines << endl; + if (ci->floodsecs) + db_buffer << "MD BI FLOODSECS " << ci->floodsecs << endl; + if (ci->repeattimes) + db_buffer << "MD BI REPEATTIMES " << ci->repeattimes << endl; + for (unsigned k = 0, end = ci->GetBadWordCount(); k < end; ++k) + db_buffer << "MD BI BADWORD " << (ci->GetBadWord(k)->type == BW_ANY ? "ANY " : "") << (ci->GetBadWord(k)->type == BW_SINGLE ? "SINGLE " : "") << (ci->GetBadWord(k)->type == BW_START ? "START " : "") << + (ci->GetBadWord(k)->type == BW_END ? "END " : "") << ":" << ci->GetBadWord(k)->word << endl; + + FOREACH_MOD(I_OnDatabaseWriteMetadata, OnDatabaseWriteMetadata(WriteMetadata, ci)); + } + + db_buffer << "OS STATS " << maxusercnt << " " << maxusertime << endl; + + for (std::list::iterator it = XLineManager::XLineManagers.begin(), it_end = XLineManager::XLineManagers.end(); it != it_end; ++it) + { + XLineManager *xl = *it; + for (unsigned i = 0, end = xl->GetCount(); i < end; ++i) + { + XLine *x = xl->GetEntry(i); + db_buffer << "OS SXLINE " << xl->Type() << " " << x->GetUser() << " " << x->GetHost() << " " << x->By << " " << x->Created << " " << x->Expires << " :" << x->Reason << endl; + } + } + + if (SessionInterface) + for (SessionService::ExceptionVector::iterator it = SessionInterface->GetExceptions().begin(); it != SessionInterface->GetExceptions().end(); ++it) + { + Exception *e = *it; + db_buffer << "OS EXCEPTION " << e->mask << " " << e->limit << " " << e->who << " " << e->time << " " << e->expires << " " << e->reason << endl; + } + + FOREACH_MOD(I_OnDatabaseWrite, OnDatabaseWrite(Write)); + + std::fstream db; + db.open(DatabaseFile.c_str(), std::ios_base::out | std::ios_base::trunc); + + if (!db.is_open()) + { + ircdproto->SendGlobops(NULL, "Unable to open %s for writing!", DatabaseFile.c_str()); + return EVENT_CONTINUE; + } + + db << db_buffer.str(); + db_buffer.str(""); + + db.close(); + + return EVENT_CONTINUE; + } + + void OnModuleLoad(User *u, Module *m) + { + if (!u) + return; + + Implementation events[] = {I_OnDatabaseRead, I_OnDatabaseReadMetadata}; + for (int i = 0; i < 2; ++i) + { + std::vector::iterator it = std::find(ModuleManager::EventHandlers[events[i]].begin(), ModuleManager::EventHandlers[events[i]].end(), m); + /* This module wants to read from the database */ + if (it != ModuleManager::EventHandlers[events[i]].end()) + /* Loop over the database and call the events it would on a normal startup */ + ReadDatabase(m); + } + } +}; + +MODULE_INIT(DBPlain) diff --git a/modules/encryption/enc_md5.cpp b/modules/encryption/enc_md5.cpp new file mode 100644 index 000000000..109f31dfc --- /dev/null +++ b/modules/encryption/enc_md5.cpp @@ -0,0 +1,372 @@ +/* Module for encryption using MD5. + * + * Modified for Anope. + * (C) 2003-2011 Anope Team + * Contact us at team@anope.org + * + * Taken from IRC Services and is copyright (c) 1996-2002 Andrew Church. + * E-mail: + * Parts written by Andrew Kempe and others. + * This program is free but copyrighted software; see the file COPYING for + * details. + */ + +#include "module.h" + +/*************************************************************************/ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + */ + +/* MD5 context. */ +struct MD5_CTX +{ + unsigned state[4]; /* state (ABCD) */ + unsigned count[2]; /* number of bits, modulo 2^64 (lsb first) */ + unsigned char buffer[64]; /* input buffer */ +}; + +/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm + */ + +/* Constants for MD5Transform routine. + */ +enum +{ + S11 = 7, + S12 = 12, + S13 = 17, + S14 = 22, + S21 = 5, + S22 = 9, + S23 = 14, + S24 = 20, + S31 = 4, + S32 = 11, + S33 = 16, + S34 = 23, + S41 = 6, + S42 = 10, + S43 = 15, + S44 = 21 +}; + +void MD5Transform(unsigned [4], const unsigned char [64]); +void Encode(unsigned char *, unsigned *, unsigned); +void Decode(unsigned *, const unsigned char *, unsigned); + +static unsigned char PADDING[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* F, G, H and I are basic MD5 functions. + */ +inline static unsigned F(unsigned x, unsigned y, unsigned z) { return (x & y) | (~x & z); } +inline static unsigned G(unsigned x, unsigned y, unsigned z) { return (x & z) | (y & ~z); } +inline static unsigned H(unsigned x, unsigned y, unsigned z) { return x ^ y ^ z; } +inline static unsigned I(unsigned x, unsigned y, unsigned z) { return y ^ (x | ~z); } + +/* ROTATE_LEFT rotates x left n bits. + */ +inline static unsigned ROTATE_LEFT(unsigned x, unsigned n) { return (x << n) | (x >> (32 - n)); } + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. + * Rotation is separate from addition to prevent recomputation. + */ +inline static void FF(unsigned &a, unsigned b, unsigned c, unsigned d, unsigned x, unsigned s, unsigned ac) +{ + a += F(b, c, d) + x + ac; + a = ROTATE_LEFT(a, s); + a += b; +} +inline static void GG(unsigned &a, unsigned b, unsigned c, unsigned d, unsigned x, unsigned s, unsigned ac) +{ + a += G(b, c, d) + x + ac; + a = ROTATE_LEFT(a, s); + a += b; +} +inline static void HH(unsigned &a, unsigned b, unsigned c, unsigned d, unsigned x, unsigned s, unsigned ac) +{ + a += H(b, c, d) + x + ac; + a = ROTATE_LEFT(a, s); + a += b; +} +inline static void II(unsigned &a, unsigned b, unsigned c, unsigned d, unsigned x, unsigned s, unsigned ac) +{ + a += I(b, c, d) + x + ac; + a = ROTATE_LEFT(a, s); + a += b; +} + +/* MD5 initialization. Begins an MD5 operation, writing a new context. + */ +void MD5Init(MD5_CTX *context) +{ + context->count[0] = context->count[1] = 0; + /* Load magic initialization constants. */ + context->state[0] = 0x67452301; + context->state[1] = 0xefcdab89; + context->state[2] = 0x98badcfe; + context->state[3] = 0x10325476; +} + +/* MD5 block update operation. Continues an MD5 message-digest + * operation, processing another message block, and updating the + * context. + */ +void MD5Update(MD5_CTX *context, const unsigned char *input, unsigned inputLen) +{ + unsigned i, index, partLen; + + /* Compute number of bytes mod 64 */ + index = (context->count[0] >> 3) & 0x3F; + + /* Update number of bits */ + if ((context->count[0] += inputLen << 3) < (inputLen << 3)) + ++context->count[1]; + context->count[1] += inputLen >> 29; + + partLen = 64 - index; + + /* Transform as many times as possible. */ + if (inputLen >= partLen) + { + memcpy(&context->buffer[index], input, partLen); + MD5Transform(context->state, context->buffer); + + for (i = partLen; i + 63 < inputLen; i += 64) + MD5Transform(context->state, &input[i]); + + index = 0; + } + else + i = 0; + + /* Buffer remaining input */ + memcpy(&context->buffer[index], &input[i], inputLen - i); +} + +/* MD5 finalization. Ends an MD5 message-digest operation, writing the + * the message digest and zeroizing the context. + */ +void MD5Final(unsigned char digest[17], MD5_CTX *context) +{ + unsigned char bits[8]; + unsigned index, padLen; + + /* Save number of bits */ + Encode(bits, context->count, 8); + + /* Pad out to 56 mod 64. */ + index = (context->count[0] >> 3) & 0x3f; + padLen = index < 56 ? 56 - index : 120 - index; + MD5Update(context, PADDING, padLen); + + /* Append length (before padding) */ + MD5Update(context, bits, 8); + /* Store state in digest */ + Encode(digest, context->state, 16); + + /* Zeroize sensitive information. */ + memset(context, 0, sizeof(*context)); +} + +/* MD5 basic transformation. Transforms state based on block. + */ +void MD5Transform(unsigned state[4], const unsigned char block[64]) +{ + unsigned a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + + Decode(x, block, 64); + + /* Round 1 */ + FF(a, b, c, d, x[0], S11, 0xd76aa478); /* 1 */ + FF(d, a, b, c, x[1], S12, 0xe8c7b756); /* 2 */ + FF(c, d, a, b, x[2], S13, 0x242070db); /* 3 */ + FF(b, c, d, a, x[3], S14, 0xc1bdceee); /* 4 */ + FF(a, b, c, d, x[4], S11, 0xf57c0faf); /* 5 */ + FF(d, a, b, c, x[5], S12, 0x4787c62a); /* 6 */ + FF(c, d, a, b, x[6], S13, 0xa8304613); /* 7 */ + FF(b, c, d, a, x[7], S14, 0xfd469501); /* 8 */ + FF(a, b, c, d, x[8], S11, 0x698098d8); /* 9 */ + FF(d, a, b, c, x[9], S12, 0x8b44f7af); /* 10 */ + FF(c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF(b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF(a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF(d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF(c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF(b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG(a, b, c, d, x[1], S21, 0xf61e2562); /* 17 */ + GG(d, a, b, c, x[6], S22, 0xc040b340); /* 18 */ + GG(c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG(b, c, d, a, x[0], S24, 0xe9b6c7aa); /* 20 */ + GG(a, b, c, d, x[5], S21, 0xd62f105d); /* 21 */ + GG(d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG(c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG(b, c, d, a, x[4], S24, 0xe7d3fbc8); /* 24 */ + GG(a, b, c, d, x[9], S21, 0x21e1cde6); /* 25 */ + GG(d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG(c, d, a, b, x[3], S23, 0xf4d50d87); /* 27 */ + GG(b, c, d, a, x[8], S24, 0x455a14ed); /* 28 */ + GG(a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG(d, a, b, c, x[2], S22, 0xfcefa3f8); /* 30 */ + GG(c, d, a, b, x[7], S23, 0x676f02d9); /* 31 */ + GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH(a, b, c, d, x[5], S31, 0xfffa3942); /* 33 */ + HH(d, a, b, c, x[8], S32, 0x8771f681); /* 34 */ + HH(c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH(b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH(a, b, c, d, x[1], S31, 0xa4beea44); /* 37 */ + HH(d, a, b, c, x[4], S32, 0x4bdecfa9); /* 38 */ + HH(c, d, a, b, x[7], S33, 0xf6bb4b60); /* 39 */ + HH(b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH(a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH(d, a, b, c, x[0], S32, 0xeaa127fa); /* 42 */ + HH(c, d, a, b, x[3], S33, 0xd4ef3085); /* 43 */ + HH(b, c, d, a, x[6], S34, 0x4881d05); /* 44 */ + HH(a, b, c, d, x[9], S31, 0xd9d4d039); /* 45 */ + HH(d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH(c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH(b, c, d, a, x[2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II(a, b, c, d, x[0], S41, 0xf4292244); /* 49 */ + II(d, a, b, c, x[7], S42, 0x432aff97); /* 50 */ + II(c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II(b, c, d, a, x[5], S44, 0xfc93a039); /* 52 */ + II(a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II(d, a, b, c, x[3], S42, 0x8f0ccc92); /* 54 */ + II(c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II(b, c, d, a, x[1], S44, 0x85845dd1); /* 56 */ + II(a, b, c, d, x[8], S41, 0x6fa87e4f); /* 57 */ + II(d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II(c, d, a, b, x[6], S43, 0xa3014314); /* 59 */ + II(b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II(a, b, c, d, x[4], S41, 0xf7537e82); /* 61 */ + II(d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II(c, d, a, b, x[2], S43, 0x2ad7d2bb); /* 63 */ + II(b, c, d, a, x[9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + /* Zeroize sensitive information. */ + memset(x, 0, sizeof(x)); +} + +/* Encodes input (unsigned) into output (unsigned char). Assumes len is + * a multiple of 4. + */ +void Encode(unsigned char *output, unsigned *input, unsigned len) +{ + unsigned i, j; + + for (i = 0, j = 0; j < len; ++i, j += 4) + { + output[j] = static_cast(input[i] & 0xff); + output[j + 1] = static_cast((input[i] >> 8) & 0xff); + output[j + 2] = static_cast((input[i] >> 16) & 0xff); + output[j + 3] = static_cast((input[i] >> 24) & 0xff); + } +} + +/* Decodes input (unsigned char) into output (unsigned). Assumes len is + * a multiple of 4. + */ +void Decode(unsigned *output, const unsigned char *input, unsigned len) +{ + unsigned i, j; + + for (i = 0, j = 0; j < len; ++i, j += 4) + output[i] = static_cast(input[j]) | (static_cast(input[j + 1]) << 8) | (static_cast(input[j + 2]) << 16) | (static_cast(input[j + 3]) << 24); +} + +/*************************************************************************/ + +/* Module stuff. */ + +class EMD5 : public Module +{ + public: + EMD5(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, ENCRYPTION) + { + this->SetAuthor("Anope"); + + Implementation i[] = { I_OnEncrypt, I_OnCheckAuthentication }; + ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); + } + + EventReturn OnEncrypt(const Anope::string &src, Anope::string &dest) + { + MD5_CTX context; + char digest[17] = ""; + Anope::string buf = "md5:"; + + MD5Init(&context); + MD5Update(&context, reinterpret_cast(src.c_str()), src.length()); + MD5Final(reinterpret_cast(digest), &context); + + buf += Anope::Hex(digest, 16); + Log(LOG_DEBUG_2) << "(enc_md5) hashed password from [" << src << "] to [" << buf << "]"; + dest = buf; + return EVENT_ALLOW; + } + + EventReturn OnCheckAuthentication(Command *c, CommandSource *source, const std::vector ¶ms, const Anope::string &account, const Anope::string &password) + { + NickAlias *na = findnick(account); + NickCore *nc = na ? na->nc : NULL; + if (na == NULL) + return EVENT_CONTINUE; + + size_t pos = nc->pass.find(':'); + if (pos == Anope::string::npos) + return EVENT_CONTINUE; + Anope::string hash_method(nc->pass.begin(), nc->pass.begin() + pos); + if (!hash_method.equals_cs("md5")) + return EVENT_CONTINUE; + + Anope::string buf; + this->OnEncrypt(password, 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) + enc_encrypt(password, nc->pass); + return EVENT_ALLOW; + } + + return EVENT_CONTINUE; + } +}; + +MODULE_INIT(EMD5) diff --git a/modules/encryption/enc_none.cpp b/modules/encryption/enc_none.cpp new file mode 100644 index 000000000..180b431c2 --- /dev/null +++ b/modules/encryption/enc_none.cpp @@ -0,0 +1,74 @@ +/* Module for plain text encryption. + * + * (C) 2003-2011 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) + { + this->SetAuthor("Anope"); + + Implementation i[] = { I_OnEncrypt, I_OnDecrypt, I_OnCheckAuthentication }; + ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); + } + + EventReturn OnEncrypt(const Anope::string &src, Anope::string &dest) + { + 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) + { + 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; + } + + EventReturn OnCheckAuthentication(Command *c, CommandSource *source, const std::vector ¶ms, const Anope::string &account, const Anope::string &password) + { + NickAlias *na = findnick(account); + NickCore *nc = na ? na->nc : NULL; + if (na == NULL) + return EVENT_CONTINUE; + + size_t pos = nc->pass.find(':'); + if (pos == Anope::string::npos) + return EVENT_CONTINUE; + Anope::string hash_method(nc->pass.begin(), nc->pass.begin() + pos); + if (!hash_method.equals_cs("plain")) + return EVENT_CONTINUE; + + Anope::string buf; + this->OnEncrypt(password, 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) + enc_encrypt(password, nc->pass); + return EVENT_ALLOW; + } + + return EVENT_CONTINUE; + } +}; + +MODULE_INIT(ENone) diff --git a/modules/encryption/enc_old.cpp b/modules/encryption/enc_old.cpp new file mode 100644 index 000000000..e68887c49 --- /dev/null +++ b/modules/encryption/enc_old.cpp @@ -0,0 +1,382 @@ +/* Include file for high-level encryption routines. + * + * (C) 2003-2011 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" + +/******** Code specific to the type of encryption. ********/ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + */ + +/* MD5 context. */ +struct MD5_CTX +{ + unsigned state[4]; /* state (ABCD) */ + unsigned count[2]; /* number of bits, modulo 2^64 (lsb first) */ + unsigned char buffer[64]; /* input buffer */ +}; + +/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm + */ + +/* Constants for MD5Transform routine. + */ +enum +{ + S11 = 7, + S12 = 12, + S13 = 17, + S14 = 22, + S21 = 5, + S22 = 9, + S23 = 14, + S24 = 20, + S31 = 4, + S32 = 11, + S33 = 16, + S34 = 23, + S41 = 6, + S42 = 10, + S43 = 15, + S44 = 21 +}; + +static void MD5Transform(unsigned [4], const unsigned char[64]); +static void Encode(unsigned char *, unsigned *, unsigned); +static void Decode(unsigned *, const unsigned char *, unsigned); + +static unsigned char PADDING[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* F, G, H and I are basic MD5 functions. + */ +inline static unsigned F(unsigned x, unsigned y, unsigned z) { return (x & y) | (~x & z); } +inline static unsigned G(unsigned x, unsigned y, unsigned z) { return (x & z) | (y & ~z); } +inline static unsigned H(unsigned x, unsigned y, unsigned z) { return x ^ y ^ z; } +inline static unsigned MD5_I(unsigned x, unsigned y, unsigned z) { return y ^ (x | ~z); } + +/* ROTATE_LEFT rotates x left n bits. + */ +inline static unsigned ROTATE_LEFT(unsigned x, unsigned n) { return (x << n) | (x >> (32 - n)); } + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. + * Rotation is separate from addition to prevent recomputation. + */ +inline static void FF(unsigned &a, unsigned b, unsigned c, unsigned d, unsigned x, unsigned s, unsigned ac) +{ + a += F(b, c, d) + x + ac; + a = ROTATE_LEFT(a, s); + a += b; +} +inline static void GG(unsigned &a, unsigned b, unsigned c, unsigned d, unsigned x, unsigned s, unsigned ac) +{ + a += G(b, c, d) + x + ac; + a = ROTATE_LEFT(a, s); + a += b; +} +inline static void HH(unsigned &a, unsigned b, unsigned c, unsigned d, unsigned x, unsigned s, unsigned ac) +{ + a += H(b, c, d) + x + ac; + a = ROTATE_LEFT(a, s); + a += b; +} +inline static void II(unsigned &a, unsigned b, unsigned c, unsigned d, unsigned x, unsigned s, unsigned ac) +{ + a += MD5_I(b, c, d) + x + ac; + a = ROTATE_LEFT(a, s); + a += b; +} + +/* MD5 initialization. Begins an MD5 operation, writing a new context. + */ +static void MD5Init(MD5_CTX *context) +{ + context->count[0] = context->count[1] = 0; + /* Load magic initialization constants. + */ + context->state[0] = 0x67452301; + context->state[1] = 0xefcdab89; + context->state[2] = 0x98badcfe; + context->state[3] = 0x10325476; +} + +/* MD5 block update operation. Continues an MD5 message-digest + * operation, processing another message block, and updating the + * context. + */ +static void MD5Update(MD5_CTX *context, const unsigned char *input, unsigned inputLen) +{ + unsigned i, index, partLen; + + /* Compute number of bytes mod 64 */ + index = (context->count[0] >> 3) & 0x3F; + + /* Update number of bits */ + if ((context->count[0] += inputLen << 3) < (inputLen << 3)) + ++context->count[1]; + context->count[1] += inputLen >> 29; + + partLen = 64 - index; + + /* Transform as many times as possible. + */ + if (inputLen >= partLen) + { + memcpy(&context->buffer[index], input, partLen); + MD5Transform(context->state, context->buffer); + + for (i = partLen; i + 63 < inputLen; i += 64) + MD5Transform(context->state, &input[i]); + + index = 0; + } + else + i = 0; + + /* Buffer remaining input */ + memcpy(&context->buffer[index], &input[i], inputLen - i); +} + +/* MD5 finalization. Ends an MD5 message-digest operation, writing the + * the message digest and zeroizing the context. + */ +static void MD5Final(unsigned char digest[17], MD5_CTX *context) +{ + unsigned char bits[8]; + unsigned index, padLen; + + /* Save number of bits */ + Encode(bits, context->count, 8); + + /* Pad out to 56 mod 64. + */ + index = (context->count[0] >> 3) & 0x3f; + padLen = index < 56 ? 56 - index : 120 - index; + MD5Update(context, PADDING, padLen); + + /* Append length (before padding) */ + MD5Update(context, bits, 8); + /* Store state in digest */ + Encode(digest, context->state, 16); + + /* Zeroize sensitive information. + */ + memset(context, 0, sizeof(*context)); +} + +/* MD5 basic transformation. Transforms state based on block. + */ +static void MD5Transform(unsigned state[4], const unsigned char block[64]) +{ + unsigned a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + + Decode(x, block, 64); + + /* Round 1 */ + FF(a, b, c, d, x[0], S11, 0xd76aa478); /* 1 */ + FF(d, a, b, c, x[1], S12, 0xe8c7b756); /* 2 */ + FF(c, d, a, b, x[2], S13, 0x242070db); /* 3 */ + FF(b, c, d, a, x[3], S14, 0xc1bdceee); /* 4 */ + FF(a, b, c, d, x[4], S11, 0xf57c0faf); /* 5 */ + FF(d, a, b, c, x[5], S12, 0x4787c62a); /* 6 */ + FF(c, d, a, b, x[6], S13, 0xa8304613); /* 7 */ + FF(b, c, d, a, x[7], S14, 0xfd469501); /* 8 */ + FF(a, b, c, d, x[8], S11, 0x698098d8); /* 9 */ + FF(d, a, b, c, x[9], S12, 0x8b44f7af); /* 10 */ + FF(c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF(b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF(a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF(d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF(c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF(b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG(a, b, c, d, x[1], S21, 0xf61e2562); /* 17 */ + GG(d, a, b, c, x[6], S22, 0xc040b340); /* 18 */ + GG(c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG(b, c, d, a, x[0], S24, 0xe9b6c7aa); /* 20 */ + GG(a, b, c, d, x[5], S21, 0xd62f105d); /* 21 */ + GG(d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG(c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG(b, c, d, a, x[4], S24, 0xe7d3fbc8); /* 24 */ + GG(a, b, c, d, x[9], S21, 0x21e1cde6); /* 25 */ + GG(d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG(c, d, a, b, x[3], S23, 0xf4d50d87); /* 27 */ + GG(b, c, d, a, x[8], S24, 0x455a14ed); /* 28 */ + GG(a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG(d, a, b, c, x[2], S22, 0xfcefa3f8); /* 30 */ + GG(c, d, a, b, x[7], S23, 0x676f02d9); /* 31 */ + GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH(a, b, c, d, x[5], S31, 0xfffa3942); /* 33 */ + HH(d, a, b, c, x[8], S32, 0x8771f681); /* 34 */ + HH(c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH(b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH(a, b, c, d, x[1], S31, 0xa4beea44); /* 37 */ + HH(d, a, b, c, x[4], S32, 0x4bdecfa9); /* 38 */ + HH(c, d, a, b, x[7], S33, 0xf6bb4b60); /* 39 */ + HH(b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH(a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH(d, a, b, c, x[0], S32, 0xeaa127fa); /* 42 */ + HH(c, d, a, b, x[3], S33, 0xd4ef3085); /* 43 */ + HH(b, c, d, a, x[6], S34, 0x4881d05); /* 44 */ + HH(a, b, c, d, x[9], S31, 0xd9d4d039); /* 45 */ + HH(d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH(c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH(b, c, d, a, x[2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II(a, b, c, d, x[0], S41, 0xf4292244); /* 49 */ + II(d, a, b, c, x[7], S42, 0x432aff97); /* 50 */ + II(c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II(b, c, d, a, x[5], S44, 0xfc93a039); /* 52 */ + II(a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II(d, a, b, c, x[3], S42, 0x8f0ccc92); /* 54 */ + II(c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II(b, c, d, a, x[1], S44, 0x85845dd1); /* 56 */ + II(a, b, c, d, x[8], S41, 0x6fa87e4f); /* 57 */ + II(d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II(c, d, a, b, x[6], S43, 0xa3014314); /* 59 */ + II(b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II(a, b, c, d, x[4], S41, 0xf7537e82); /* 61 */ + II(d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II(c, d, a, b, x[2], S43, 0x2ad7d2bb); /* 63 */ + II(b, c, d, a, x[9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + /* Zeroize sensitive information. + */ + memset(x, 0, sizeof(x)); +} + +/* Encodes input (unsigned) into output (unsigned char). Assumes len is + * a multiple of 4. + */ +static void Encode(unsigned char *output, unsigned *input, unsigned len) +{ + unsigned i, j; + + for (i = 0, j = 0; j < len; ++i, j += 4) + { + output[j] = static_cast(input[i] & 0xff); + output[j + 1] = static_cast((input[i] >> 8) & 0xff); + output[j + 2] = static_cast((input[i] >> 16) & 0xff); + output[j + 3] = static_cast((input[i] >> 24) & 0xff); + } +} + +/* Decodes input (unsigned char) into output (unsigned). Assumes len is + * a multiple of 4. + */ +static void Decode(unsigned *output, const unsigned char *input, unsigned len) +{ + unsigned i, j; + + for (i = 0, j = 0; j < len; ++i, j += 4) + output[i] = static_cast(input[j]) | (static_cast(input[j + 1]) << 8) | (static_cast(input[j + 2]) << 16) | (static_cast(input[j + 3]) << 24); +} + +/*************************************************************************/ + +/******** Our own high-level routines. ********/ + +inline static char XTOI(char c) { return c > 9 ? c - 'A' + 10 : c - '0'; } + +class EOld : public Module +{ + public: + EOld(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, ENCRYPTION) + { + this->SetAuthor("Anope"); + + Implementation i[] = { I_OnEncrypt, I_OnCheckAuthentication }; + ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); + } + + EventReturn OnEncrypt(const Anope::string &src, Anope::string &dest) + { + MD5_CTX context; + char digest[33] = "", digest2[17] = ""; + int i; + Anope::string buf = "oldmd5:"; + + memset(&context, 0, sizeof(context)); + + MD5Init(&context); + MD5Update(&context, reinterpret_cast(src.c_str()), src.length()); + MD5Final(reinterpret_cast(digest), &context); + for (i = 0; i < 32; i += 2) + digest2[i / 2] = XTOI(digest[i]) << 4 | XTOI(digest[i + 1]); + + buf += Anope::Hex(digest2, 16); + Log(LOG_DEBUG_2) << "(enc_old) hashed password from [" << src << "] to [" << buf << "]"; + dest = buf; + return EVENT_ALLOW; + } + + EventReturn OnCheckAuthentication(Command *c, CommandSource *source, const std::vector ¶ms, const Anope::string &account, const Anope::string &password) + { + NickAlias *na = findnick(account); + NickCore *nc = na ? na->nc : NULL; + if (na == NULL) + return EVENT_CONTINUE; + + size_t pos = nc->pass.find(':'); + if (pos == Anope::string::npos) + return EVENT_CONTINUE; + Anope::string hash_method(nc->pass.begin(), nc->pass.begin() + pos); + if (!hash_method.equals_cs("oldmd5")) + return EVENT_CONTINUE; + + Anope::string buf; + this->OnEncrypt(password, 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) + enc_encrypt(password, nc->pass); + return EVENT_ALLOW; + } + + return EVENT_CONTINUE; + } +}; + +MODULE_INIT(EOld) diff --git a/modules/encryption/enc_sha1.cpp b/modules/encryption/enc_sha1.cpp new file mode 100644 index 000000000..4fe321bdc --- /dev/null +++ b/modules/encryption/enc_sha1.cpp @@ -0,0 +1,222 @@ +/* +SHA-1 in C +By Steve Reid +100% Public Domain + +Test Vectors (from FIPS PUB 180-1) +"abc" + A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D +"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 +A million repetitions of "a" + 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F +*/ + +/* #define LITTLE_ENDIAN * This should be #define'd if true. */ + +#include "module.h" + +struct SHA1_CTX +{ + uint32 state[5]; + uint32 count[2]; + unsigned char buffer[64]; +}; + +void SHA1Transform(uint32 state[5], const unsigned char buffer[64]); +void SHA1Init(SHA1_CTX *context); +void SHA1Update(SHA1_CTX *context, const unsigned char *data, uint32 len); +void SHA1Final(unsigned char digest[20], SHA1_CTX *context); + +inline static uint32 rol(uint32 value, uint32 bits) { return (value << bits) | (value >> (32 - bits)); } + +union CHAR64LONG16 +{ + unsigned char c[64]; + uint32 l[16]; +}; + +/* blk0() and blk() perform the initial expand. */ +/* I got the idea of expanding during the round function from SSLeay */ +inline static uint32 blk0(CHAR64LONG16 *block, uint32 i) +{ +#ifdef LITTLE_ENDIAN + return block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) | (rol(block->l[i], 8) & 0x00FF00FF); +#else + return block->l[i]; +#endif +} +inline static uint32 blk(CHAR64LONG16 *block, uint32 i) { return block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ block->l[(i + 8) & 15] ^ block->l[(i + 2) & 15] ^ block->l[i & 15],1); } + +/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ +inline static void R0(CHAR64LONG16 *block, uint32 v, uint32 &w, uint32 x, uint32 y, uint32 &z, uint32 i) { z += ((w & (x ^ y)) ^ y) + blk0(block, i) + 0x5A827999 + rol(v, 5); w = rol(w, 30); } +inline static void R1(CHAR64LONG16 *block, uint32 v, uint32 &w, uint32 x, uint32 y, uint32 &z, uint32 i) { z += ((w & (x ^ y)) ^ y) + blk(block, i) + 0x5A827999 + rol(v, 5); w = rol(w, 30); } +inline static void R2(CHAR64LONG16 *block, uint32 v, uint32 &w, uint32 x, uint32 y, uint32 &z, uint32 i) { z += (w ^ x ^ y) + blk(block, i) + 0x6ED9EBA1 + rol(v, 5); w = rol(w, 30); } +inline static void R3(CHAR64LONG16 *block, uint32 v, uint32 &w, uint32 x, uint32 y, uint32 &z, uint32 i) { z += (((w | x) & y) | (w & x)) + blk(block, i) + 0x8F1BBCDC + rol(v, 5); w = rol(w, 30); } +inline static void R4(CHAR64LONG16 *block, uint32 v, uint32 &w, uint32 x, uint32 y, uint32 &z, uint32 i) { z += (w ^ x ^ y) + blk(block, i) + 0xCA62C1D6 + rol(v, 5); w = rol(w, 30); } + +/* Hash a single 512-bit block. This is the core of the algorithm. */ + +void SHA1Transform(uint32 state[5], const unsigned char buffer[64]) +{ + uint32 a, b, c, d, e; + static unsigned char workspace[64]; + CHAR64LONG16 *block = reinterpret_cast(workspace); + memcpy(block, buffer, 64); + /* Copy context->state[] to working vars */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(block, a, b, c, d, e, 0); R0(block, e, a, b, c, d, 1); R0(block, d, e, a, b, c, 2); R0(block, c, d, e, a, b, 3); + R0(block, b, c, d, e, a, 4); R0(block, a, b, c, d, e, 5); R0(block, e, a, b, c, d, 6); R0(block, d, e, a, b, c, 7); + R0(block, c, d, e, a, b, 8); R0(block, b, c, d, e, a, 9); R0(block, a, b, c, d, e, 10); R0(block, e, a, b, c, d, 11); + R0(block, d, e, a, b, c, 12); R0(block, c, d, e, a, b, 13); R0(block, b, c, d, e, a, 14); R0(block, a, b, c, d, e, 15); + R1(block, e, a, b, c, d, 16); R1(block, d, e, a, b, c, 17); R1(block, c, d, e, a, b, 18); R1(block, b, c, d, e, a, 19); + R2(block, a, b, c, d, e, 20); R2(block, e, a, b, c, d, 21); R2(block, d, e, a, b, c, 22); R2(block, c, d, e, a, b, 23); + R2(block, b, c, d, e, a, 24); R2(block, a, b, c, d, e, 25); R2(block, e, a, b, c, d, 26); R2(block, d, e, a, b, c, 27); + R2(block, c, d, e, a, b, 28); R2(block, b, c, d, e, a, 29); R2(block, a, b, c, d, e, 30); R2(block, e, a, b, c, d, 31); + R2(block, d, e, a, b, c, 32); R2(block, c, d, e, a, b, 33); R2(block, b, c, d, e, a, 34); R2(block, a, b, c, d, e, 35); + R2(block, e, a, b, c, d, 36); R2(block, d, e, a, b, c, 37); R2(block, c, d, e, a, b, 38); R2(block, b, c, d, e, a, 39); + R3(block, a, b, c, d, e, 40); R3(block, e, a, b, c, d, 41); R3(block, d, e, a, b, c, 42); R3(block, c, d, e, a, b, 43); + R3(block, b, c, d, e, a, 44); R3(block, a, b, c, d, e, 45); R3(block, e, a, b, c, d, 46); R3(block, d, e, a, b, c, 47); + R3(block, c, d, e, a, b, 48); R3(block, b, c, d, e, a, 49); R3(block, a, b, c, d, e, 50); R3(block, e, a, b, c, d, 51); + R3(block, d, e, a, b, c, 52); R3(block, c, d, e, a, b, 53); R3(block, b, c, d, e, a, 54); R3(block, a, b, c, d, e, 55); + R3(block, e, a, b, c, d, 56); R3(block, d, e, a, b, c, 57); R3(block, c, d, e, a, b, 58); R3(block, b, c, d, e, a, 59); + R4(block, a, b, c, d, e, 60); R4(block, e, a, b, c, d, 61); R4(block, d, e, a, b, c, 62); R4(block, c, d, e, a, b, 63); + R4(block, b, c, d, e, a, 64); R4(block, a, b, c, d, e, 65); R4(block, e, a, b, c, d, 66); R4(block, d, e, a, b, c, 67); + R4(block, c, d, e, a, b, 68); R4(block, b, c, d, e, a, 69); R4(block, a, b, c, d, e, 70); R4(block, e, a, b, c, d, 71); + R4(block, d, e, a, b, c, 72); R4(block, c, d, e, a, b, 73); R4(block, b, c, d, e, a, 74); R4(block, a, b, c, d, e, 75); + R4(block, e, a, b, c, d, 76); R4(block, d, e, a, b, c, 77); R4(block, c, d, e, a, b, 78); R4(block, b, c, d, e, a, 79); + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + /* Wipe variables */ + a = b = c = d = e = 0; +} + +/* SHA1Init - Initialize new context */ + +void SHA1Init(SHA1_CTX *context) +{ + /* SHA1 initialization constants */ + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; + context->count[0] = context->count[1] = 0; +} + +/* Run your data through this. */ + +void SHA1Update(SHA1_CTX *context, const unsigned char *data, uint32 len) +{ + uint32 i, j; + + j = (context->count[0] >> 3) & 63; + if ((context->count[0] += len << 3) < (len << 3)) + ++context->count[1]; + context->count[1] += len >> 29; + if (j + len > 63) + { + memcpy(&context->buffer[j], data, (i = 64 - j)); + SHA1Transform(context->state, context->buffer); + for (; i + 63 < len; i += 64) + SHA1Transform(context->state, &data[i]); + j = 0; + } + else + i = 0; + memcpy(&context->buffer[j], &data[i], len - i); +} + +/* Add padding and return the message digest. */ + +void SHA1Final(unsigned char digest[21], SHA1_CTX *context) +{ + uint32 i; + unsigned char finalcount[8]; + + for (i = 0; i < 8; ++i) + finalcount[i] = static_cast((context->count[i >= 4 ? 0 : 1] >> ((3 - (i & 3)) * 8)) & 255); /* Endian independent */ + SHA1Update(context, reinterpret_cast("\200"), 1); + while ((context->count[0] & 504) != 448) + SHA1Update(context, reinterpret_cast("\0"), 1); + SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ + for (i = 0; i < 20; ++i) + digest[i] = static_cast((context->state[i>>2] >> ((3 - (i & 3)) * 8)) & 255); + /* Wipe variables */ + i = 0; + memset(context->buffer, 0, 64); + memset(context->state, 0, 20); + memset(context->count, 0, 8); + memset(&finalcount, 0, 8); + SHA1Transform(context->state, context->buffer); +} + +/*****************************************************************************/ +/*****************************************************************************/ + +/* Module stuff. */ + +class ESHA1 : public Module +{ + public: + ESHA1(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, ENCRYPTION) + { + this->SetAuthor("Anope"); + + Implementation i[] = { I_OnEncrypt, I_OnCheckAuthentication }; + ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); + } + + EventReturn OnEncrypt(const Anope::string &src, Anope::string &dest) + { + SHA1_CTX context; + char digest[21] = ""; + Anope::string buf = "sha1:"; + + SHA1Init(&context); + SHA1Update(&context, reinterpret_cast(src.c_str()), src.length()); + SHA1Final(reinterpret_cast(digest), &context); + + buf += Anope::Hex(digest, 20); + Log(LOG_DEBUG_2) << "(enc_sha1) hashed password from [" << src << "] to [" << buf << "]"; + dest = buf; + return EVENT_ALLOW; + } + + EventReturn OnCheckAuthentication(Command *c, CommandSource *source, const std::vector ¶ms, const Anope::string &account, const Anope::string &password) + { + NickAlias *na = findnick(account); + NickCore *nc = na ? na->nc : NULL; + if (na == NULL) + return EVENT_CONTINUE; + + size_t pos = nc->pass.find(':'); + if (pos == Anope::string::npos) + return EVENT_CONTINUE; + Anope::string hash_method(nc->pass.begin(), nc->pass.begin() + pos); + if (!hash_method.equals_cs("sha1")) + return EVENT_CONTINUE; + + Anope::string buf; + this->OnEncrypt(password, buf); + if (nc->pass.equals_cs(buf)) + { + if (ModuleManager::FindFirstOf(ENCRYPTION) != this) + enc_encrypt(password, nc->pass); + return EVENT_ALLOW; + } + + return EVENT_CONTINUE; + } +}; + +MODULE_INIT(ESHA1) diff --git a/modules/encryption/enc_sha256.cpp b/modules/encryption/enc_sha256.cpp new file mode 100644 index 000000000..3034f3731 --- /dev/null +++ b/modules/encryption/enc_sha256.cpp @@ -0,0 +1,314 @@ +/* This module generates and compares password hashes using SHA256 algorithms. + * To help reduce the risk of dictionary attacks, the code appends random bytes + * (so-called "salt") to the original plain text before generating hashes and + * stores this salt appended to the result. To verify another plain text value + * against the given hash, this module will retrieve the salt value from the + * password string and use it when computing a new hash of the plain text. + * + * 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. + * + * Modified for Anope. + * (C) 2003-2011 Anope Team + * Contact us at team@anope.org + * + * Taken from InspIRCd ( www.inspircd.org ) + * see http://wiki.inspircd.org/Credits + * + * This program is free but copyrighted software; see + * the file COPYING for details. + */ + +/* FIPS 180-2 SHA-224/256/384/512 implementation + * Last update: 05/23/2005 + * Issue date: 04/30/2005 + * + * Copyright (C) 2005 Olivier Gay + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "module.h" + +static const unsigned SHA256_DIGEST_SIZE = 256 / 8; +static const unsigned SHA256_BLOCK_SIZE = 512 / 8; + +/** An sha256 context + */ +class SHA256Context +{ + public: + unsigned tot_len; + unsigned len; + unsigned char block[2 * SHA256_BLOCK_SIZE]; + uint32 h[8]; +}; + +inline static uint32 SHFR(uint32 x, uint32 n) { return x >> n; } +inline static uint32 ROTR(uint32 x, uint32 n) { return (x >> n) | (x << ((sizeof(x) << 3) - n)); } +inline static uint32 ROTL(uint32 x, uint32 n) { return (x << n) | (x >> ((sizeof(x) << 3) - n)); } +inline static uint32 CH(uint32 x, uint32 y, uint32 z) { return (x & y) ^ (~x & z); } +inline static uint32 MAJ(uint32 x, uint32 y, uint32 z) { return (x & y) ^ (x & z) ^ (y & z); } + +inline static uint32 SHA256_F1(uint32 x) { return ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22); } +inline static uint32 SHA256_F2(uint32 x) { return ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25); } +inline static uint32 SHA256_F3(uint32 x) { return ROTR(x, 7) ^ ROTR(x, 18) ^ SHFR(x, 3); } +inline static uint32 SHA256_F4(uint32 x) { return ROTR(x, 17) ^ ROTR(x, 19) ^ SHFR(x, 10); } + +inline static void UNPACK32(unsigned x, unsigned char *str) +{ + str[3] = static_cast(x); + str[2] = static_cast(x >> 8); + str[1] = static_cast(x >> 16); + str[0] = static_cast(x >> 24); +} + +inline static void PACK32(unsigned char *str, uint32 &x) +{ + x = static_cast(str[3]) | static_cast(str[2]) << 8 | static_cast(str[1]) << 16 | static_cast(str[0]) << 24; +} + +/* Macros used for loops unrolling */ + +inline static void SHA256_SCR(uint32 w[64], int i) +{ + w[i] = SHA256_F4(w[i - 2]) + w[i - 7] + SHA256_F3(w[i - 15]) + w[i - 16]; +} + +uint32 sha256_k[64] = +{ + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +}; + +class ESHA256 : public Module +{ + unsigned iv[8]; + bool use_iv; + + /* initializes the IV with a new random value */ + void NewRandomIV() + { + for (int i = 0; i < 8; ++i) + iv[i] = getrandom32(); + } + + /* returns the IV as base64-encrypted string */ + Anope::string GetIVString() + { + char buf[33]; + for (int i = 0; i < 8; ++i) + UNPACK32(iv[i], reinterpret_cast(&buf[i << 2])); + buf[32] = '\0'; + return Anope::Hex(buf, 32); + } + + /* splits the appended IV from the password string so it can be used for the next encryption */ + /* password format: :: */ + void GetIVFromPass(const Anope::string &password) + { + size_t pos = password.find(':'); + Anope::string buf = password.substr(password.find(':', pos + 1) + 1, password.length()); + char buf2[33]; + Anope::Unhex(buf, buf2); + for (int i = 0 ; i < 8; ++i) + PACK32(reinterpret_cast(&buf2[i << 2]), iv[i]); + } + + void SHA256Init(SHA256Context *ctx) + { + for (int i = 0; i < 8; ++i) + ctx->h[i] = iv[i]; + ctx->len = 0; + ctx->tot_len = 0; + } + + void SHA256Transform(SHA256Context *ctx, unsigned char *message, unsigned block_nb) + { + uint32 w[64], wv[8]; + unsigned char *sub_block; + for (unsigned i = 1; i <= block_nb; ++i) + { + int j; + sub_block = message + ((i - 1) << 6); + + for (j = 0; j < 16; ++j) + PACK32(&sub_block[j << 2], w[j]); + for (j = 16; j < 64; ++j) + SHA256_SCR(w, j); + for (j = 0; j < 8; ++j) + wv[j] = ctx->h[j]; + for (j = 0; j < 64; ++j) + { + uint32 t1 = wv[7] + SHA256_F2(wv[4]) + CH(wv[4], wv[5], wv[6]) + sha256_k[j] + w[j]; + uint32 t2 = SHA256_F1(wv[0]) + MAJ(wv[0], wv[1], wv[2]); + wv[7] = wv[6]; + wv[6] = wv[5]; + wv[5] = wv[4]; + wv[4] = wv[3] + t1; + wv[3] = wv[2]; + wv[2] = wv[1]; + wv[1] = wv[0]; + wv[0] = t1 + t2; + } + for (j = 0; j < 8; ++j) + ctx->h[j] += wv[j]; + } + } + + void SHA256Update(SHA256Context *ctx, const unsigned char *message, unsigned len) + { + /* + * XXX here be dragons! + * After many hours of pouring over this, I think I've found the problem. + * When Special created our module from the reference one, he used: + * + * unsigned rem_len = SHA256_BLOCK_SIZE - ctx->len; + * + * instead of the reference's version of: + * + * unsigned tmp_len = SHA256_BLOCK_SIZE - ctx->len; + * unsigned rem_len = len < tmp_len ? len : tmp_len; + * + * I've changed back to the reference version of this code, and it seems to work with no errors. + * So I'm inclined to believe this was the problem.. + * -- w00t (January 06, 2008) + */ + unsigned tmp_len = SHA256_BLOCK_SIZE - ctx->len, rem_len = len < tmp_len ? len : tmp_len; + + memcpy(&ctx->block[ctx->len], message, rem_len); + if (ctx->len + len < SHA256_BLOCK_SIZE) + { + ctx->len += len; + return; + } + unsigned new_len = len - rem_len, block_nb = new_len / SHA256_BLOCK_SIZE; + unsigned char *shifted_message = new unsigned char[len - rem_len]; + memcpy(shifted_message, message + rem_len, len - rem_len); + SHA256Transform(ctx, ctx->block, 1); + SHA256Transform(ctx, shifted_message, block_nb); + rem_len = new_len % SHA256_BLOCK_SIZE; + memcpy(ctx->block, &shifted_message[block_nb << 6], rem_len); + delete [] shifted_message; + ctx->len = rem_len; + ctx->tot_len += (block_nb + 1) << 6; + } + + void SHA256Final(SHA256Context *ctx, unsigned char *digest) + { + unsigned block_nb = 1 + ((SHA256_BLOCK_SIZE - 9) < (ctx->len % SHA256_BLOCK_SIZE)); + unsigned len_b = (ctx->tot_len + ctx->len) << 3; + unsigned pm_len = block_nb << 6; + memset(ctx->block + ctx->len, 0, pm_len - ctx->len); + ctx->block[ctx->len] = 0x80; + UNPACK32(len_b, ctx->block + pm_len - 4); + SHA256Transform(ctx, ctx->block, block_nb); + for (int i = 0 ; i < 8; ++i) + UNPACK32(ctx->h[i], &digest[i << 2]); + } + +/********** ANOPE ******/ + public: + ESHA256(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, ENCRYPTION) + { + this->SetAuthor("Anope"); + + Implementation i[] = { I_OnEncrypt, I_OnCheckAuthentication }; + ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); + + use_iv = false; + } + + EventReturn OnEncrypt(const Anope::string &src, Anope::string &dest) + { + char digest[SHA256_DIGEST_SIZE + 1]; + SHA256Context ctx; + std::stringstream buf; + + if (!use_iv) + NewRandomIV(); + else + use_iv = false; + + SHA256Init(&ctx); + SHA256Update(&ctx, reinterpret_cast(src.c_str()), src.length()); + SHA256Final(&ctx, reinterpret_cast(digest)); + digest[SHA256_DIGEST_SIZE] = '\0'; + buf << "sha256:" << Anope::Hex(digest, SHA256_DIGEST_SIZE) << ":" << GetIVString(); + Log(LOG_DEBUG_2) << "(enc_sha256) hashed password from [" << src << "] to [" << buf.str() << " ]"; + dest = buf.str(); + return EVENT_ALLOW; + } + + EventReturn OnCheckAuthentication(Command *c, CommandSource *source, const std::vector ¶ms, const Anope::string &account, const Anope::string &password) + { + NickAlias *na = findnick(account); + NickCore *nc = na ? na->nc : NULL; + if (na == NULL) + return EVENT_CONTINUE; + + size_t pos = nc->pass.find(':'); + if (pos == Anope::string::npos) + return EVENT_CONTINUE; + Anope::string hash_method(nc->pass.begin(), nc->pass.begin() + pos); + if (!hash_method.equals_cs("sha256")) + return EVENT_CONTINUE; + + GetIVFromPass(nc->pass); + use_iv = true; + Anope::string buf; + this->OnEncrypt(password, 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) + enc_encrypt(password, nc->pass); + return EVENT_ALLOW; + } + + return EVENT_CONTINUE; + } +}; + +MODULE_INIT(ESHA256) diff --git a/modules/extra/cs_appendtopic.cpp b/modules/extra/cs_appendtopic.cpp deleted file mode 100644 index c9af50613..000000000 --- a/modules/extra/cs_appendtopic.cpp +++ /dev/null @@ -1,117 +0,0 @@ -/* cs_appendtopic.c - Add text to a channels topic - * - * (C) 2003-2011 Anope Team - * Contact us at team@anope.org - * - * Based on the original module by SGR - * Included in the Anope module pack since Anope 1.7.9 - * Anope Coder: GeniusDex - * - * 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" - -/* ------------------------------------------------------------ - * Name: cs_appendtopic - * Author: SGR - * Date: 31/08/2003 - * ------------------------------------------------------------ - * - * This module has no configurable options. For information on - * this module, load it and refer to /ChanServ APPENDTOPIC HELP - * - * Thanks to dengel, Rob and Certus for all there support. - * Especially Rob, who always manages to show me where I have - * not allocated any memory. Even if it takes a few weeks of - * pestering to get him to look at it. - * - * ------------------------------------------------------------ - */ - -/* ---------------------------------------------------------------------- */ -/* DO NOT EDIT BELOW THIS LINE UNLESS YOU KNOW WHAT YOU ARE DOING */ -/* ---------------------------------------------------------------------- */ - -class CommandCSAppendTopic : public Command -{ - public: - CommandCSAppendTopic(Module *creator) : Command(creator, "chanserv/appendtopic", 2, 2) - { - this->SetDesc(_("Add text to a channels topic")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - const Anope::string &newtopic = params[1]; - - User *u = source.u; - Channel *c = findchan(params[0]);; - - if (!c) - source.Reply(CHAN_X_NOT_IN_USE, params[0].c_str()); - else if (!c->ci) - source.Reply(CHAN_X_NOT_REGISTERED, c->name.c_str()); - else if (!c->ci->HasPriv(u, CA_TOPIC)) - source.Reply(ACCESS_DENIED); - else - { - Anope::string topic; - if (!c->ci->last_topic.empty()) - { - topic = c->ci->last_topic + " " + newtopic; - c->ci->last_topic.clear(); - } - else - topic = newtopic; - - bool has_topiclock = c->ci->HasFlag(CI_TOPICLOCK); - c->ci->UnsetFlag(CI_TOPICLOCK); - c->ChangeTopic(u->nick, topic, Anope::CurTime); - if (has_topiclock) - c->ci->SetFlag(CI_TOPICLOCK); - - bool override = c->ci->HasPriv(u, CA_TOPIC); - Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, c->ci) << "changed topic to " << topic; - } - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - source.Reply(_("Syntax: APPENDTOPIC channel text")); - source.Reply(" "); - source.Reply(("This command allows users to append text to a currently set\n" - "channel topic. When TOPICLOCK is on, the topic is updated and\n" - "the new, updated topic is locked.")); - - return true; - } - - void OnSyntaxError(CommandSource &source, const Anope::string &subcommand) - { - source.Reply(_("Syntax: APPENDTOPIC channel text")); - } -}; - -class CSAppendTopic : public Module -{ - CommandCSAppendTopic commandcsappendtopic; - - public: - CSAppendTopic(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, SUPPORTED), - commandcsappendtopic(this) - { - this->SetAuthor("SGR"); - - ModuleManager::RegisterService(&commandcsappendtopic); - } -}; - -MODULE_INIT(CSAppendTopic) diff --git a/modules/extra/cs_enforce.cpp b/modules/extra/cs_enforce.cpp deleted file mode 100644 index 26885c9b0..000000000 --- a/modules/extra/cs_enforce.cpp +++ /dev/null @@ -1,215 +0,0 @@ -/* cs_enforce - Add a /cs ENFORCE command to enforce various set - * options and channelmodes on a channel. - * - * (C) 2003-2011 Anope Team - * Contact us at team@anope.org - * - * Included in the Anope module pack since Anope 1.7.9 - * Anope Coder: GeniusDex - * - * Please read COPYING and README for further details. - * - * Send any bug reports to the Anope Coder, as he will be able - * to deal with it best. - */ - -#include "module.h" - -class CommandCSEnforce : public Command -{ - private: - void DoSet(Channel *c) - { - ChannelInfo *ci; - - if (!(ci = c->ci)) - return; - - if (ci->HasFlag(CI_SECUREOPS)) - this->DoSecureOps(c); - if (ci->HasFlag(CI_RESTRICTED)) - this->DoRestricted(c); - } - - void DoModes(Channel *c) - { - if (c->HasMode(CMODE_REGISTEREDONLY)) - this->DoCModeR(c); - } - - void DoSecureOps(Channel *c) - { - ChannelInfo *ci; - bool hadsecureops = false; - - if (!(ci = c->ci)) - return; - - /* Dirty hack to allow chan_set_correct_modes to work ok. - * We pretend like SECUREOPS is on so it doesn't ignore that - * part of the code. This way we can enforce SECUREOPS even - * if it's off. - */ - if (!ci->HasFlag(CI_SECUREOPS)) - { - ci->SetFlag(CI_SECUREOPS); - hadsecureops = true; - } - - for (CUserList::iterator it = c->users.begin(), it_end = c->users.end(); it != it_end; ++it) - { - UserContainer *uc = *it; - - chan_set_correct_modes(uc->user, c, 0); - } - - if (hadsecureops) - ci->UnsetFlag(CI_SECUREOPS); - } - - void DoRestricted(Channel *c) - { - ChannelInfo *ci = c->ci; - if (ci == NULL) - return; - - for (CUserList::iterator it = c->users.begin(), it_end = c->users.end(); it != it_end; ) - { - UserContainer *uc = *it++; - User *user = uc->user; - - if (ci->AccessFor(user).empty()) - { - Anope::string mask; - get_idealban(ci, user, mask); - Anope::string reason = translate(user, CHAN_NOT_ALLOWED_TO_JOIN); - c->SetMode(NULL, CMODE_BAN, mask); - c->Kick(NULL, user, "%s", reason.c_str()); - } - } - } - - void DoCModeR(Channel *c) - { - ChannelInfo *ci; - Anope::string mask; - - if (!(ci = c->ci)) - return; - - for (CUserList::iterator it = c->users.begin(), it_end = c->users.end(); it != it_end; ) - { - UserContainer *uc = *it++; - - if (!uc->user->IsIdentified()) - { - get_idealban(ci, uc->user, mask); - Anope::string reason = translate(uc->user, CHAN_NOT_ALLOWED_TO_JOIN); - if (!c->HasMode(CMODE_REGISTEREDONLY)) - c->SetMode(NULL, CMODE_BAN, mask); - c->Kick(NULL, uc->user, "%s", reason.c_str()); - } - } - } - public: - CommandCSEnforce(Module *creator) : Command(creator, "chanserv/enforce", 1, 2) - { - this->SetDesc(_("Enforce various channel modes and set options")); - this->SetSyntax(_("\037channel\037 [\037what\037]")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - const Anope::string &what = params.size() > 1 ? params[1] : ""; - - User *u = source.u; - Channel *c = findchan(params[0]); - - if (!c) - source.Reply(CHAN_X_NOT_IN_USE, params[0].c_str()); - else if (!c->ci) - source.Reply(CHAN_X_NOT_REGISTERED, c->name.c_str()); - else if (!c->ci->HasPriv(u, CA_AKICK)) - source.Reply(ACCESS_DENIED); - else - { - if (what.empty() || what.equals_ci("SET")) - { - this->DoSet(c); - source.Reply(_("Enforced %s"), !what.empty() ? what.c_str() : "SET"); - } - else if (what.equals_ci("MODES")) - { - this->DoModes(c); - source.Reply(_("Enforced %s"), what.c_str()); - } - else if (what.equals_ci("SECUREOPS")) - { - this->DoSecureOps(c); - source.Reply(_("Enforced %s"), what.c_str()); - } - else if (what.equals_ci("RESTRICTED")) - { - this->DoRestricted(c); - source.Reply(_("Enforced %s"), what.c_str()); - } - else if (what.equals_ci("+R")) - { - this->DoCModeR(c); - source.Reply(_("Enforced %s"), what.c_str()); - } - else - this->OnSyntaxError(source, ""); - } - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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 SET, SECUREOPS, RESTRICTED, MODES,\n" - "or +R. When left out, it defaults to SET.\n" - " \n" - "If \037what\037 is SET, it will enforce SECUREOPS and RESTRICTED\n" - "on the users currently in the channel, if they are set. Give\n" - "SECUREOPS to enforce the SECUREOPS option, even if it is not\n" - "enabled. Use RESTRICTED to enfore the RESTRICTED option, also\n" - "if it's not enabled.")); - source.Reply(" "); - if (ModeManager::FindChannelModeByName(CMODE_REGISTERED)) - source.Reply(_("If \037what\037 is MODES, it will enforce channelmode +R if it is\n" - "set. If +R is specified for \037what\037, the +R channelmode will\n" - "also be enforced, but even if it is not set. If it is not set,\n" - "users will be banned to ensure they don't just rejoin.")); - else - source.Reply(_("If \037what\037 is MODES, nothing will be enforced, since it would\n" - "enforce modes that the current ircd does not support. If +R is\n" - "specified for \037what\037, an equalivant of channelmode +R on\n" - "other ircds will be enforced. All users that are in the channel\n" - "but have not identified for their nickname will be kicked and\n" - "banned from the channel.")); - - return true; - } -}; - -class CSEnforce : public Module -{ - CommandCSEnforce commandcsenforce; - - public: - CSEnforce(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, SUPPORTED), - commandcsenforce(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandcsenforce); - } -}; - -MODULE_INIT(CSEnforce) diff --git a/modules/extra/cs_entrymsg.cpp b/modules/extra/cs_entrymsg.cpp deleted file mode 100644 index 3aa8ed862..000000000 --- a/modules/extra/cs_entrymsg.cpp +++ /dev/null @@ -1,224 +0,0 @@ -/* ChanServ core functions - * - * (C) 2003-2011 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 EntryMsg -{ - static unsigned MaxEntries; - - EntryMsg(const Anope::string &cname, const Anope::string &cmessage, time_t ct = Anope::CurTime) - { - this->creator = cname; - this->message = cmessage; - this->when = ct; - } - - Anope::string creator; - Anope::string message; - time_t when; -}; -unsigned EntryMsg::MaxEntries = 0; - -class CommandEntryMessage : public Command -{ - private: - void DoList(CommandSource &source, ChannelInfo *ci) - { - std::vector messages; - if (ci->GetExtRegular("cs_entrymsg", messages)) - { - source.Reply(_("Entry message list for \2%s\2:"), ci->name.c_str()); - for (unsigned i = 0; i < messages.size(); ++i) - source.Reply(CHAN_LIST_ENTRY, i + 1, messages[i].message.c_str(), messages[i].creator.c_str(), do_strftime(messages[i].when).c_str()); - source.Reply(_("End of entry message list.")); - } - else - source.Reply(_("Entry message list for \2%s\2 is empty."), ci->name.c_str()); - } - - void DoAdd(CommandSource &source, ChannelInfo *ci, const Anope::string &message) - { - std::vector messages; - ci->GetExtRegular("cs_entrymsg", messages); - - if (EntryMsg::MaxEntries && messages.size() >= EntryMsg::MaxEntries) - source.Reply(_("The entry message list for \2%s\2 is full."), ci->name.c_str()); - else - { - messages.push_back(EntryMsg(source.u->nick, message)); - ci->Extend("cs_entrymsg", new ExtensibleItemRegular >(messages)); - source.Reply(_("Entry message added to \2%s\2"), ci->name.c_str()); - } - } - - void DoDel(CommandSource &source, ChannelInfo *ci, const Anope::string &message) - { - std::vector messages; - 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 (ci->GetExtRegular("cs_entrymsg", messages)) - { - try - { - unsigned i = convertTo(message); - if (i > 0 && i <= messages.size()) - { - messages.erase(messages.begin() + i - 1); - if (!messages.empty()) - ci->Extend("cs_entrymsg", new ExtensibleItemRegular >(messages)); - else - ci->Shrink("cs_entrymsg"); - source.Reply(_("Entry message \2%i\2 for \2%s\2 deleted."), i, ci->name.c_str()); - } - else - throw ConvertException(); - } - catch (const ConvertException &) - { - source.Reply(_("Entry message \2%s\2 not found on channel \2%s\2."), message.c_str(), ci->name.c_str()); - } - } - else - source.Reply(_("Entry message list for \2%s\2 is empty."), ci->name.c_str()); - } - - void DoClear(CommandSource &source, ChannelInfo *ci) - { - ci->Shrink("cs_entrymsg"); - source.Reply(_("Entry messages for \2%s\2 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|DEL|LIST|CLEAR} [\037message\037|\037num\037]")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - - ChannelInfo *ci = cs_findchan(params[0]); - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - if (IsFounder(u, ci) || u->HasCommand("chanserv/entrymsg")) - { - bool success = true; - 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) - { - success = false; - 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 - { - success = false; - this->OnSyntaxError(source, ""); - } - if (success) - Log(IsFounder(u, ci) ? LOG_COMMAND : LOG_OVERRIDE, u, this, ci) << params[1]; - } - else - source.Reply(ACCESS_DENIED); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Controls what messages will be sent to users when they join the channel.")); - return true; - } -}; - -class CSEntryMessage : public Module -{ - CommandEntryMessage commandentrymsg; - - public: - CSEntryMessage(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, SUPPORTED), commandentrymsg(this) - { - this->SetAuthor("Anope"); - - Implementation i[] = { I_OnJoinChannel, I_OnReload, I_OnDatabaseReadMetadata, I_OnDatabaseWriteMetadata }; - ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); - - ModuleManager::RegisterService(&commandentrymsg); - - this->OnReload(); - } - - void OnJoinChannel(User *u, Channel *c) - { - if (u && c && c->ci && u->server->IsSynced()) - { - std::vector messages; - - if (c->ci->GetExtRegular("cs_entrymsg", messages)) - for (unsigned i = 0; i < messages.size(); ++i) - u->SendMessage(c->ci->WhoSends(), "[%s] %s", c->ci->name.c_str(), messages[i].message.c_str()); - } - } - - void OnReload() - { - ConfigReader config; - EntryMsg::MaxEntries = config.ReadInteger("cs_entrymsg", "maxentries", "5", 0, true); - } - - EventReturn OnDatabaseReadMetadata(ChannelInfo *ci, const Anope::string &key, const std::vector ¶ms) - { - if (key.find("CS_ENTRYMSG_") == 0 && params.size() > 2) - { - Anope::string creator = params[0]; - time_t t = params[1].is_pos_number_only() ? convertTo(params[1]) : Anope::CurTime; - Anope::string message = params[2]; - for (unsigned j = 3; j < params.size(); ++j) - message += " " + params[j]; - - std::vector messages; - ci->GetExtRegular("cs_entrymsg", messages); - messages.push_back(EntryMsg(creator, message, t)); - ci->Extend("cs_entrymsg", new ExtensibleItemRegular >(messages)); - - return EVENT_STOP; - } - - return EVENT_CONTINUE; - } - - void OnDatabaseWriteMetadata(void (*WriteMetadata)(const Anope::string &, const Anope::string &), ChannelInfo *ci) - { - std::vector messages; - if (ci->GetExtRegular("cs_entrymsg", messages)) - for (unsigned i = 0; i < messages.size(); ++i) - WriteMetadata("CS_ENTRYMSG_" + stringify(i), messages[i].creator + " " + stringify(messages[i].when) + " " + messages[i].message); - } -}; - -MODULE_INIT(CSEntryMessage) diff --git a/modules/extra/cs_set_misc.cpp b/modules/extra/cs_set_misc.cpp deleted file mode 100644 index 8f145ab88..000000000 --- a/modules/extra/cs_set_misc.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/* - * (C) 2003-2011 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 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 ¶ms) - { - ChannelInfo *ci = cs_findchan(params[0]); - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - ci->Shrink("cs_set_misc:" + source.command.replace_all_cs(" ", "_")); - if (params.size() > 1) - { - ci->Extend("cs_set_misc:" + source.command.replace_all_cs(" ", "_"), new ExtensibleItemRegular(params[1])); - source.Reply(CHAN_SETTING_CHANGED, source.command.c_str(), ci->name.c_str(), params[1].c_str()); - } - else - source.Reply(CHAN_SETTING_UNSET, source.command.c_str(), ci->name.c_str()); - } -}; - -class CommandCSSASetMisc : public CommandCSSetMisc -{ - public: - CommandCSSASetMisc(Module *creator) : CommandCSSetMisc(creator, "chanserv/saset/misc") - { - } -}; - -class CSSetMisc : public Module -{ - CommandCSSetMisc commandcssetmisc; - CommandCSSASetMisc commandcssasetmisc; - - public: - CSSetMisc(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, SUPPORTED), - commandcssetmisc(this), commandcssasetmisc(this) - { - this->SetAuthor("Anope"); - - Implementation i[] = { I_OnChanInfo, I_OnDatabaseWriteMetadata, I_OnDatabaseReadMetadata }; - ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); - - ModuleManager::RegisterService(&this->commandcssetmisc); - ModuleManager::RegisterService(&this->commandcssasetmisc); - } - - void OnChanInfo(CommandSource &source, ChannelInfo *ci, bool ShowHidden) - { - std::deque list; - ci->GetExtList(list); - - for (unsigned i = 0; i < list.size(); ++i) - { - if (list[i].find("cs_set_misc:") != 0) - continue; - - Anope::string value; - if (ci->GetExtRegular(list[i], value)) - source.Reply(" %s: %s", list[i].substr(12).replace_all_cs("_", " ").c_str(), value.c_str()); - } - } - - void OnDatabaseWriteMetadata(void (*WriteMetadata)(const Anope::string &, const Anope::string &), ChannelInfo *ci) - { - std::deque list; - ci->GetExtList(list); - - for (unsigned i = 0; i < list.size(); ++i) - { - if (list[i].find("cs_set_misc:") != 0) - continue; - - Anope::string value; - if (ci->GetExtRegular(list[i], value)) - WriteMetadata(list[i], ":" + value); - } - } - - EventReturn OnDatabaseReadMetadata(ChannelInfo *ci, const Anope::string &key, const std::vector ¶ms) - { - if (key.find("cs_set_misc:") == 0) - ci->Extend(key, new ExtensibleItemRegular(params[0])); - - return EVENT_CONTINUE; - } -}; - -MODULE_INIT(CSSetMisc) diff --git a/modules/extra/cs_sync.cpp b/modules/extra/cs_sync.cpp deleted file mode 100644 index 2937cc4fc..000000000 --- a/modules/extra/cs_sync.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - * - * (C) 2003-2011 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 ¶ms) - { - ChannelInfo *ci = cs_findchan(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 - { - for (CUserList::iterator it = ci->c->users.begin(), it_end = ci->c->users.end(); it != it_end; ++it) - chan_set_correct_modes((*it)->user, ci->c, 1); - - source.Reply(_("All user modes on \2%s\2 have been synced."), ci->name.c_str()); - } - } - - bool OnHelp(CommandSource &source, const Anope::string ¶ms) - { - 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, SUPPORTED), - commandcssync(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandcssync); - } -}; - -MODULE_INIT(CSSync) diff --git a/modules/extra/cs_tban.cpp b/modules/extra/cs_tban.cpp deleted file mode 100644 index 404279bef..000000000 --- a/modules/extra/cs_tban.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/* cs_tban.c - Bans the user for a given length of time - * - * (C) 2003-2011 Anope Team - * Contact us at team@anope.org - * - * Based on the original module by Rob - * Included in the Anope module pack since Anope 1.7.8 - * Anope Coder: Rob - * - * 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 Module *me; - -class TempBan : public CallBack -{ - private: - dynamic_reference chan; - Anope::string mask; - - public: - TempBan(time_t seconds, Channel *c, const Anope::string &banmask) : CallBack(me, seconds), chan(c), mask(banmask) { } - - void Tick(time_t ctime) - { - if (chan && chan->ci) - chan->RemoveMode(NULL, CMODE_BAN, mask); - } -}; - -static bool CanBanUser(CommandSource &source, Channel *c, User *u2) -{ - User *u = source.u; - ChannelInfo *ci = c->ci; - bool ok = false; - if (!ci->HasPriv(u, CA_BAN)) - source.Reply(ACCESS_DENIED); - else if (matches_list(c, u2, CMODE_EXCEPT)) - source.Reply(CHAN_EXCEPTED, u2->nick.c_str(), ci->name.c_str()); - else if (u2->IsProtected()) - source.Reply(ACCESS_DENIED); - else - ok = true; - - return ok; -} - -class CommandCSTBan : public Command -{ - public: - CommandCSTBan(Module *m) : Command(m, "TBAN", 3, 3) - { - this->SetDesc(_("Bans the user for a given length of time")); - this->SetSyntax(_("\037channel\037 \037nick\037 \037time\037")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - Channel *c = findchan(params[0]); - - const Anope::string &nick = params[1]; - const Anope::string &time = params[2]; - - User *u2; - if (!c) - source.Reply(CHAN_X_NOT_IN_USE, params[0].c_str()); - else if (!(u2 = finduser(nick))) - source.Reply(NICK_X_NOT_IN_USE, nick.c_str()); - else - if (CanBanUser(source, c, u2)) - { - Anope::string mask; - get_idealban(c->ci, u2, mask); - c->SetMode(NULL, CMODE_BAN, mask); - new TempBan(dotime(time), c, mask); - source.Reply(_("%s banned from %s, will auto-expire in %s"), mask.c_str(), c->name.c_str(), time.c_str()); - } - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->OnSyntaxError(source, ""); - source.Reply(" "); - source.Reply(_("Bans the given user from a channel for a specified length of\n" - "time. If the ban is removed before by hand, it will NOT be replaced.")); - - return true; - } -}; - -class CSTBan : public Module -{ - CommandCSTBan commandcstban; - - public: - CSTBan(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, SUPPORTED), - commandcstban(this) - { - this->SetAuthor("Anope"); - - me = this; - - ModuleManager::RegisterService(&commandcstban); - } -}; - -MODULE_INIT(CSTBan) diff --git a/modules/extra/hs_request.cpp b/modules/extra/hs_request.cpp deleted file mode 100644 index 7686794f5..000000000 --- a/modules/extra/hs_request.cpp +++ /dev/null @@ -1,424 +0,0 @@ -/* hs_request.c - Add request and activate functionality to HostServ, - * - * - * (C) 2003-2011 Anope Team - * Contact us at team@anope.org - * - * Based on the original module by Rob - * Included in the Anope module pack since Anope 1.7.11 - * Anope Coder: GeniusDex - * - * 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" -#include "memoserv.h" - -static bool HSRequestMemoUser = false; -static bool HSRequestMemoOper = false; - -void my_add_host_request(const Anope::string &nick, const Anope::string &vIdent, const Anope::string &vhost, const Anope::string &creator, time_t tmp_time); -void req_send_memos(CommandSource &source, const Anope::string &vIdent, const Anope::string &vHost); - -struct HostRequest -{ - Anope::string ident; - Anope::string host; - time_t time; -}; - -typedef std::map > RequestMap; -RequestMap Requests; - -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 ¶ms) - { - User *u = source.u; - - 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->UserLen) - { - source.Reply(HOST_SET_IDENTTOOLONG, Config->UserLen); - return; - } - else if (!ircd->vident) - { - 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->HostLen) - { - source.Reply(HOST_SET_TOOLONG, Config->HostLen); - return; - } - - if (!isValidHost(host, 3)) - { - source.Reply(HOST_SET_ERROR); - return; - } - - if (HSRequestMemoOper && Config->MSSendDelay > 0 && u && u->lastmemosend + Config->MSSendDelay > Anope::CurTime) - { - source.Reply(_("Please wait %d seconds before requesting a new vHost"), Config->MSSendDelay); - u->lastmemosend = Anope::CurTime; - return; - } - my_add_host_request(u->nick, user, host, u->nick, Anope::CurTime); - - source.Reply(_("Your vHost has been requested")); - req_send_memos(source, user, host); - Log(LOG_COMMAND, u, this, NULL) << "to request new vhost " << (!user.empty() ? user + "@" : "") << host; - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Request the given vHost to be actived 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 ¶ms) - { - User *u = source.u; - - const Anope::string &nick = params[0]; - - NickAlias *na = findnick(nick); - if (na) - { - RequestMap::iterator it = Requests.find(na->nick); - if (it != Requests.end()) - { - na->hostinfo.SetVhost(it->second->ident, it->second->host, u->nick, it->second->time); - FOREACH_MOD(I_OnSetVhost, OnSetVhost(na)); - - if (HSRequestMemoUser && memoserv) - memoserv->Send(Config->HostServ, 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, u, this, NULL) << "for " << na->nick << " for vhost " << (!it->second->ident.empty() ? it->second->ident + "@" : "") << it->second->host; - delete it->second; - Requests.erase(it); - } - else - source.Reply(_("No request for nick %s found."), nick.c_str()); - } - else - source.Reply(NICK_X_NOT_REGISTERED, nick.c_str()); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Activate the requested vHost for the given nick.")); - if (HSRequestMemoUser) - 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")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - - const Anope::string &nick = params[0]; - const Anope::string &reason = params.size() > 1 ? params[1] : ""; - - RequestMap::iterator it = Requests.find(nick); - if (it != Requests.end()) - { - delete it->second; - Requests.erase(it); - - if (HSRequestMemoUser && 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(Config->HostServ, nick, message, true); - } - - source.Reply(_("vHost for %s has been rejected"), nick.c_str()); - Log(LOG_COMMAND, u, this, NULL) << "to reject vhost for " << nick << " (" << (!reason.empty() ? reason : "") << ")"; - } - else - source.Reply(_("No request for nick %s found."), nick.c_str()); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Reject the requested vHost for the given nick.")); - if (HSRequestMemoUser) - source.Reply(_("A memo informing the user will also be sent.")); - - return true; - } -}; - -class HSListBase : public Command -{ - protected: - void DoList(CommandSource &source) - { - int counter = 1; - int from = 0, to = 0; - unsigned display_counter = 0; - - for (RequestMap::iterator it = Requests.begin(), it_end = Requests.end(); it != it_end; ++it) - { - HostRequest *hr = it->second; - if (((counter >= from && counter <= to) || (!from && !to)) && display_counter < Config->NSListMax) - { - ++display_counter; - if (!hr->ident.empty()) - source.Reply(_("#%d Nick:\002%s\002, vhost:\002%s\002@\002%s\002 (%s - %s)"), counter, it->first.c_str(), hr->ident.c_str(), hr->host.c_str(), it->first.c_str(), do_strftime(hr->time).c_str()); - else - source.Reply(_("#%d Nick:\002%s\002, vhost:\002%s\002 (%s - %s)"), counter, it->first.c_str(), hr->host.c_str(), it->first.c_str(), do_strftime(hr->time).c_str()); - } - ++counter; - } - source.Reply(_("Displayed all records (Count: \002%d\002)"), display_counter); - - return; - } - public: - HSListBase(Module *creator, const Anope::string &cmd, int min, int max) : Command(creator, cmd, min, max) - { - } -}; - -class CommandHSWaiting : public HSListBase -{ - public: - CommandHSWaiting(Module *creator) : HSListBase(creator, "hostserv/waiting", 0, 0) - { - this->SetDesc(_("Retrieves the vhost requests")); - this->SetSyntax(""); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - return this->DoList(source); - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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; - - public: - HSRequest(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, SUPPORTED), - commandhsrequest(this), commandhsactive(this), commandhsreject(this), commandhswaiting(this) - { - this->SetAuthor("Anope"); - - ModuleManager::RegisterService(&commandhsrequest); - ModuleManager::RegisterService(&commandhsactive); - ModuleManager::RegisterService(&commandhsreject); - ModuleManager::RegisterService(&commandhswaiting); - - Implementation i[] = { I_OnDelNick, I_OnDatabaseRead, I_OnDatabaseWrite, I_OnReload }; - ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); - - this->OnReload(); - } - - ~HSRequest() - { - /* Clean up all open host requests */ - while (!Requests.empty()) - { - delete Requests.begin()->second; - Requests.erase(Requests.begin()); - } - } - - void OnDelNick(NickAlias *na) - { - RequestMap::iterator it = Requests.find(na->nick); - - if (it != Requests.end()) - { - delete it->second; - Requests.erase(it); - } - } - - EventReturn OnDatabaseRead(const std::vector ¶ms) - { - if (params[0].equals_ci("HS_REQUEST") && params.size() >= 5) - { - Anope::string vident = params[2].equals_ci("(null)") ? "" : params[2]; - my_add_host_request(params[1], vident, params[3], params[1], params[4].is_pos_number_only() ? convertTo(params[4]) : 0); - - return EVENT_STOP; - } - - return EVENT_CONTINUE; - } - - void OnDatabaseWrite(void (*Write)(const Anope::string &)) - { - for (RequestMap::iterator it = Requests.begin(), it_end = Requests.end(); it != it_end; ++it) - { - HostRequest *hr = it->second; - std::stringstream buf; - buf << "HS_REQUEST " << it->first << " " << (hr->ident.empty() ? "(null)" : hr->ident) << " " << hr->host << " " << hr->time; - Write(buf.str()); - } - } - - void OnReload() - { - ConfigReader config; - HSRequestMemoUser = config.ReadFlag("hs_request", "memouser", "no", 0); - HSRequestMemoOper = config.ReadFlag("hs_request", "memooper", "no", 0); - - Log(LOG_DEBUG) << "[hs_request] Set config vars: MemoUser=" << HSRequestMemoUser << " MemoOper=" << HSRequestMemoOper; - } -}; - -void req_send_memos(CommandSource &source, const Anope::string &vIdent, const Anope::string &vHost) -{ - Anope::string host; - std::list >::iterator it, it_end; - - if (!vIdent.empty()) - host = vIdent + "@" + vHost; - else - host = vHost; - - if (HSRequestMemoOper == 1 && memoserv) - for (unsigned i = 0; i < Config->Opers.size(); ++i) - { - Oper *o = Config->Opers[i]; - - NickAlias *na = findnick(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.u->GetMask().c_str()); - - memoserv->Send(Config->HostServ, na->nick, message, true); - } -} - -void my_add_host_request(const Anope::string &nick, const Anope::string &vIdent, const Anope::string &vhost, const Anope::string &creator, time_t tmp_time) -{ - HostRequest *hr = new HostRequest; - hr->ident = vIdent; - hr->host = vhost; - hr->time = tmp_time; - RequestMap::iterator it = Requests.find(nick); - if (it != Requests.end()) - { - delete it->second; - Requests.erase(it); - } - Requests.insert(std::make_pair(nick, hr)); -} - -void my_load_config() -{ - ConfigReader config; - HSRequestMemoUser = config.ReadFlag("hs_request", "memouser", "no", 0); - HSRequestMemoOper = config.ReadFlag("hs_request", "memooper", "no", 0); - - Log(LOG_DEBUG) << "[hs_request] Set config vars: MemoUser=" << HSRequestMemoUser << " MemoOper=" << HSRequestMemoOper; -} - -MODULE_INIT(HSRequest) diff --git a/modules/extra/language/CMakeLists.txt b/modules/extra/language/CMakeLists.txt deleted file mode 100644 index af4dd90a6..000000000 --- a/modules/extra/language/CMakeLists.txt +++ /dev/null @@ -1,38 +0,0 @@ -# Only do this if gettext is installed -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) - - foreach(LANG_PO ${LANG_SRCS_PO}) - # Get the domain for this language file - string(LENGTH ${LANG_PO} LANG_PO_LENGTH) - math(EXPR DOMAIN_LENGTH "${LANG_PO_LENGTH} - 9") - string(SUBSTRING ${LANG_PO} 0 ${DOMAIN_LENGTH} LANG_DOMAIN) - - # Get the language for this language file - math(EXPR DOMAIN_LENGTH "${LANG_PO_LENGTH} - 8") - string(SUBSTRING ${LANG_PO} ${DOMAIN_LENGTH} 5 LANG_LANG) - - # Get the .mo file name - string(REGEX REPLACE "\\.po$" ".mo" LANG_MO ${LANG_PO}) - # Add the .mo file to a list for use later with add_custom_target - set(LANG_SRCS_MO ${LANG_SRCS_MO} ${CMAKE_CURRENT_BINARY_DIR}/${LANG_MO}) - # Run msgfmt on the language file, depends on the .po file - add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${LANG_MO} - COMMAND ${GETTEXT_MSGFMT_EXECUTABLE} -c ${CMAKE_CURRENT_SOURCE_DIR}/${LANG_PO} -o ${CMAKE_CURRENT_BINARY_DIR}/${LANG_MO} - MAIN_DEPENDENCY ${LANG_PO} - ) - # Add to cpack ignored files if not on Windows. - if(NOT WIN32) - add_to_cpack_ignored_files("${LANG_MO}") - endif(NOT WIN32) - - # Install the new language file - install(CODE "FILE(MAKE_DIRECTORY \${CMAKE_INSTALL_PREFIX}/data/languages/${LANG_LANG}/LC_MESSAGES/)") - install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${LANG_MO} DESTINATION data/languages/${LANG_LANG}/LC_MESSAGES RENAME ${LANG_DOMAIN}.mo PERMISSIONS ${PERMS}) - endforeach(LANG_PO) - - # Generate languages, depends on the mo files - add_custom_target(module_language DEPENDS ${LANG_SRCS_MO}) -endif(GETTEXT_FOUND) diff --git a/modules/extra/language/cs_appendtopic.de_DE.po b/modules/extra/language/cs_appendtopic.de_DE.po deleted file mode 100644 index 2305709c1..000000000 --- a/modules/extra/language/cs_appendtopic.de_DE.po +++ /dev/null @@ -1,37 +0,0 @@ -# German translations for cs_appendtopic -# German messages for cs_appendtopic -# Copyright (C) 2011 Anope Team -# This file is distributed under the same license as Anope IRC Services -# Adam , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: Anope\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-06-17 13:49-0400\n" -"PO-Revision-Date: 2010-09-25 20:57-0400\n" -"Last-Translator: Adam \n" -"Language-Team: German\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=ISO-8859-1\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" - -#: ../cs_appendtopic.cpp:50 -msgid "Add text to a channels topic" -msgstr "Fügt einen Text zu einem Channel-Topic hinzu." - -#: ../cs_appendtopic.cpp:90 ../cs_appendtopic.cpp:101 -msgid "Syntax: APPENDTOPIC channel text" -msgstr "Syntax: APPENDTOPIC Channel Text" - -#: ../cs_appendtopic.cpp:92 -msgid "" -"This command allows users to append text to a currently set\n" -"channel topic. When TOPICLOCK is on, the topic is updated and\n" -"the new, updated topic is locked." -msgstr "" -"Dieser Befehl erlaubt Benutzern, einen Text zu dem vorhandenen Channel-" -"Topic\n" -"hinzuzufügen. Wenn TOPICLOCK gesetzt ist, wird das Topic aktualisiert\n" -"und das neue, aktualisierte Topic wird gesperrt." diff --git a/modules/extra/language/cs_appendtopic.it_IT.po b/modules/extra/language/cs_appendtopic.it_IT.po deleted file mode 100644 index 43620d0b2..000000000 --- a/modules/extra/language/cs_appendtopic.it_IT.po +++ /dev/null @@ -1,39 +0,0 @@ -# Italian translations for cs_appendtopic -# Traduzioni italiane per il cs_appendtopic -# Copyright (C) 2011 Anope Team -# This file is distributed under the same license as Anope IRC Services -# Adam , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: Anope\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-06-17 13:49-0400\n" -"PO-Revision-Date: 2010-09-25 21:04-0400\n" -"Last-Translator: Adam \n" -"Language-Team: Italian\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=ISO-8859-1\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" - -#: ../cs_appendtopic.cpp:50 -#, fuzzy -msgid "Add text to a channels topic" -msgstr " APPENDTOPIC Aggiunge del testo al topic di un canale" - -#: ../cs_appendtopic.cpp:90 ../cs_appendtopic.cpp:101 -msgid "Syntax: APPENDTOPIC channel text" -msgstr "Sintassi: APPENDTOPIC canale testo" - -#: ../cs_appendtopic.cpp:92 -msgid "" -"This command allows users to append text to a currently set\n" -"channel topic. When TOPICLOCK is on, the topic is updated and\n" -"the new, updated topic is locked." -msgstr "" -"Questo comando permette agli utenti di aggiungere del testo ad un topic di " -"un canale\n" -"già impostato. Se TOPICLOCK è attivato, il topic viene aggiornato e il nuovo " -"topic\n" -"viene bloccato." diff --git a/modules/extra/language/cs_appendtopic.nl_NL.po b/modules/extra/language/cs_appendtopic.nl_NL.po deleted file mode 100644 index 21d81eaba..000000000 --- a/modules/extra/language/cs_appendtopic.nl_NL.po +++ /dev/null @@ -1,38 +0,0 @@ -# Dutch translations for cs_appendtopic -# Engelse vertalingen voor het cs_appendtopic -# Copyright (C) 2011 Anope Team -# This file is distributed under the same license as Anope IRC Services -# Adam , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: Anope\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-06-17 13:49-0400\n" -"PO-Revision-Date: 2010-09-25 20:55-0400\n" -"Last-Translator: Adam \n" -"Language-Team: Dutch\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=ISO-8859-1\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" - -#: ../cs_appendtopic.cpp:50 -#, fuzzy -msgid "Add text to a channels topic" -msgstr " APPENDTOPIC Voeg tekst aan een kanaal onderwerp toe" - -#: ../cs_appendtopic.cpp:90 ../cs_appendtopic.cpp:101 -msgid "Syntax: APPENDTOPIC channel text" -msgstr "Gebruik: APPENDTOPIC kanaal tekst" - -#: ../cs_appendtopic.cpp:92 -msgid "" -"This command allows users to append text to a currently set\n" -"channel topic. When TOPICLOCK is on, the topic is updated and\n" -"the new, updated topic is locked." -msgstr "" -"Dit command stelt gebruikers in staat om text toe te voegen\n" -"achter het huidige onderwerp van een kanaal. Als TOPICLOCK aan\n" -"staat, zal het onderwerp worden bijgewerkt en zal het nieuwe,\n" -"bijgewerkte topic worden geforceerd." diff --git a/modules/extra/language/cs_appendtopic.pt_PT.po b/modules/extra/language/cs_appendtopic.pt_PT.po deleted file mode 100644 index e97d94a90..000000000 --- a/modules/extra/language/cs_appendtopic.pt_PT.po +++ /dev/null @@ -1,36 +0,0 @@ -# Portuguese translations for cs_appendtopic -# Copyright (C) 2011 Anope Team -# This file is distributed under the same license as Anope IRC Services -# Adam , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: Anope\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-06-17 13:49-0400\n" -"PO-Revision-Date: 2010-09-25 21:00-0400\n" -"Last-Translator: Adam \n" -"Language-Team: Portuguese\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=ISO-8859-1\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" - -#: ../cs_appendtopic.cpp:50 -#, fuzzy -msgid "Add text to a channels topic" -msgstr " APPENDTOPIC Adiciona texto ao tópico de um canal" - -#: ../cs_appendtopic.cpp:90 ../cs_appendtopic.cpp:101 -msgid "Syntax: APPENDTOPIC channel text" -msgstr "Sintaxe: APPENDTOPIC canal texto" - -#: ../cs_appendtopic.cpp:92 -msgid "" -"This command allows users to append text to a currently set\n" -"channel topic. When TOPICLOCK is on, the topic is updated and\n" -"the new, updated topic is locked." -msgstr "" -"Este comando permite aos usuários anexar texto a um tópico de canal\n" -"já definido. Quando TOPICLOCK está ativado, o tópico é atualizado e\n" -"o novo tópico é travado." diff --git a/modules/extra/language/cs_appendtopic.ru_RU.po b/modules/extra/language/cs_appendtopic.ru_RU.po deleted file mode 100644 index cb5566a8c..000000000 --- a/modules/extra/language/cs_appendtopic.ru_RU.po +++ /dev/null @@ -1,41 +0,0 @@ -# Russian translations for cs_appendtopic -# ?????????? ???????? ??? ?????? cs_appendtopic -# Copyright (C) 2011 Anope Team -# This file is distributed under the same license as Anope IRC Services -# Adam , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: Anope\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-06-17 13:49-0400\n" -"PO-Revision-Date: 2010-09-25 21:02-0400\n" -"Last-Translator: Adam \n" -"Language-Team: Russian\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=ISO-8859-1\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%" -"10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" - -#: ../cs_appendtopic.cpp:50 -#, fuzzy -msgid "Add text to a channels topic" -msgstr " APPENDTOPIC Äîáàâëÿåò òåêñò ê òîïèêó êàíàëà" - -#: ../cs_appendtopic.cpp:90 ../cs_appendtopic.cpp:101 -msgid "Syntax: APPENDTOPIC channel text" -msgstr "Ñèíòàêñèñ: APPENDTOPIC #êàíàë òåêñò" - -#: ../cs_appendtopic.cpp:92 -msgid "" -"This command allows users to append text to a currently set\n" -"channel topic. When TOPICLOCK is on, the topic is updated and\n" -"the new, updated topic is locked." -msgstr "" -"Äàííàÿ êîìàíäà ïîçâîëÿåò äîáàâèòü òåêñò ê òîïèêó, êîòîðûé óñòàíîâëåí íà " -"óêàçàííîì\n" -"êàíàëå. Åñëè àêòèâèðîâàí ðåæèì TOPICLOCK, òîïèê áóäåò îáíîâëåí è " -"çàáëîêèðîâàí.\n" -"Ïðèìå÷àíèå: òåêñò áóäåò ÄÎÁÀÂËÅÍ ê òîïèêó, òî åñòü ñòàðûé òîïèê óäàëåí ÍÅ " -"ÁÓÄÅÒ." diff --git a/modules/extra/language/cs_enforce.de_DE.po b/modules/extra/language/cs_enforce.de_DE.po deleted file mode 100644 index 5e8ccd94b..000000000 --- a/modules/extra/language/cs_enforce.de_DE.po +++ /dev/null @@ -1,86 +0,0 @@ -# German translations for cs_enforce -# German messages for cs_enforce -# Copyright (C) 2011 Anope Team -# This file is distributed under the same license as Anope IRC Services -# Adam , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: Anope\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-06-17 13:49-0400\n" -"PO-Revision-Date: 2010-09-25 21:12-0400\n" -"Last-Translator: Adam \n" -"Language-Team: German\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=ISO-8859-1\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" - -#: ../cs_enforce.cpp:127 -#, fuzzy -msgid "Enforce various channel modes and set options" -msgstr " ENFORCE Erzwingt verschieden Modes und SET Optionen" - -#: ../cs_enforce.cpp:180 -msgid "" -"Enforce various channel modes and set options. The channel\n" -"option indicates what channel to enforce the modes and options\n" -"on. The what option indicates what modes and options to\n" -"enforce, and can be any of SET, SECUREOPS, RESTRICTED, MODES,\n" -"or +R. When left out, it defaults to SET.\n" -" \n" -"If what is SET, it will enforce SECUREOPS and RESTRICTED\n" -"on the users currently in the channel, if they are set. Give\n" -"SECUREOPS to enforce the SECUREOPS option, even if it is not\n" -"enabled. Use RESTRICTED to enfore the RESTRICTED option, also\n" -"if it's not enabled." -msgstr "" -"Erzwingt verschieden Modes und SET Optionen. Die Channel\n" -"Option zeigt dir den Channel an, indem Modes und Optionen\n" -"zu erzwingen sind. Die was Option zeigt dir welche Modes\n" -"und Optionen zu erzwingen sind. Die können nur SET, SECUREOPS,\n" -"RESTRICTED, MODES oder +R sein.Default ist SET.\n" -" \n" -"Wenn was SET ist, wird SECUREOPS und RESTRICTED\n" -"auf die User die z.Z.in Channel sind erzwungen, wenn sie AN sind.\n" -"Benutze SECUREOPS oder RESTRICTED , um die Optionen einzeln\n" -"zu erzwingen, also wenn sie nicht eingeschaltet sind." - -#: ../cs_enforce.cpp:147 ../cs_enforce.cpp:152 ../cs_enforce.cpp:157 -#: ../cs_enforce.cpp:162 ../cs_enforce.cpp:167 -#, c-format -msgid "Enforced %s" -msgstr "Erzwungen %s" - -#: ../cs_enforce.cpp:193 -msgid "" -"If what is MODES, it will enforce channelmode +R if it is\n" -"set. If +R is specified for what, the +R channelmode will\n" -"also be enforced, but even if it is not set. If it is not set,\n" -"users will be banned to ensure they don't just rejoin." -msgstr "" -"Wenn was MODES ist, wird das ChannelMode +R erzwungen\n" -"falls an. Wenn was +R ist, wird +R erzwungen aber eben\n" -"wenn noch nicht als Channel-Mode ist. Wenn +R noch nicht als\n" -"Channel-Mode war werden alle User aus den Channel gebannt um\n" -"sicher zu sein das sie nicht rejoinen." - -#: ../cs_enforce.cpp:198 -msgid "" -"If what is MODES, nothing will be enforced, since it would\n" -"enforce modes that the current ircd does not support. If +R is\n" -"specified for what, an equalivant of channelmode +R on\n" -"other ircds will be enforced. All users that are in the channel\n" -"but have not identified for their nickname will be kicked and\n" -"banned from the channel." -msgstr "" -"Wenn was MODES ist, wird nichts erzwungen weil es MODES seine\n" -"können die dein IRCD nicht unterstützt. Wenn was +R ist\n" -"oder ein Modes was auf ein anderen IRCD gleich +R ist, wird es\n" -"erzwungen. Alle User die nicht für deren Nicknamen identifiziert\n" -"sind werden aus den Channel gekickt und gebannt." - -#: ../cs_enforce.cpp:178 ../cs_enforce.cpp:210 -msgid "Syntax: ENFORCE channel [what]" -msgstr "Syntax: ENFORCE Channel [was]" diff --git a/modules/extra/language/cs_enforce.it_IT.po b/modules/extra/language/cs_enforce.it_IT.po deleted file mode 100644 index 4721e823f..000000000 --- a/modules/extra/language/cs_enforce.it_IT.po +++ /dev/null @@ -1,86 +0,0 @@ -# Italian translations for cs_enforce -# Traduzioni italiane per il cs_enforce -# Copyright (C) 2011 Anope Team -# This file is distributed under the same license as Anope IRC Services -# Adam , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: Anope\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-06-17 13:49-0400\n" -"PO-Revision-Date: 2010-09-25 21:55-0400\n" -"Last-Translator: Adam \n" -"Language-Team: Italian\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=ISO-8859-1\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" - -#: ../cs_enforce.cpp:127 -#, fuzzy -msgid "Enforce various channel modes and set options" -msgstr " ENFORCE Forza diversi modi di canale ed opzioni SET" - -#: ../cs_enforce.cpp:180 -msgid "" -"Enforce various channel modes and set options. The channel\n" -"option indicates what channel to enforce the modes and options\n" -"on. The what option indicates what modes and options to\n" -"enforce, and can be any of SET, SECUREOPS, RESTRICTED, MODES,\n" -"or +R. When left out, it defaults to SET.\n" -" \n" -"If what is SET, it will enforce SECUREOPS and RESTRICTED\n" -"on the users currently in the channel, if they are set. Give\n" -"SECUREOPS to enforce the SECUREOPS option, even if it is not\n" -"enabled. Use RESTRICTED to enfore the RESTRICTED option, also\n" -"if it's not enabled." -msgstr "" -"Forza diversi modi di canale ed opzioni SET. Il parametro canale\n" -"indica il canale sul quale forzare i modi e le opzioni. Il parametro\n" -"cosa indica i modi e le opzioni da forzare, e possono essere\n" -"qualsiasi delle opzioni SET, SECUREOPS, RESTRICTED, MODES, o +R.\n" -"Se non specificato, viene sottointeso SET.\n" -" \n" -"Se cosa è SET, forzerà SECUREOPS e RESTRICTED sugli utenti\n" -"attualmente nel canale, se sono impostati. Specifica SECUREOPS per\n" -"forzare l'opzione SECUREOPS, anche se non è attivata. Specifica\n" -"RESTRICTED per forzare l'opzione RESTRICTED, anche se non è\n" -"attivata." - -#: ../cs_enforce.cpp:147 ../cs_enforce.cpp:152 ../cs_enforce.cpp:157 -#: ../cs_enforce.cpp:162 ../cs_enforce.cpp:167 -#, c-format -msgid "Enforced %s" -msgstr "Forzato %s" - -#: ../cs_enforce.cpp:193 -msgid "" -"If what is MODES, it will enforce channelmode +R if it is\n" -"set. If +R is specified for what, the +R channelmode will\n" -"also be enforced, but even if it is not set. If it is not set,\n" -"users will be banned to ensure they don't just rejoin." -msgstr "" -"Se cosa è MODES, forzerà il modo del canale +R se è impostato.\n" -"Se +R è specificato per cosa, il modo del canale +R verrà\n" -"forzato, anche se non è impostato. Se non è impostato, gli utenti\n" -"verranno bannati per assicurare che non rientrino semplicemente." - -#: ../cs_enforce.cpp:198 -msgid "" -"If what is MODES, nothing will be enforced, since it would\n" -"enforce modes that the current ircd does not support. If +R is\n" -"specified for what, an equalivant of channelmode +R on\n" -"other ircds will be enforced. All users that are in the channel\n" -"but have not identified for their nickname will be kicked and\n" -"banned from the channel." -msgstr "" -"Se cosa è MODES, niente verrà forzato, siccome forzerebbe\n" -"dei modi che l'ircd in uso non supporterebbe. Se +R è specificato\n" -"per cosa, un modo equivalente a +R sui altri ircd verrà\n" -"forzato. Tutti gli utenti presenti nel canale ma non identificati\n" -"per il loro nickname verranno bannati ed espulsi dal canale." - -#: ../cs_enforce.cpp:178 ../cs_enforce.cpp:210 -msgid "Syntax: ENFORCE channel [what]" -msgstr "Sintassi: ENFORCE canale [cosa]" diff --git a/modules/extra/language/cs_enforce.nl_NL.po b/modules/extra/language/cs_enforce.nl_NL.po deleted file mode 100644 index 935e8dce7..000000000 --- a/modules/extra/language/cs_enforce.nl_NL.po +++ /dev/null @@ -1,89 +0,0 @@ -# Dutch translations for cs_enforce -# Engelse vertalingen voor het cs_enforce -# Copyright (C) 2011 Anope Team -# This file is distributed under the same license as the Anope IRC Services -# Adam , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: Anope\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-06-17 13:49-0400\n" -"PO-Revision-Date: 2010-09-25 21:07-0400\n" -"Last-Translator: Adam \n" -"Language-Team: Dutch\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=ISO-8859-1\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" - -#: ../cs_enforce.cpp:127 -#, fuzzy -msgid "Enforce various channel modes and set options" -msgstr " ENFORCE Forceer enkele kanaalmodes en set-opties" - -#: ../cs_enforce.cpp:180 -msgid "" -"Enforce various channel modes and set options. The channel\n" -"option indicates what channel to enforce the modes and options\n" -"on. The what option indicates what modes and options to\n" -"enforce, and can be any of SET, SECUREOPS, RESTRICTED, MODES,\n" -"or +R. When left out, it defaults to SET.\n" -" \n" -"If what is SET, it will enforce SECUREOPS and RESTRICTED\n" -"on the users currently in the channel, if they are set. Give\n" -"SECUREOPS to enforce the SECUREOPS option, even if it is not\n" -"enabled. Use RESTRICTED to enfore the RESTRICTED option, also\n" -"if it's not enabled." -msgstr "" -"Forceer enkele kannalmodes en set-opties. De kanaal optie\n" -"geeft aan op welk kanaal de modes en opties geforceerd moeten\n" -"worden. De wat optie geeft aan welke modes en opties\n" -"geforceerd moeten worden; dit kan SET, SECUREOPS, RESTRICTED,\n" -"MODES, of +R zijn. Indien weggelaten is dit standaard SET.\n" -" \n" -"Als er voor wat SET wordt ingevuld, zullen SECUREOPS en\n" -"RESTRICTED geforceerd worden op de gebruikers in het kanaal,\n" -"maar alleen als die opties aangezet zijn voor het kanaal. Als\n" -"SECUREOPS of RESTRICTED wordt gegeven voor wat zal die optie\n" -"altijd geforceerd worden, ook als die niet is aangezet." - -#: ../cs_enforce.cpp:147 ../cs_enforce.cpp:152 ../cs_enforce.cpp:157 -#: ../cs_enforce.cpp:162 ../cs_enforce.cpp:167 -#, c-format -msgid "Enforced %s" -msgstr "" - -#: ../cs_enforce.cpp:193 -msgid "" -"If what is MODES, it will enforce channelmode +R if it is\n" -"set. If +R is specified for what, the +R channelmode will\n" -"also be enforced, but even if it is not set. If it is not set,\n" -"users will be banned to ensure they don't just rejoin." -msgstr "" -"Als er voor wat MODES wordt ingevuld, zal kanaalmode +R worden\n" -"geforceerd, als die op het kanaal aan staat. Als +R wordt ingevuld,\n" -"zal kanaalmode +R worden geforceerd, maar ook als die niet aanstaat voor het " -"kanaal. Als +R niet aan staat, zullen alle ook\n" -"gebanned worden om te zorgen dat ze niet opnieuw het kanaal binnen\n" -"kunnen komen." - -#: ../cs_enforce.cpp:198 -msgid "" -"If what is MODES, nothing will be enforced, since it would\n" -"enforce modes that the current ircd does not support. If +R is\n" -"specified for what, an equalivant of channelmode +R on\n" -"other ircds will be enforced. All users that are in the channel\n" -"but have not identified for their nickname will be kicked and\n" -"banned from the channel." -msgstr "" -"Als er voor wat MODES wordt ingevuld, zal er niks gebeuren.\n" -"Normaal gesproken wordt er een kanaalmode geforceerd die op deze\n" -"server niet ondersteund wordt. Als +R wordt ingevuld voor wat\n" -"zullen alle gebruikers die in het kanaal zitten maar zich niet\n" -"hebben geidentificeerd voor hun nick uit het kanaal gekicked en\n" -"verbannen worden." - -#: ../cs_enforce.cpp:178 ../cs_enforce.cpp:210 -msgid "Syntax: ENFORCE channel [what]" -msgstr "Syntax: ENFORCE kanaal [wat]" diff --git a/modules/extra/language/cs_enforce.pt_PT.po b/modules/extra/language/cs_enforce.pt_PT.po deleted file mode 100644 index 955ea87aa..000000000 --- a/modules/extra/language/cs_enforce.pt_PT.po +++ /dev/null @@ -1,88 +0,0 @@ -# Portuguese translations for cs_enforce -# Copyright (C) 2011 Anope Team -# This file is distributed under the same license as the Anope IRC Services -# Adam , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: Anope\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-06-17 13:49-0400\n" -"PO-Revision-Date: 2010-09-25 21:24-0400\n" -"Last-Translator: Adam \n" -"Language-Team: Portuguese\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=ISO-8859-1\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" - -#: ../cs_enforce.cpp:127 -#, fuzzy -msgid "Enforce various channel modes and set options" -msgstr "" -" ENFORCE Verifica o cumprimento de vários modos de canal e opções " -"ajustadas" - -#: ../cs_enforce.cpp:180 -msgid "" -"Enforce various channel modes and set options. The channel\n" -"option indicates what channel to enforce the modes and options\n" -"on. The what option indicates what modes and options to\n" -"enforce, and can be any of SET, SECUREOPS, RESTRICTED, MODES,\n" -"or +R. When left out, it defaults to SET.\n" -" \n" -"If what is SET, it will enforce SECUREOPS and RESTRICTED\n" -"on the users currently in the channel, if they are set. Give\n" -"SECUREOPS to enforce the SECUREOPS option, even if it is not\n" -"enabled. Use RESTRICTED to enfore the RESTRICTED option, also\n" -"if it's not enabled." -msgstr "" -"Verifica o cumprimento de vários modos de canal e opções ajustadas.\n" -"O campo canal indica qual canal deve ter os modos e opções verificadas\n" -"O campo opção indica quais modos e opções devem ser verificadas,\n" -"e pode ser: SET, SECUREOPS, RESTRICTED, MODES ou +R\n" -"Quando deixado em branco, o padrão é SET.\n" -" \n" -"Se opção for SET, serão verificadas as opções SECUREOPS e RESTRICTED\n" -"para usuários que estiverem no canal, caso elas estejam ativadas. Use\n" -"SECUREOPS para verificar a opção SECUREOPS, mesmo que ela não esteja " -"ativada\n" -"Use RESTRICTED para verificar a opção RESTRICTED, mesmo que ela não esteja\n" -"ativada." - -#: ../cs_enforce.cpp:147 ../cs_enforce.cpp:152 ../cs_enforce.cpp:157 -#: ../cs_enforce.cpp:162 ../cs_enforce.cpp:167 -#, c-format -msgid "Enforced %s" -msgstr "Verificado %s" - -#: ../cs_enforce.cpp:193 -msgid "" -"If what is MODES, it will enforce channelmode +R if it is\n" -"set. If +R is specified for what, the +R channelmode will\n" -"also be enforced, but even if it is not set. If it is not set,\n" -"users will be banned to ensure they don't just rejoin." -msgstr "" -"Se opção for MODES, será verificado o modo de canal +R caso ele\n" -"esteja ativado. Se +R for especificado para opção, o modo de canal\n" -"+R também será verificado, mesmo que ele não esteja ativado. Se ele não\n" -"estiver ativado, os usuários serão banidos para evitar que reentrem no canal." - -#: ../cs_enforce.cpp:198 -msgid "" -"If what is MODES, nothing will be enforced, since it would\n" -"enforce modes that the current ircd does not support. If +R is\n" -"specified for what, an equalivant of channelmode +R on\n" -"other ircds will be enforced. All users that are in the channel\n" -"but have not identified for their nickname will be kicked and\n" -"banned from the channel." -msgstr "" -"Se opção for MODES, nada será verificado, visto que isto poderia\n" -"verificar modos que o IRCd atual não suporta. Se +R for especificado\n" -"para opção, um equivalente ao modo de canal +R em outros IRCds\n" -"será verificado. Todos os usuários que estão no canal, mas não estejam\n" -"identificados para seus nicks serão kickados e banidos do canal." - -#: ../cs_enforce.cpp:178 ../cs_enforce.cpp:210 -msgid "Syntax: ENFORCE channel [what]" -msgstr "Sintaxe: ENFORCE canal [opção]" diff --git a/modules/extra/language/cs_enforce.ru_RU.po b/modules/extra/language/cs_enforce.ru_RU.po deleted file mode 100644 index 2beef37cd..000000000 --- a/modules/extra/language/cs_enforce.ru_RU.po +++ /dev/null @@ -1,90 +0,0 @@ -# Russian translations for cs_enforce -# ?????????? ???????? ??? ?????? cs_enforce -# Copyright (C) 2011 Anope Team -# This file is distributed under the same license as Anope IRC Services -# Adam , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: Anope\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-06-17 13:49-0400\n" -"PO-Revision-Date: 2010-09-25 21:38-0400\n" -"Last-Translator: Adam \n" -"Language-Team: Russian\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=ISO-8859-1\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%" -"10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" - -#: ../cs_enforce.cpp:127 -#, fuzzy -msgid "Enforce various channel modes and set options" -msgstr "" -" ENFORCE Ïåðåïðîâåðêà è óñòàíîâêà ðàçëè÷íûõ ðåæèìîâ è îïöèé êàíàëà" - -#: ../cs_enforce.cpp:180 -msgid "" -"Enforce various channel modes and set options. The channel\n" -"option indicates what channel to enforce the modes and options\n" -"on. The what option indicates what modes and options to\n" -"enforce, and can be any of SET, SECUREOPS, RESTRICTED, MODES,\n" -"or +R. When left out, it defaults to SET.\n" -" \n" -"If what is SET, it will enforce SECUREOPS and RESTRICTED\n" -"on the users currently in the channel, if they are set. Give\n" -"SECUREOPS to enforce the SECUREOPS option, even if it is not\n" -"enabled. Use RESTRICTED to enfore the RESTRICTED option, also\n" -"if it's not enabled." -msgstr "" -"Ïåðåïðîâåðêà è óñòàíîâêà ðàçëè÷íûõ ðåæèìîâ è îïöèé êàíàëà.\n" -"Ïàðàìåòð óêàçûâàåò êàêèå îïöèè èëè ðåæèìû êàíàëà äîëæíû áûòü\n" -"ïåðåïðîâåðåíû.  êà÷åñòâå ïàðàìåòðà ìîãóò áûòü óêàçàíû: SET, SECUREOPS,\n" -"RESTRICTED, MODES, èëè +R. Åñëè ïàðàìåòð íå óêàçàí, ïî-óìîë÷àíèþ áóäåò SET.\n" -" \n" -"Åñëè â êà÷åñòâå ïàðàìåòðà óêàçàíî SET, áóäóò ïåðåïðîâåðåíû îïöèè\n" -"SECUREOPS è RESTRICTED îòíîñèòåëüíî ïîëüçîâàòåëåé íà óêàçàííîì êàíàëå\n" -"(ïðè óñëîâèè, ÷òî îïöèè âêëþ÷åíû). Îòäåëüíî óêàçàííûé ïàðàìåòð SECUREOPS\n" -"ïðèìåíèò îïöèþ SECUREOPS (äàæå åñëè îíà ÍÅ óñòàíîâëåíà). Ïàðàìåòð\n" -"RESTRICTED ïðèìåíèò îïöèþ RESTRICTED (äàæå åñëè îíà ÍÅ óñòàíîâëåíà)" - -#: ../cs_enforce.cpp:147 ../cs_enforce.cpp:152 ../cs_enforce.cpp:157 -#: ../cs_enforce.cpp:162 ../cs_enforce.cpp:167 -#, c-format -msgid "Enforced %s" -msgstr "Ïåðåïðîâåðåíî: %s" - -#: ../cs_enforce.cpp:193 -msgid "" -"If what is MODES, it will enforce channelmode +R if it is\n" -"set. If +R is specified for what, the +R channelmode will\n" -"also be enforced, but even if it is not set. If it is not set,\n" -"users will be banned to ensure they don't just rejoin." -msgstr "" -"Åñëè â êà÷åñòâå ïàðàìåòðà óêàçàíî MODES, áóäåò ïåðåïðîâåðåí ðåæèì +R\n" -"(åñëè îí óñòàíîâëåí). Îòäåëüíî óêàçàííûé ïàðàìåòð +R ïðèìåíèò\n" -"êàíàëüíûé ðåæèì +R, äàæå åñëè îí íå óñòàíîâëåí, è çàáàíèò âñåõ " -"ïîëüçîâàòåëåé,\n" -"êîòîðûå íå èäåíòèôèöèðîâàëèñü ê ñâîåìó íèêó èëè íå èìåþò çàðåãèñòðèðîâàííîãî " -"íèêà." - -#: ../cs_enforce.cpp:198 -msgid "" -"If what is MODES, nothing will be enforced, since it would\n" -"enforce modes that the current ircd does not support. If +R is\n" -"specified for what, an equalivant of channelmode +R on\n" -"other ircds will be enforced. All users that are in the channel\n" -"but have not identified for their nickname will be kicked and\n" -"banned from the channel." -msgstr "" -"Åñëè â êà÷åñòâå ïàðàìåòðà óêàçàíî MODES, ïåðåïðîâåðêà îñóùåñòâëåíà\n" -"ÍÅ ÁÓÄÅÒ, òàê êàê òåêóùèé IRCD íå ïîääåðæèâàåò íåîáõîäèìûå ðåæèìû.\n" -"Îòäåëüíî óêàçàííûé ïàðàìåòð +R ïðèìåíèò êàíàëüíûé ðåæèì, ýêâèâàëåíòíûé\n" -"ðåæèìó +R è çàáàíèò âñåõ ïîëüçîâàòåëåé, êîòîðûå íå èäåíòèôèöèðîâàëèñü ê " -"ñâîåìó\n" -"íèêó èëè íå èìåþò çàðåãèñòðèðîâàííîãî íèêà." - -#: ../cs_enforce.cpp:178 ../cs_enforce.cpp:210 -msgid "Syntax: ENFORCE channel [what]" -msgstr "Ñèíòàêñèñ: ENFORCE #êàíàë ïàðàìåòð" diff --git a/modules/extra/language/cs_tban.de_DE.po b/modules/extra/language/cs_tban.de_DE.po deleted file mode 100644 index 37ae58466..000000000 --- a/modules/extra/language/cs_tban.de_DE.po +++ /dev/null @@ -1,40 +0,0 @@ -# German translations for cs_tban -# German messages for cs_tban -# Copyright (C) 2011 Anope Team -# This file is distributed under the same license as the Anope IRC Services -# Adam , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: Anope\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-06-17 13:49-0400\n" -"PO-Revision-Date: 2010-09-26 00:16-0400\n" -"Last-Translator: Adam \n" -"Language-Team: German\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=ISO-8859-1\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" - -#: ../cs_tban.cpp:85 -#, c-format -msgid "%s banned from %s, will auto-expire in %s" -msgstr "%s gebannt von %s, wird auto-auslaufen in %s" - -#: ../cs_tban.cpp:95 -msgid "" -"Bans the given user from a channel for a specified length of\n" -"time. If the ban is removed before by hand, it will NOT be replaced." -msgstr "" -"Bant ein User für eine bestimmte Zeit aus ein Channel\n" -"Wenn der Ban manuell entfernt wird, wird es NICHT ersetzt." - -#: ../cs_tban.cpp:61 -#, fuzzy -msgid "Bans the user for a given length of time" -msgstr " TBAN Bant ein User für eine bestimmte Zeit aus ein Channel" - -#: ../cs_tban.cpp:103 -msgid "Syntax: TBAN channel nick time" -msgstr "Syntax: TBAN Channel Nickname Zeit" diff --git a/modules/extra/language/cs_tban.it_IT.po b/modules/extra/language/cs_tban.it_IT.po deleted file mode 100644 index 3989c74d7..000000000 --- a/modules/extra/language/cs_tban.it_IT.po +++ /dev/null @@ -1,41 +0,0 @@ -# Italian translations for cs_tban -# Traduzioni italiane per il cs_tban -# Copyright (C) 2011 Anope Team -# This file is distributed under the same license as the Anope IRC Services -# Adam , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: Anope\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-06-17 13:49-0400\n" -"PO-Revision-Date: 2010-09-26 00:22-0400\n" -"Last-Translator: Adam \n" -"Language-Team: Italian\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=ISO-8859-1\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" - -#: ../cs_tban.cpp:85 -#, c-format -msgid "%s banned from %s, will auto-expire in %s" -msgstr "%s bannato da %s, scadrà automaticamente tra %s" - -#: ../cs_tban.cpp:95 -msgid "" -"Bans the given user from a channel for a specified length of\n" -"time. If the ban is removed before by hand, it will NOT be replaced." -msgstr "" -"Banna l'utente specificato da un canale per un periodo di tempo\n" -"specificato. Se il ban viene rimosso a mano prima della scadenza, NON verrà " -"rimpiazzato." - -#: ../cs_tban.cpp:61 -#, fuzzy -msgid "Bans the user for a given length of time" -msgstr " TBAN Banna l'utente per un periodo di tempo specificato" - -#: ../cs_tban.cpp:103 -msgid "Syntax: TBAN channel nick time" -msgstr "Sintassi: TBAN canale nick tempo" diff --git a/modules/extra/language/cs_tban.nl_NL.po b/modules/extra/language/cs_tban.nl_NL.po deleted file mode 100644 index 18239f383..000000000 --- a/modules/extra/language/cs_tban.nl_NL.po +++ /dev/null @@ -1,41 +0,0 @@ -# Dutch translations for cs_tban -# Engelse vertalingen voor het cs_tban -# Copyright (C) 2011 Anope Team -# This file is distributed under the same license as the Anope IRC Services -# Adam , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: Anope\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-06-17 13:49-0400\n" -"PO-Revision-Date: 2010-09-26 00:15-0400\n" -"Last-Translator: Adam \n" -"Language-Team: Dutch\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=ISO-8859-1\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" - -#: ../cs_tban.cpp:85 -#, c-format -msgid "%s banned from %s, will auto-expire in %s" -msgstr "%s verbannen van %s, zal verlopen in %s" - -#: ../cs_tban.cpp:95 -msgid "" -"Bans the given user from a channel for a specified length of\n" -"time. If the ban is removed before by hand, it will NOT be replaced." -msgstr "" -"Verbant de gegeven gebruiken van het gegeven kanaal voor de\n" -"gegeven tijdsduur. Als de verbanning eerder wordt verwijderd,\n" -"zal deze NIET worden vervangen." - -#: ../cs_tban.cpp:61 -#, fuzzy -msgid "Bans the user for a given length of time" -msgstr " TBAN Verban een gebruiker voor een bepaalde tijd" - -#: ../cs_tban.cpp:103 -msgid "Syntax: TBAN channel nick time" -msgstr "Syntax: TBAN kanaal nick tijd" diff --git a/modules/extra/language/cs_tban.pt_PT.po b/modules/extra/language/cs_tban.pt_PT.po deleted file mode 100644 index 944d977fd..000000000 --- a/modules/extra/language/cs_tban.pt_PT.po +++ /dev/null @@ -1,40 +0,0 @@ -# Portuguese translations for cs_tban -# Copyright (C) 2011 Anope Team -# This file is distributed under the same license as the Anope IRC Services -# Adam , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: Anope\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-06-17 13:49-0400\n" -"PO-Revision-Date: 2010-09-26 00:18-0400\n" -"Last-Translator: Adam \n" -"Language-Team: Portuguese\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=ISO-8859-1\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" - -#: ../cs_tban.cpp:85 -#, c-format -msgid "%s banned from %s, will auto-expire in %s" -msgstr "%s foi banido do %s, irá auto-expirar em %s" - -#: ../cs_tban.cpp:95 -msgid "" -"Bans the given user from a channel for a specified length of\n" -"time. If the ban is removed before by hand, it will NOT be replaced." -msgstr "" -"Bane de um canal o usuário especificado por um determinado período de\n" -"tempo. Se o ban for removido manualmente antes do tempo, ele não será " -"recolocado." - -#: ../cs_tban.cpp:61 -#, fuzzy -msgid "Bans the user for a given length of time" -msgstr " TBAN Bane o usuário por um determinado período de tempo" - -#: ../cs_tban.cpp:103 -msgid "Syntax: TBAN channel nick time" -msgstr "Sintaxe: TBAN canal nick tempo" diff --git a/modules/extra/language/cs_tban.ru_RU.po b/modules/extra/language/cs_tban.ru_RU.po deleted file mode 100644 index 881c9ea79..000000000 --- a/modules/extra/language/cs_tban.ru_RU.po +++ /dev/null @@ -1,42 +0,0 @@ -# Russian translations for cs_tban -# ?????????? ???????? ??? ?????? cs_tban -# Copyright (C) 2011 Anope Team -# This file is distributed under the same license as the Anope IRC Services -# Adam , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: Anope\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-06-17 13:49-0400\n" -"PO-Revision-Date: 2010-09-26 00:20-0400\n" -"Last-Translator: Adam \n" -"Language-Team: Russian\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=ISO-8859-1\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%" -"10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" - -#: ../cs_tban.cpp:85 -#, c-format -msgid "%s banned from %s, will auto-expire in %s" -msgstr "Óñòàíîâëåííûé áàí %s íà êàíàëå %s èñòå÷åò ÷åðåç %s ñåêóíä" - -#: ../cs_tban.cpp:95 -msgid "" -"Bans the given user from a channel for a specified length of\n" -"time. If the ban is removed before by hand, it will NOT be replaced." -msgstr "" -"Áàíèò ïîëüçîâàòåëÿ íà óêàçàííûé ïðîìåæóòîê âðåìåíè â ñåêóíäàõ\n" -"Ïðèìå÷àíèå: óäàëåííûé âðó÷íóþ (äî ñâîåãî èñòå÷åíèÿ) áàí ÍÅ ÁÓÄÅÒ\n" -"ïåðåóñòàíîâëåí ñåðâèñàìè àâòîìàòè÷åñêè!" - -#: ../cs_tban.cpp:61 -#, fuzzy -msgid "Bans the user for a given length of time" -msgstr " TBAN Áàíèò ïîëüçîâàòåëÿ íà óêàçàííûé ïðîìåæóòîê âðåìåíè" - -#: ../cs_tban.cpp:103 -msgid "Syntax: TBAN channel nick time" -msgstr "Ñèíòàêñèñ: TBAN #êàíàë íèê âðåìÿ" diff --git a/modules/extra/language/hs_request.it_IT.po b/modules/extra/language/hs_request.it_IT.po deleted file mode 100644 index 5fa437bad..000000000 --- a/modules/extra/language/hs_request.it_IT.po +++ /dev/null @@ -1,149 +0,0 @@ -# Italian translations for hs_request -# Traduzioni italiane per il hs_request -# Copyright (C) 2011 Anope Team -# This file is distributed under the same license as Anope IRC Services -# Adam , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: Anope\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-06-17 13:50-0400\n" -"PO-Revision-Date: 2010-09-25 23:42-0400\n" -"Last-Translator: Adam \n" -"Language-Team: Italian\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=ISO-8859-1\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" - -#: ../hs_request.cpp:267 -#, c-format -msgid "#%d Nick:%s, vhost:%s (%s - %s)" -msgstr "" - -#: ../hs_request.cpp:265 -#, c-format -msgid "#%d Nick:%s, vhost:%s@%s (%s - %s)" -msgstr "" - -#: ../hs_request.cpp:180 ../hs_request.cpp:238 -msgid "A memo informing the user will also be sent." -msgstr "Viene inviato un memo per informare l'utente." - -#: ../hs_request.cpp:178 -msgid "Activate the requested vHost for the given nick." -msgstr "Attiva il vHost richiesto per il nick specificato." - -#: ../hs_request.cpp:139 -#, fuzzy -msgid "Approve the requested vHost of a user" -msgstr "Attiva il vHost richiesto per il nick specificato." - -#: ../hs_request.cpp:286 -msgid "Convenience command for LIST +req" -msgstr "" - -#: ../hs_request.cpp:271 -#, c-format -msgid "Displayed all records (Count: %d)" -msgstr "" - -#: ../hs_request.cpp:166 ../hs_request.cpp:227 -#, c-format -msgid "No request for nick %s found." -msgstr "Nessuna richiesta trovata per il nick %s." - -#: ../hs_request.cpp:105 -#, c-format -msgid "Please wait %d seconds before requesting a new vHost" -msgstr "Prego attendere %d secondi prima di richiedere un nuovo vHost" - -#: ../hs_request.cpp:236 -msgid "Reject the requested vHost for the given nick." -msgstr "Rifiuta il vHost richiesto per il nick specificato." - -#: ../hs_request.cpp:196 -#, fuzzy -msgid "Reject the requested vHost of a user" -msgstr "Rifiuta il vHost richiesto per il nick specificato." - -#: ../hs_request.cpp:52 -#, fuzzy -msgid "Request a vHost for your nick" -msgstr " REQUEST Richiede un vHost per il tuo nick" - -#: ../hs_request.cpp:122 -msgid "" -"Request the given vHost to be actived for your nick by the\n" -"network administrators. Please be patient while your request\n" -"is being considered." -msgstr "" -"Richiede l'attivazione del vHost specificato per il tuo nick da parte\n" -"degli amministratori di rete. Sei pregato di pazientare finchè la tua\n" -"richiesta viene elaborata." - -#: ../hs_request.cpp:176 ../hs_request.cpp:187 -msgid "Syntax: ACTIVATE nick" -msgstr "Sintassi: ACTIVATE nick" - -#: ../hs_request.cpp:234 ../hs_request.cpp:245 -msgid "Syntax: REJECT nick" -msgstr "Sintassi: REJECT nick" - -#: ../hs_request.cpp:68 ../hs_request.cpp:120 ../hs_request.cpp:130 -msgid "Syntax: REQUEST vhost" -msgstr "Sintassi: REQUEST vhost" - -#: ../hs_request.cpp:296 -msgid "Syntax: WAITING" -msgstr "Sintassi: WAITING" - -#: ../hs_request.cpp:298 -msgid "" -"This command is provided for convenience. It is essentially\n" -"the same as performing a LIST +req ." -msgstr "" -"Questo comando è per comodità. Praticamente è la stessa cosa che\n" -"eseguire un LIST +req ." - -#: ../hs_request.cpp:111 -msgid "Your vHost has been requested" -msgstr "Il tuo vHost è stato richiesto" - -#: ../hs_request.cpp:158 -msgid "[auto memo] Your requested vHost has been approved." -msgstr "[auto memo] Il vHost da te richiesto è stato approvato." - -#: ../hs_request.cpp:218 -msgid "[auto memo] Your requested vHost has been rejected." -msgstr "[auto memo] Il vHost da te richiesto è stato rifiutato." - -#: ../hs_request.cpp:216 -#, c-format -msgid "[auto memo] Your requested vHost has been rejected. Reason: %s" -msgstr "[auto memo] Il vHost da te richiesto è stato rifiutato. Motivo: %s" - -#: ../hs_request.cpp:438 -#, fuzzy, c-format -msgid "[auto memo] vHost %s has been requested by %s." -msgstr "[auto memo] è stato richiesto il vHost %s." - -#: ../hs_request.cpp:160 -#, c-format -msgid "vHost for %s has been activated" -msgstr "Il vHost per %s è stato attivato" - -#: ../hs_request.cpp:223 -#, c-format -msgid "vHost for %s has been rejected" -msgstr "Il vHost per %s è stato rifiutato" - -#~ msgid "" -#~ " ACTIVATE Approve the requested vHost of a user\n" -#~ " REJECT Reject the requested vHost of a user\n" -#~ " WAITING Convenience command for LIST +req" -#~ msgstr "" -#~ " ACTIVATE Approva il vHost richiesto di un utente\n" -#~ " REJECT Rifiuta il vHost richiesto di un utente\n" -#~ " WAITING Comando per LIST +req" diff --git a/modules/extra/language/hs_request.nl_NL.po b/modules/extra/language/hs_request.nl_NL.po deleted file mode 100644 index 2f1f065a1..000000000 --- a/modules/extra/language/hs_request.nl_NL.po +++ /dev/null @@ -1,150 +0,0 @@ -# Dutch translations for hs_request -# Engelse vertalingen voor het hs_request -# Copyright (C) 2011 Anope Team -# This file is distributed under the same license as Anope IRC Services -# Adam , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: Anope\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-06-17 13:50-0400\n" -"PO-Revision-Date: 2010-09-25 22:26-0400\n" -"Last-Translator: Adam \n" -"Language-Team: Dutch\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=ISO-8859-1\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" - -#: ../hs_request.cpp:267 -#, c-format -msgid "#%d Nick:%s, vhost:%s (%s - %s)" -msgstr "" - -#: ../hs_request.cpp:265 -#, c-format -msgid "#%d Nick:%s, vhost:%s@%s (%s - %s)" -msgstr "" - -#: ../hs_request.cpp:180 ../hs_request.cpp:238 -msgid "A memo informing the user will also be sent." -msgstr "" -"Een memo die de gebruiker op de hoogste stelt zal ook worden verstuurd." - -#: ../hs_request.cpp:178 -msgid "Activate the requested vHost for the given nick." -msgstr "Activeer de aangevraagde vHost voor de gegeven nick." - -#: ../hs_request.cpp:139 -#, fuzzy -msgid "Approve the requested vHost of a user" -msgstr "Activeer de aangevraagde vHost voor de gegeven nick." - -#: ../hs_request.cpp:286 -msgid "Convenience command for LIST +req" -msgstr "" - -#: ../hs_request.cpp:271 -#, c-format -msgid "Displayed all records (Count: %d)" -msgstr "" - -#: ../hs_request.cpp:166 ../hs_request.cpp:227 -#, c-format -msgid "No request for nick %s found." -msgstr "Geen aanvraag voor nick %s gevonden." - -#: ../hs_request.cpp:105 -#, c-format -msgid "Please wait %d seconds before requesting a new vHost" -msgstr "Wacht %d seconden voor je een nieuwe vHost aanvraagt" - -#: ../hs_request.cpp:236 -msgid "Reject the requested vHost for the given nick." -msgstr "Keur de aangevraagde vHost voor de gegeven nick af." - -#: ../hs_request.cpp:196 -#, fuzzy -msgid "Reject the requested vHost of a user" -msgstr "Keur de aangevraagde vHost voor de gegeven nick af." - -#: ../hs_request.cpp:52 -#, fuzzy -msgid "Request a vHost for your nick" -msgstr " REQUEST Vraag een vHost aan voor je nick" - -#: ../hs_request.cpp:122 -msgid "" -"Request the given vHost to be actived for your nick by the\n" -"network administrators. Please be patient while your request\n" -"is being considered." -msgstr "" -"Verzoek de gegeven vHost te activeren voor jouw nick bij de\n" -"netwerk beheerders. Het kan even duren voordat je aanvraag\n" -"afgehandeld wordt." - -#: ../hs_request.cpp:176 ../hs_request.cpp:187 -msgid "Syntax: ACTIVATE nick" -msgstr "Gebruik: ACTIVATE nick" - -#: ../hs_request.cpp:234 ../hs_request.cpp:245 -msgid "Syntax: REJECT nick" -msgstr "Gebruik: REJECT nick" - -#: ../hs_request.cpp:68 ../hs_request.cpp:120 ../hs_request.cpp:130 -msgid "Syntax: REQUEST vhost" -msgstr "Gebruik: REQUEST vhost" - -#: ../hs_request.cpp:296 -msgid "Syntax: WAITING" -msgstr "Gebruik: WAITING" - -#: ../hs_request.cpp:298 -msgid "" -"This command is provided for convenience. It is essentially\n" -"the same as performing a LIST +req ." -msgstr "" -"Dit commando is beschikbaar als handigheid. Het is simpelweg\n" -"hetzelfde als LIST +req ." - -#: ../hs_request.cpp:111 -msgid "Your vHost has been requested" -msgstr "Je vHost is aangevraagd" - -#: ../hs_request.cpp:158 -msgid "[auto memo] Your requested vHost has been approved." -msgstr "[auto memo] Je aangevraagde vHost is geaccepteerd." - -#: ../hs_request.cpp:218 -msgid "[auto memo] Your requested vHost has been rejected." -msgstr "[auto memo] Je aangevraagde vHost is afgekeurd." - -#: ../hs_request.cpp:216 -#, c-format -msgid "[auto memo] Your requested vHost has been rejected. Reason: %s" -msgstr "[auto memo] Je aangevraagde vHost is afgekeurd. Reden: %s" - -#: ../hs_request.cpp:438 -#, fuzzy, c-format -msgid "[auto memo] vHost %s has been requested by %s." -msgstr "[auto memo] vHost %s is aangevraagd." - -#: ../hs_request.cpp:160 -#, c-format -msgid "vHost for %s has been activated" -msgstr "vHost voor %s is geactiveerd" - -#: ../hs_request.cpp:223 -#, c-format -msgid "vHost for %s has been rejected" -msgstr "vHost voor %s is afgekeurd" - -#~ msgid "" -#~ " ACTIVATE Approve the requested vHost of a user\n" -#~ " REJECT Reject the requested vHost of a user\n" -#~ " WAITING Convenience command for LIST +req" -#~ msgstr "" -#~ " ACTIVATE Activeer de aangevraagde vHost voor een gebruiker\n" -#~ " REJECT Keur de aangevraagde vHost voor een gebruiker af\n" -#~ " WAITING Snelkoppeling naar LIST +req" diff --git a/modules/extra/language/hs_request.pt_PT.po b/modules/extra/language/hs_request.pt_PT.po deleted file mode 100644 index 74f767395..000000000 --- a/modules/extra/language/hs_request.pt_PT.po +++ /dev/null @@ -1,148 +0,0 @@ -# Portuguese translations for hs_request -# Copyright (C) 2011 Anope Team -# This file is distributed under the same license as Anope IRC Services -# Adam , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: Anope\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-06-17 13:50-0400\n" -"PO-Revision-Date: 2010-09-25 22:48-0400\n" -"Last-Translator: Adam \n" -"Language-Team: Portuguese\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=ISO-8859-1\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" - -#: ../hs_request.cpp:267 -#, c-format -msgid "#%d Nick:%s, vhost:%s (%s - %s)" -msgstr "" - -#: ../hs_request.cpp:265 -#, c-format -msgid "#%d Nick:%s, vhost:%s@%s (%s - %s)" -msgstr "" - -#: ../hs_request.cpp:180 ../hs_request.cpp:238 -msgid "A memo informing the user will also be sent." -msgstr "Um memo informando o usuário também será enviado." - -#: ../hs_request.cpp:178 -msgid "Activate the requested vHost for the given nick." -msgstr "Ativa o vHost solicitado para o nick fornecido." - -#: ../hs_request.cpp:139 -#, fuzzy -msgid "Approve the requested vHost of a user" -msgstr "Ativa o vHost solicitado para o nick fornecido." - -#: ../hs_request.cpp:286 -msgid "Convenience command for LIST +req" -msgstr "" - -#: ../hs_request.cpp:271 -#, c-format -msgid "Displayed all records (Count: %d)" -msgstr "" - -#: ../hs_request.cpp:166 ../hs_request.cpp:227 -#, c-format -msgid "No request for nick %s found." -msgstr "Nenhum pedido encontrado para o nick %s." - -#: ../hs_request.cpp:105 -#, c-format -msgid "Please wait %d seconds before requesting a new vHost" -msgstr "Por favor, espere %d segundos antes de fazer um novo pedido de vHost" - -#: ../hs_request.cpp:236 -msgid "Reject the requested vHost for the given nick." -msgstr "Recusa o pedido de vHost para o nick fornecido." - -#: ../hs_request.cpp:196 -#, fuzzy -msgid "Reject the requested vHost of a user" -msgstr "Recusa o pedido de vHost para o nick fornecido." - -#: ../hs_request.cpp:52 -#, fuzzy -msgid "Request a vHost for your nick" -msgstr " REQUEST Request a vHost for your nick" - -#: ../hs_request.cpp:122 -msgid "" -"Request the given vHost to be actived for your nick by the\n" -"network administrators. Please be patient while your request\n" -"is being considered." -msgstr "" -"Solicita a ativação do vHost fornecido em seu nick pelos\n" -"administradores da rede. Por favor, tenha paciência\n" -"enquanto seu pedido é analisado." - -#: ../hs_request.cpp:176 ../hs_request.cpp:187 -msgid "Syntax: ACTIVATE nick" -msgstr "Sintaxe: ACTIVATE nick" - -#: ../hs_request.cpp:234 ../hs_request.cpp:245 -msgid "Syntax: REJECT nick" -msgstr "Sintaxe: REJECT nick" - -#: ../hs_request.cpp:68 ../hs_request.cpp:120 ../hs_request.cpp:130 -msgid "Syntax: REQUEST vhost" -msgstr "Sintaxe: REQUEST vhost" - -#: ../hs_request.cpp:296 -msgid "Syntax: WAITING" -msgstr "Sintaxe: WAITING" - -#: ../hs_request.cpp:298 -msgid "" -"This command is provided for convenience. It is essentially\n" -"the same as performing a LIST +req ." -msgstr "" -"Este comando é usado por conveniência. É essencialmente\n" -"o mesmo que fazer um LIST +req" - -#: ../hs_request.cpp:111 -msgid "Your vHost has been requested" -msgstr "Seu pedido de vHost foi encaminhado" - -#: ../hs_request.cpp:158 -msgid "[auto memo] Your requested vHost has been approved." -msgstr "[Auto Memo] Seu pedido de vHost foi aprovado." - -#: ../hs_request.cpp:218 -msgid "[auto memo] Your requested vHost has been rejected." -msgstr "[Auto Memo] Seu pedido de vHost foi recusado." - -#: ../hs_request.cpp:216 -#, c-format -msgid "[auto memo] Your requested vHost has been rejected. Reason: %s" -msgstr "[Auto Memo] Seu pedido de vHost foi recusado. Motivo: %s" - -#: ../hs_request.cpp:438 -#, fuzzy, c-format -msgid "[auto memo] vHost %s has been requested by %s." -msgstr "[Auto Memo] O vHost %s foi solicitado." - -#: ../hs_request.cpp:160 -#, c-format -msgid "vHost for %s has been activated" -msgstr "O vHost para %s foi ativado" - -#: ../hs_request.cpp:223 -#, c-format -msgid "vHost for %s has been rejected" -msgstr "O vHost de %s foi recusado" - -#~ msgid "" -#~ " ACTIVATE Approve the requested vHost of a user\n" -#~ " REJECT Reject the requested vHost of a user\n" -#~ " WAITING Convenience command for LIST +req" -#~ msgstr "" -#~ " ACTIVATE Aprova o pedido de vHost de um usuário\n" -#~ " REJECT Recusa o pedido de vHost de um usuário\n" -#~ " WAITING Comando para LISTAR +req" diff --git a/modules/extra/language/hs_request.ru_RU.po b/modules/extra/language/hs_request.ru_RU.po deleted file mode 100644 index 81c905802..000000000 --- a/modules/extra/language/hs_request.ru_RU.po +++ /dev/null @@ -1,151 +0,0 @@ -# Russian translations for hs_request -# ?????????? ???????? ??? ?????? PACKAGE. -# Copyright (C) 2011 Anope Team -# This file is distributed under the same license as Anope IRC Services -# Adam , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: Anope\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-06-17 13:50-0400\n" -"PO-Revision-Date: 2010-09-25 23:27-0400\n" -"Last-Translator: Adam \n" -"Language-Team: Russian\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=ISO-8859-1\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%" -"10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" - -#: ../hs_request.cpp:267 -#, c-format -msgid "#%d Nick:%s, vhost:%s (%s - %s)" -msgstr "" - -#: ../hs_request.cpp:265 -#, c-format -msgid "#%d Nick:%s, vhost:%s@%s (%s - %s)" -msgstr "" - -#: ../hs_request.cpp:180 ../hs_request.cpp:238 -msgid "A memo informing the user will also be sent." -msgstr "Ïîëüçîâàòåëþ áóäåò ïîñëàíî àâòî-óâåäîìëåíèå îá àêòèâàöèè åãî çàïðîñà." - -#: ../hs_request.cpp:178 -msgid "Activate the requested vHost for the given nick." -msgstr "Óòâåðäèòü çàïðàøèâàåìûé vHost äëÿ óêàçàííîãî íèêà." - -#: ../hs_request.cpp:139 -#, fuzzy -msgid "Approve the requested vHost of a user" -msgstr "Óòâåðäèòü çàïðàøèâàåìûé vHost äëÿ óêàçàííîãî íèêà." - -#: ../hs_request.cpp:286 -msgid "Convenience command for LIST +req" -msgstr "" - -#: ../hs_request.cpp:271 -#, c-format -msgid "Displayed all records (Count: %d)" -msgstr "" - -#: ../hs_request.cpp:166 ../hs_request.cpp:227 -#, c-format -msgid "No request for nick %s found." -msgstr "Çàïðîñ íà vHost äëÿ íèêà %s íå íàéäåí." - -#: ../hs_request.cpp:105 -#, c-format -msgid "Please wait %d seconds before requesting a new vHost" -msgstr "Ïîæàëóéñòà, ïîäîæäèòå %d ñåêóíä, ïðåæäå ÷åì çàïðàøèâàòü íîâûé vHost" - -#: ../hs_request.cpp:236 -msgid "Reject the requested vHost for the given nick." -msgstr "Îòêëîíèòü çàïðàøèâàåìûé vHost äëÿ óêàçàííîãî íèêà." - -#: ../hs_request.cpp:196 -#, fuzzy -msgid "Reject the requested vHost of a user" -msgstr "Îòêëîíèòü çàïðàøèâàåìûé vHost äëÿ óêàçàííîãî íèêà." - -#: ../hs_request.cpp:52 -#, fuzzy -msgid "Request a vHost for your nick" -msgstr " REQUEST Çàïðîñ íà vHost äëÿ âàøåãî òåêóùåãî íèêà" - -#: ../hs_request.cpp:122 -msgid "" -"Request the given vHost to be actived for your nick by the\n" -"network administrators. Please be patient while your request\n" -"is being considered." -msgstr "" -"Îòïðàâëÿåò çàïðîñ íà àêòèâàöèþ vHost, êîòîðûé áóäåò ðàññìîòðåí îäíèì èç\n" -"àäìèíèñòðàòîðîâ ñåòè. Ïðîñüáà ïðîÿâèòü òåðïåíèå, ïîêà çàïðîñ\n" -"ðàññìàòðèâàåòñÿ àäìèíèñòðàöèåé." - -#: ../hs_request.cpp:176 ../hs_request.cpp:187 -msgid "Syntax: ACTIVATE nick" -msgstr "Ñèíòàêñèñ: ACTIVATE íèê" - -#: ../hs_request.cpp:234 ../hs_request.cpp:245 -msgid "Syntax: REJECT nick" -msgstr "Ñèíòàêñèñ: REJECT íèê" - -#: ../hs_request.cpp:68 ../hs_request.cpp:120 ../hs_request.cpp:130 -msgid "Syntax: REQUEST vhost" -msgstr "Ñèíòàêñèñ: REQUEST vHost" - -#: ../hs_request.cpp:296 -msgid "Syntax: WAITING" -msgstr "Ñèíòàêñèñ: WAITING" - -#: ../hs_request.cpp:298 -msgid "" -"This command is provided for convenience. It is essentially\n" -"the same as performing a LIST +req ." -msgstr "" -"Äàííàÿ êîìàíäà ñîçäàíà äëÿ óäîáñòâà èñïîëüçîâàíèÿ è âûâîäèò ñïèñîê " -"çàïðîñîâ,\n" -"îæèäàþùèõ îáðàáîòêè. Àíàëîãè÷íàÿ êîìàíäà: LIST +req ." - -#: ../hs_request.cpp:111 -msgid "Your vHost has been requested" -msgstr "Âàø çàïðîñ íà vHost îòïðàâëåí." - -#: ../hs_request.cpp:158 -msgid "[auto memo] Your requested vHost has been approved." -msgstr "[àâòî-ñîîáùåíèå] Çàïðàøèâàåìûé âàìè vHost óòâåðæäåí è àêòèâèðîâàí." - -#: ../hs_request.cpp:218 -msgid "[auto memo] Your requested vHost has been rejected." -msgstr "[àâòî-ñîîáùåíèå] Çàïðàøèâàåìûé âàìè vHost îòêëîíåí." - -#: ../hs_request.cpp:216 -#, c-format -msgid "[auto memo] Your requested vHost has been rejected. Reason: %s" -msgstr "[àâòî-ñîîáùåíèå] Çàïðàøèâàåìûé âàìè vHost îòêëîíåí. Ïðè÷èíà: %s" - -#: ../hs_request.cpp:438 -#, fuzzy, c-format -msgid "[auto memo] vHost %s has been requested by %s." -msgstr "[àâòî-ñîîáùåíèå] Áûë çàïðîøåí vHost %s" - -#: ../hs_request.cpp:160 -#, c-format -msgid "vHost for %s has been activated" -msgstr "vHost äëÿ %s óñïåøíî àêòèâèðîâàí" - -#: ../hs_request.cpp:223 -#, c-format -msgid "vHost for %s has been rejected" -msgstr "vHost äëÿ %s îòêëîíåí." - -#~ msgid "" -#~ " ACTIVATE Approve the requested vHost of a user\n" -#~ " REJECT Reject the requested vHost of a user\n" -#~ " WAITING Convenience command for LIST +req" -#~ msgstr "" -#~ " ACTIVATE Óòâåðäèòü çàïðàøèâàåìûé ïîëüçîâàòåëåì vHost\n" -#~ " REJECT Îòêëîíèòü çàïðàøèâàåìûé ïîëüçîâàòåëåì vHost\n" -#~ " WAITING Ñïèñîê çàïðîñîâ îæèäàþùèõ îáðàáîòêè (àíàëîã LIST +req)" diff --git a/modules/extra/language/ns_maxemail.de_DE.po b/modules/extra/language/ns_maxemail.de_DE.po deleted file mode 100644 index fe1f3aef4..000000000 --- a/modules/extra/language/ns_maxemail.de_DE.po +++ /dev/null @@ -1,28 +0,0 @@ -# German translations for ns_maxemail -# German messages for ns_maxemail -# Copyright (C) 2011 Anope Team -# This file is distributed under the same license as Anope IRC Services -# Adam , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: Anope\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-02-04 03:41-0500\n" -"PO-Revision-Date: 2010-09-26 00:03-0400\n" -"Last-Translator: Adam \n" -"Language-Team: German\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=ISO-8859-1\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" - -#: ../ns_maxemail.cpp:33 -#, fuzzy, c-format -msgid "The given email address has reached its usage limit of %d users." -msgstr "Die angegebene eMail hat die limit Begrenzung von %d User erreicht." - -#: ../ns_maxemail.cpp:31 -#, fuzzy -msgid "The given email address has reached its usage limit of 1 user." -msgstr "Die angegebene eMail hat die limit Begrenzung von 1 User erreicht." diff --git a/modules/extra/language/ns_maxemail.it_IT.po b/modules/extra/language/ns_maxemail.it_IT.po deleted file mode 100644 index 1cd1a4083..000000000 --- a/modules/extra/language/ns_maxemail.it_IT.po +++ /dev/null @@ -1,32 +0,0 @@ -# Italian translations for ns_maxemail -# Traduzioni italiane per il ns_maxemail -# Copyright (C) 2011 Anope Team -# This file is distributed under the same license as Anope IRC Services -# Adam , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: Anope\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-02-04 03:41-0500\n" -"PO-Revision-Date: 2010-09-26 00:11-0400\n" -"Last-Translator: Adam \n" -"Language-Team: Italian\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=ISO-8859-1\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" - -#: ../ns_maxemail.cpp:33 -#, fuzzy, c-format -msgid "The given email address has reached its usage limit of %d users." -msgstr "" -"L'indirizzo email specificato ha raggiunto il suo limite d'utilizzo di %d " -"utenti." - -#: ../ns_maxemail.cpp:31 -#, fuzzy -msgid "The given email address has reached its usage limit of 1 user." -msgstr "" -"L'indirizzo email specificato ha raggiunto il suo limite d'utilizzo di 1 " -"utente." diff --git a/modules/extra/language/ns_maxemail.nl_NL.po b/modules/extra/language/ns_maxemail.nl_NL.po deleted file mode 100644 index fb84c786a..000000000 --- a/modules/extra/language/ns_maxemail.nl_NL.po +++ /dev/null @@ -1,28 +0,0 @@ -# Dutch translations for ns_maxemail -# Engelse vertalingen voor het ns_maxemail -# Copyright (C) 2011 Anope Team -# This file is distributed under the same license as Anope IRC Services -# Adam , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: Anope\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-02-04 03:41-0500\n" -"PO-Revision-Date: 2010-09-25 23:57-0400\n" -"Last-Translator: Adam \n" -"Language-Team: Dutch\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=ISO-8859-1\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" - -#: ../ns_maxemail.cpp:33 -#, fuzzy, c-format -msgid "The given email address has reached its usage limit of %d users." -msgstr "Het gegeven email adres heeft de limiet van %d gebruikers bereikt." - -#: ../ns_maxemail.cpp:31 -#, fuzzy -msgid "The given email address has reached its usage limit of 1 user." -msgstr "Het gegeven email adres heeft de limiet van 1 gebruiker bereikt." diff --git a/modules/extra/language/ns_maxemail.pt_PT.po b/modules/extra/language/ns_maxemail.pt_PT.po deleted file mode 100644 index ff807f582..000000000 --- a/modules/extra/language/ns_maxemail.pt_PT.po +++ /dev/null @@ -1,28 +0,0 @@ -# Portuguese translations for ns_maxemail -# Copyright (C) 2011 Anope Team -# This file is distributed under the same license as Anope IRC Services -# Adam , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: Anope\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-02-04 03:41-0500\n" -"PO-Revision-Date: 2010-09-26 00:05-0400\n" -"Last-Translator: Adam \n" -"Language-Team: Portuguese\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=ISO-8859-1\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" - -#: ../ns_maxemail.cpp:33 -#, fuzzy, c-format -msgid "The given email address has reached its usage limit of %d users." -msgstr "" -"O endereço de email fornecido alcançou seu limite de uso de %d usuários." - -#: ../ns_maxemail.cpp:31 -#, fuzzy -msgid "The given email address has reached its usage limit of 1 user." -msgstr "O endereço de email fornecido alcançou seu limite de uso de 1 usuário." diff --git a/modules/extra/language/ns_maxemail.ru_RU.po b/modules/extra/language/ns_maxemail.ru_RU.po deleted file mode 100644 index 1fa267d55..000000000 --- a/modules/extra/language/ns_maxemail.ru_RU.po +++ /dev/null @@ -1,30 +0,0 @@ -# Russian translations for ns_maxemail -# ?????????? ???????? ??? ?????? ns_maxemail -# Copyright (C) 2011 Anope Team -# This file is distributed under the same license as Anope IRC Services -# Adam , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: Anope\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-02-04 03:41-0500\n" -"PO-Revision-Date: 2010-09-26 00:07-0400\n" -"Last-Translator: Adam \n" -"Language-Team: Russian\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=ISO-8859-1\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%" -"10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" - -#: ../ns_maxemail.cpp:33 -#, fuzzy, c-format -msgid "The given email address has reached its usage limit of %d users." -msgstr "" -"Óêàçàííûé âàìè email-àäðåñ èñïîëüçóåòñÿ ìàêñèìàëüíî äîïóñòèìîå êîë-âî ðàç: %d" - -#: ../ns_maxemail.cpp:31 -#, fuzzy -msgid "The given email address has reached its usage limit of 1 user." -msgstr "Óêàçàííûé âàìè email-àäðåñ óæå êåì-òî èñïîëüçóåòñÿ." diff --git a/modules/extra/language/update.sh b/modules/extra/language/update.sh deleted file mode 100755 index 1f16ea961..000000000 --- a/modules/extra/language/update.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash - -for f in `ls ../*.cpp` -do - BASE=`basename $f | cut -d'.' -f1` - xgettext -C -s -d $BASE -o $BASE.pot --from-code=utf-8 --keyword --keyword=_ $f -done - -for f in `ls *.po` -do - msgmerge -v -s -U $f `basename $f | cut -d'.' -f1`.pot -done - -rm -f *~ diff --git a/modules/extra/ns_set_misc.cpp b/modules/extra/ns_set_misc.cpp deleted file mode 100644 index f0ee269cc..000000000 --- a/modules/extra/ns_set_misc.cpp +++ /dev/null @@ -1,126 +0,0 @@ -/* - * - * (C) 2003-2011 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 CommandNSSetMisc : public Command -{ - public: - CommandNSSetMisc(Module *creator, const Anope::string &cname = "nickserv/set/misc", size_t min = 1) : Command(creator, cname, min, min + 1) - { - this->SetSyntax(_("\037parameter\037")); - } - - void Run(CommandSource &source, const Anope::string &user, const Anope::string ¶m) - { - NickAlias *na = findnick(user); - if (!na) - { - source.Reply(NICK_X_NOT_REGISTERED, user.c_str()); - return; - } - NickCore *nc = na->nc; - - nc->Shrink("ns_set_misc:" + source.command.replace_all_cs(" ", "_")); - if (!param.empty()) - { - nc->Extend("ns_set_misc:" + source.command.replace_all_cs(" ", "_"), new ExtensibleItemRegular(param)); - source.Reply(CHAN_SETTING_CHANGED, source.command.c_str(), nc->display.c_str(), param.c_str()); - } - else - source.Reply(CHAN_SETTING_UNSET, source.command.c_str(), nc->display.c_str()); - - return; - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - this->Run(source, source.u->Account()->display, params[0]); - } -}; - -class CommandNSSASetMisc : public CommandNSSetMisc -{ - public: - CommandNSSASetMisc(Module *creator) : CommandNSSetMisc(creator, "nickserv/saset/misc", 2) - { - this->ClearSyntax(); - this->SetSyntax(_("\037nickname\037 \037parameter\037")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - this->Run(source, params[0], params[1]); - } -}; - -class NSSetMisc : public Module -{ - CommandNSSetMisc commandnssetmisc; - CommandNSSASetMisc commandnssasetmisc; - - public: - NSSetMisc(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, SUPPORTED), - commandnssetmisc(this), commandnssasetmisc(this) - { - this->SetAuthor("Anope"); - - Implementation i[] = { I_OnNickInfo, I_OnDatabaseWriteMetadata, I_OnDatabaseReadMetadata }; - ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); - - ModuleManager::RegisterService(&this->commandnssetmisc); - ModuleManager::RegisterService(&this->commandnssasetmisc); - } - - void OnNickInfo(CommandSource &source, NickAlias *na, bool ShowHidden) - { - std::deque list; - na->nc->GetExtList(list); - - for (unsigned i = 0; i < list.size(); ++i) - { - if (list[i].find("ns_set_misc:") != 0) - continue; - - Anope::string value; - if (na->nc->GetExtRegular(list[i], value)) - source.Reply(" %s: %s", list[i].substr(12).replace_all_cs("_", " ").c_str(), value.c_str()); - } - } - - void OnDatabaseWriteMetadata(void (*WriteMetadata)(const Anope::string &, const Anope::string &), NickCore *nc) - { - std::deque list; - nc->GetExtList(list); - - for (unsigned i = 0; i < list.size(); ++i) - { - if (list[i].find("ns_set_misc:") != 0) - continue; - - Anope::string value; - if (nc->GetExtRegular(list[i], value)) - WriteMetadata(list[i], ":" + value); - } - } - - EventReturn OnDatabaseReadMetadata(NickCore *nc, const Anope::string &key, const std::vector ¶ms) - { - if (key.find("ns_set_misc:") == 0) - nc->Extend(key, new ExtensibleItemRegular(params[0])); - - return EVENT_CONTINUE; - } -}; - -MODULE_INIT(NSSetMisc) diff --git a/modules/extra/os_defcon.cpp b/modules/extra/os_defcon.cpp deleted file mode 100644 index ea513e1dc..000000000 --- a/modules/extra/os_defcon.cpp +++ /dev/null @@ -1,663 +0,0 @@ -/* OperServ core functions - * - * (C) 2003-2011 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 "global.h" -#include "os_session.h" - -enum DefconLevel -{ - DEFCON_NO_NEW_CHANNELS, - DEFCON_NO_NEW_NICKS, - DEFCON_NO_MLOCK_CHANGE, - DEFCON_FORCE_CHAN_MODES, - DEFCON_REDUCE_SESSION, - DEFCON_NO_NEW_CLIENTS, - DEFCON_OPER_ONLY, - DEFCON_SILENT_OPER_ONLY, - DEFCON_AKILL_NEW_CLIENTS, - DEFCON_NO_NEW_MEMOS -}; - -bool DefConModesSet = false; - -struct DefconConfig -{ - std::vector > DefCon; - Flags DefConModesOn; - Flags DefConModesOff; - std::map DefConModesOnParams; - - int defaultlevel, sessionlimit; - Anope::string chanmodes, message, offmessage, akillreason; - std::vector defcons; - time_t akillexpire, timeout; - bool globalondefcon; - - DefconConfig() - { - this->DefCon.resize(6); - this->defcons.resize(5); - } - - bool Check(DefconLevel level) - { - return this->Check(this->defaultlevel, level); - } - - bool Check(int dlevel, DefconLevel level) - { - return this->DefCon[dlevel].test(level); - } - - void Add(int dlevel, DefconLevel level) - { - this->DefCon[dlevel][level] = true; - } - - void Del(int dlevel, DefconLevel level) - { - this->DefCon[dlevel][level] = false; - } - - bool SetDefConParam(ChannelModeName Name, const Anope::string &buf) - { - return DefConModesOnParams.insert(std::make_pair(Name, buf)).second; - } - - void UnsetDefConParam(ChannelModeName Name) - { - DefConModesOnParams.erase(Name); - } - - bool GetDefConParam(ChannelModeName Name, Anope::string &buf) - { - std::map::iterator it = DefConModesOnParams.find(Name); - - buf.clear(); - - if (it != DefConModesOnParams.end()) - { - buf = it->second; - return true; - } - - return false; - } -}; - -static DefconConfig DConfig; - -/**************************************************************************/ - -void defcon_sendlvls(CommandSource &source); -void runDefCon(); -static Anope::string defconReverseModes(const Anope::string &modes); - -class DefConTimeout : public CallBack -{ - int level; - - public: - DefConTimeout(Module *mod, int newlevel) : CallBack(mod, DConfig.timeout), level(newlevel) { } - - void Tick(time_t) - { - if (DConfig.defaultlevel != level) - { - DConfig.defaultlevel = level; - FOREACH_MOD(I_OnDefconLevel, OnDefconLevel(level)); - Log(findbot(Config->OperServ), "operserv/defcon") << "Defcon level timeout, returning to level " << level; - - if (DConfig.globalondefcon) - { - if (!DConfig.offmessage.empty()) - global->SendGlobal(findbot(Config->Global), "", DConfig.offmessage); - else - global->SendGlobal(findbot(Config->Global), "", Anope::printf(translate(_("The Defcon Level is now at Level: \002%d\002")), DConfig.defaultlevel)); - - if (!DConfig.message.empty()) - global->SendGlobal(findbot(Config->Global), "", DConfig.message); - } - - runDefCon(); - } - } -}; -static DefConTimeout *timeout; - -class CommandOSDefcon : public Command -{ - void SendLevels(CommandSource &source) - { - if (DConfig.Check(DEFCON_NO_NEW_CHANNELS)) - source.Reply(_("* No new channel registrations")); - if (DConfig.Check(DEFCON_NO_NEW_NICKS)) - source.Reply(_("* No new nick registrations")); - if (DConfig.Check(DEFCON_NO_MLOCK_CHANGE)) - source.Reply(_("* No MLOCK changes")); - if (DConfig.Check(DEFCON_FORCE_CHAN_MODES) && !DConfig.chanmodes.empty()) - source.Reply(_("* Force Chan Modes (%s) to be set on all channels"), DConfig.chanmodes.c_str()); - if (DConfig.Check(DEFCON_REDUCE_SESSION)) - source.Reply(_("* Use the reduced session limit of %d"), DConfig.sessionlimit); - if (DConfig.Check(DEFCON_NO_NEW_CLIENTS)) - source.Reply(_("* Kill any NEW clients connecting")); - if (DConfig.Check(DEFCON_OPER_ONLY)) - source.Reply(_("* Ignore any non-opers with message")); - if (DConfig.Check(DEFCON_SILENT_OPER_ONLY)) - source.Reply(_("* Silently ignore non-opers")); - if (DConfig.Check(DEFCON_AKILL_NEW_CLIENTS)) - source.Reply(_("* AKILL any new clients connecting")); - if (DConfig.Check(DEFCON_NO_NEW_MEMOS)) - source.Reply(_("* No new memos sent")); - } - - public: - CommandOSDefcon(Module *creator) : Command(creator, "operserv/defcon", 1, 1) - { - this->SetDesc(_("Manipulate the DefCon system")); - this->SetSyntax(_("[\0021\002|\0022\002|\0023\002|\0024\002|\0025\002]")); - } - - void Execute(CommandSource &source, const std::vector ¶ms) - { - User *u = source.u; - const Anope::string &lvl = params[0]; - - if (lvl.empty()) - { - source.Reply(_("Services are now at DEFCON \002%d\002"), DConfig.defaultlevel); - this->SendLevels(source); - return; - } - - int newLevel = 0; - try - { - newLevel = convertTo(lvl); - } - catch (const ConvertException &) { } - - if (newLevel < 1 || newLevel > 5) - { - this->OnSyntaxError(source, ""); - return; - } - - DConfig.defaultlevel = newLevel; - - FOREACH_MOD(I_OnDefconLevel, OnDefconLevel(newLevel)); - - if (timeout) - { - delete timeout; - timeout = NULL; - } - - if (DConfig.timeout) - timeout = new DefConTimeout(this->module, 5); - - source.Reply(_("Services are now at DEFCON \002%d\002"), DConfig.defaultlevel); - this->SendLevels(source); - Log(LOG_ADMIN, u, 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.defaultlevel == 5 && !DConfig.offmessage.empty()) - global->SendGlobal(findbot(Config->Global), "", DConfig.offmessage); - else if (DConfig.defaultlevel != 5) - { - global->SendGlobal(findbot(Config->Global), "", Anope::printf(_("The Defcon level is now at: \002%d\002"), DConfig.defaultlevel)); - if (!DConfig.message.empty()) - global->SendGlobal(findbot(Config->Global), "", DConfig.message); - } - } - - /* Run any defcon functions, e.g. FORCE CHAN MODE */ - runDefCon(); - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &subcommand) - { - 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.")); - return true; - } -}; - -class OSDefcon : public Module -{ - service_reference session_service; - service_reference akills; - CommandOSDefcon commandosdefcon; - - void ParseModeString() - { - int add = -1; /* 1 if adding, 0 if deleting, -1 if neither */ - unsigned char mode; - ChannelMode *cm; - ChannelModeParam *cmp; - Anope::string modes, param; - - spacesepstream ss(DConfig.chanmodes); - - DConfig.DefConModesOn.ClearFlags(); - DConfig.DefConModesOff.ClearFlags(); - ss.GetToken(modes); - - /* Loop while there are modes to set */ - for (unsigned i = 0, end = modes.length(); i < end; ++i) - { - mode = modes[i]; - - switch (mode) - { - case '+': - add = 1; - continue; - case '-': - add = 0; - continue; - default: - if (add < 0) - continue; - } - - if ((cm = ModeManager::FindChannelModeByChar(mode))) - { - if (cm->Type == MODE_STATUS || cm->Type == MODE_LIST || !cm->CanSet(NULL)) - { - Log() << "DefConChanModes mode character '" << mode << "' cannot be locked"; - continue; - } - else if (add) - { - DConfig.DefConModesOn.SetFlag(cm->Name); - DConfig.DefConModesOff.UnsetFlag(cm->Name); - - if (cm->Type == MODE_PARAM) - { - cmp = debug_cast(cm); - - if (!ss.GetToken(param)) - { - Log() << "DefConChanModes mode character '" << mode << "' has no parameter while one is expected"; - continue; - } - - if (!cmp->IsValid(param)) - continue; - - DConfig.SetDefConParam(cmp->Name, param); - } - } - else if (DConfig.DefConModesOn.HasFlag(cm->Name)) - { - DConfig.DefConModesOn.UnsetFlag(cm->Name); - - if (cm->Type == MODE_PARAM) - DConfig.UnsetDefConParam(cm->Name); - } - } - } - - /* We can't mlock +L if +l is not mlocked as well. */ - if ((cm = ModeManager::FindChannelModeByName(CMODE_REDIRECT)) && DConfig.DefConModesOn.HasFlag(cm->Name) && !DConfig.DefConModesOn.HasFlag(CMODE_LIMIT)) - { - DConfig.DefConModesOn.UnsetFlag(CMODE_REDIRECT); - - Log() << "DefConChanModes must lock mode +l as well to lock mode +L"; - } - - /* Some ircd we can't set NOKNOCK without INVITE */ - /* So check if we need there is a NOKNOCK MODE and that we need INVITEONLY */ - if (ircd->knock_needs_i && (cm = ModeManager::FindChannelModeByName(CMODE_NOKNOCK)) && DConfig.DefConModesOn.HasFlag(cm->Name) && !DConfig.DefConModesOn.HasFlag(CMODE_INVITE)) - { - DConfig.DefConModesOn.UnsetFlag(CMODE_NOKNOCK); - Log() << "DefConChanModes must lock mode +i as well to lock mode +K"; - } - } - - public: - OSDefcon(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, SUPPORTED), session_service("session"), akills("xlinemanager/sgline"), commandosdefcon(this) - { - this->SetAuthor("Anope"); - - Implementation i[] = { I_OnReload, I_OnChannelModeSet, I_OnChannelModeUnset, I_OnPreCommand, I_OnUserConnect, I_OnChannelModeAdd, I_OnChannelCreate }; - ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); - - ModuleManager::RegisterService(&commandosdefcon); - - try - { - this->OnReload(); - } - catch (const ConfigException &ex) - { - throw ModuleException(ex.GetReason()); - } - } - - void OnReload() - { - ConfigReader config; - DefconConfig dconfig; - - dconfig.defaultlevel = config.ReadInteger("defcon", "defaultlevel", 0, 0); - dconfig.defcons[4] = config.ReadValue("defcon", "level4", 0); - dconfig.defcons[3] = config.ReadValue("defcon", "level3", 0); - dconfig.defcons[2] = config.ReadValue("defcon", "level2", 0); - dconfig.defcons[1] = config.ReadValue("defcon", "level1", 0); - dconfig.sessionlimit = config.ReadInteger("defcon", "sessionlimit", 0, 0); - dconfig.akillreason = config.ReadValue("defcon", "akillreason", 0); - dconfig.akillexpire = dotime(config.ReadValue("defcon", "akillexpire", 0)); - dconfig.chanmodes = config.ReadValue("defcon", "chanmodes", 0); - dconfig.timeout = dotime(config.ReadValue("defcon", "timeout", 0)); - dconfig.globalondefcon = config.ReadFlag("defcon", "globalondefcon", 0); - dconfig.message = config.ReadValue("defcon", "message", 0); - dconfig.offmessage = config.ReadValue("defcon", "offmessage", 0); - - if (dconfig.defaultlevel < 1 || dconfig.defaultlevel > 5) - throw ConfigException("The value for must be between 1 and 5"); - else if (dconfig.akillexpire <= 0) - throw ConfigException("The value for must be greater than zero!"); - - for (unsigned level = 1; level < 5; ++level) - { - spacesepstream operations(dconfig.defcons[level]); - Anope::string operation; - while (operations.GetToken(operation)) - { - if (operation.equals_ci("nonewchannels")) - dconfig.Add(level, DEFCON_NO_NEW_CHANNELS); - else if (operation.equals_ci("nonewnicks")) - dconfig.Add(level, DEFCON_NO_NEW_NICKS); - else if (operation.equals_ci("nomlockchanges")) - dconfig.Add(level, DEFCON_NO_MLOCK_CHANGE); - else if (operation.equals_ci("forcechanmodes")) - dconfig.Add(level, DEFCON_FORCE_CHAN_MODES); - else if (operation.equals_ci("reducedsessions")) - dconfig.Add(level, DEFCON_REDUCE_SESSION); - else if (operation.equals_ci("nonewclients")) - dconfig.Add(level, DEFCON_NO_NEW_CLIENTS); - else if (operation.equals_ci("operonly")) - dconfig.Add(level, DEFCON_OPER_ONLY); - else if (operation.equals_ci("silentoperonly")) - dconfig.Add(level, DEFCON_SILENT_OPER_ONLY); - else if (operation.equals_ci("akillnewclients")) - dconfig.Add(level, DEFCON_AKILL_NEW_CLIENTS); - else if (operation.equals_ci("nonewmemos")) - dconfig.Add(level, DEFCON_NO_NEW_MEMOS); - } - - if (dconfig.Check(level, DEFCON_REDUCE_SESSION) && dconfig.sessionlimit <= 0) - throw ConfigException("The value for must be greater than zero!"); - else if (dconfig.Check(level, DEFCON_AKILL_NEW_CLIENTS) && dconfig.akillreason.empty()) - throw ConfigException("The value for must not be empty!"); - else if (dconfig.Check(level, DEFCON_FORCE_CHAN_MODES) && dconfig.chanmodes.empty()) - throw ConfigException("The value for must not be empty!"); - } - - DConfig = dconfig; - this->ParseModeString(); - } - - EventReturn OnUserConnect(User *u, bool &exempt) - { - if (!exempt && u->server->IsSynced() && DConfig.Check(DEFCON_AKILL_NEW_CLIENTS) && !u->server->IsULined()) - { - if (DConfig.Check(DEFCON_AKILL_NEW_CLIENTS) && akills) - { - Log(findbot(Config->OperServ), "operserv/defcon") << "DEFCON: adding akill for *@" << u->host; - XLine *x = akills->Add("*@" + u->host, Config->OperServ, Anope::CurTime + DConfig.akillexpire, DConfig.akillreason); - if (x) - x->By = Config->OperServ; - } - - if (DConfig.Check(DEFCON_NO_NEW_CLIENTS) || DConfig.Check(DEFCON_AKILL_NEW_CLIENTS)) - u->Kill(Config->OperServ, DConfig.akillreason); - - return EVENT_STOP; - } - - return EVENT_CONTINUE; - } - - EventReturn OnChannelModeSet(Channel *c, ChannelModeName Name, const Anope::string ¶m) - { - ChannelMode *cm = ModeManager::FindChannelModeByName(Name); - - if (DConfig.Check(DEFCON_FORCE_CHAN_MODES) && cm && DConfig.DefConModesOff.HasFlag(Name)) - { - c->RemoveMode(findbot(Config->OperServ), Name, param); - - return EVENT_STOP; - } - - return EVENT_CONTINUE; - } - - EventReturn OnChannelModeUnset(Channel *c, ChannelModeName Name, const Anope::string &) - { - ChannelMode *cm = ModeManager::FindChannelModeByName(Name); - - if (DConfig.Check(DEFCON_FORCE_CHAN_MODES) && cm && DConfig.DefConModesOn.HasFlag(Name)) - { - Anope::string param; - - if (DConfig.GetDefConParam(Name, param)) - c->SetMode(findbot(Config->OperServ), Name, param); - else - c->SetMode(findbot(Config->OperServ), Name); - - return EVENT_STOP; - - } - - return EVENT_CONTINUE; - } - - EventReturn OnPreCommand(CommandSource &source, Command *command, std::vector ¶ms) - { - if (command->name == "nickserv/register" || command->name == "nickserv/group") - { - if (DConfig.Check(DEFCON_NO_NEW_NICKS)) - { - source.Reply(_("Services are in Defcon mode, Please try again later.")); - return EVENT_STOP; - } - } - else if (command->name == "chanserv/set/mlock") - { - if (DConfig.Check(DEFCON_NO_MLOCK_CHANGE)) - { - source.Reply(_("Services are in Defcon mode, Please try again later.")); - return EVENT_STOP; - } - } - else if (command->name == "chanserv/register") - { - if (DConfig.Check(DEFCON_NO_NEW_CHANNELS)) - { - source.Reply(_("Services are in Defcon mode, Please try again later.")); - return EVENT_STOP; - } - } - else if (command->name == "memoserv/send") - { - if (DConfig.Check(DEFCON_NO_NEW_MEMOS)) - { - source.Reply(_("Services are in Defcon mode, Please try again later.")); - return EVENT_STOP; - } - } - - return EVENT_CONTINUE; - } - - void OnUserConnect(dynamic_reference &u, bool &exempt) - { - if (exempt || !u || !u->server->IsSynced() || u->server->IsULined()) - return; - - if (DConfig.Check(DEFCON_AKILL_NEW_CLIENTS) && akills) - { - Log(findbot(Config->OperServ), "operserv/defcon") << "DEFCON: adding akill for *@" << u->host; - XLine *x = akills->Add("*@" + u->host, Config->OperServ, Anope::CurTime + DConfig.akillexpire, DConfig.akillreason); - if (x) - x->By = Config->OperServ; - } - if (DConfig.Check(DEFCON_NO_NEW_CLIENTS) || DConfig.Check(DEFCON_AKILL_NEW_CLIENTS)) - { - u->Kill(Config->OperServ, DConfig.akillreason); - return; - } - - if (!DConfig.sessionlimit) - return; - - if (DConfig.Check(DEFCON_AKILL_NEW_CLIENTS) && akills) - { - Log(findbot(Config->OperServ), "operserv/defcon") << "DEFCON: adding akill for *@" << u->host; - XLine *x = akills->Add("*@" + u->host, Config->OperServ, Anope::CurTime + DConfig.akillexpire, !DConfig.akillreason.empty() ? DConfig.akillreason : "DEFCON AKILL"); - if (x) - x->By = Config->OperServ; - } - - if (DConfig.Check(DEFCON_NO_NEW_CLIENTS) || DConfig.Check(DEFCON_AKILL_NEW_CLIENTS)) - { - u->Kill(Config->OperServ, DConfig.akillreason); - return; - } - - Session *session = session_service->FindSession(u->host); - Exception *exception = session_service->FindException(u); - - if (DConfig.Check(DEFCON_REDUCE_SESSION) && !exception) - { - if (session && session->count > DConfig.sessionlimit) - { - if (!Config->SessionLimitExceeded.empty()) - ircdproto->SendMessage(findbot(Config->OperServ), u->nick, Config->SessionLimitExceeded.c_str(), u->host.c_str()); - if (!Config->SessionLimitDetailsLoc.empty()) - ircdproto->SendMessage(findbot(Config->OperServ), u->nick, "%s", Config->SessionLimitDetailsLoc.c_str()); - - u->Kill(Config->OperServ, "Defcon session limit exceeded"); - ++session->hits; - if (akills && Config->MaxSessionKill && session->hits >= Config->MaxSessionKill) - { - akills->Add("*@" + u->host, Config->OperServ, Anope::CurTime + Config->SessionAutoKillExpiry, "Defcon session limit exceeded"); - ircdproto->SendGlobops(findbot(Config->OperServ), "[DEFCON] Added a temporary AKILL for \2*@%s\2 due to excessive connections", u->host.c_str()); - } - } - } - } - - void OnChannelModeAdd(ChannelMode *cm) - { - if (DConfig.chanmodes.find(cm->ModeChar) != Anope::string::npos) - this->ParseModeString(); - } - - void OnChannelCreate(Channel *c) - { - if (DConfig.Check(DEFCON_FORCE_CHAN_MODES)) - c->SetModes(findbot(Config->OperServ), false, "%s", DConfig.chanmodes.c_str()); - } -}; - -/** - * Send a message to the oper about which precautions are "active" for this level - **/ -void defcon_sendlvls(CommandSource &source) -{ - if (DConfig.Check(DEFCON_NO_NEW_CHANNELS)) - source.Reply(_("* No new channel registrations")); - if (DConfig.Check(DEFCON_NO_NEW_NICKS)) - source.Reply(_("* No new nick registrations")); - if (DConfig.Check(DEFCON_NO_MLOCK_CHANGE)) - source.Reply(_("* No MLOCK changes")); - if (DConfig.Check(DEFCON_FORCE_CHAN_MODES) && !DConfig.chanmodes.empty()) - source.Reply(_("* Force Chan Modes (%s) to be set on all channels"), DConfig.chanmodes.c_str()); - if (DConfig.Check(DEFCON_REDUCE_SESSION)) - source.Reply(_("* Use the reduced session limit of %d"), DConfig.sessionlimit); - if (DConfig.Check(DEFCON_NO_NEW_CLIENTS)) - source.Reply(_("* Kill any NEW clients connecting")); - if (DConfig.Check(DEFCON_OPER_ONLY)) - source.Reply(_("* Ignore any non-opers with message")); - if (DConfig.Check(DEFCON_SILENT_OPER_ONLY)) - source.Reply(_("* Silently ignore non-opers")); - if (DConfig.Check(DEFCON_AKILL_NEW_CLIENTS)) - source.Reply(_("* AKILL any new clients connecting")); - if (DConfig.Check(DEFCON_NO_NEW_MEMOS)) - source.Reply(_("* No new memos sent")); -} - -void runDefCon() -{ - if (DConfig.Check(DEFCON_FORCE_CHAN_MODES)) - { - if (!DConfig.chanmodes.empty() && !DefConModesSet) - { - if (DConfig.chanmodes[0] == '+' || DConfig.chanmodes[0] == '-') - { - Log(findbot(Config->OperServ), "operserv/defcon") << "DEFCON: setting " << DConfig.chanmodes << " on all channels"; - DefConModesSet = true; - for (channel_map::const_iterator it = ChannelList.begin(), it_end = ChannelList.end(); it != it_end; ++it) - it->second->SetModes(findbot(Config->OperServ), false, "%s", DConfig.chanmodes.c_str()); - } - } - } - else - { - if (!DConfig.chanmodes.empty() && DefConModesSet) - { - if (DConfig.chanmodes[0] == '+' || DConfig.chanmodes[0] == '-') - { - DefConModesSet = false; - Anope::string newmodes = defconReverseModes(DConfig.chanmodes); - if (!newmodes.empty()) - { - Log(findbot(Config->OperServ), "operserv/defcon") << "DEFCON: setting " << newmodes << " on all channels"; - for (channel_map::const_iterator it = ChannelList.begin(), it_end = ChannelList.end(); it != it_end; ++it) - it->second->SetModes(findbot(Config->OperServ), false, "%s", newmodes.c_str()); - } - } - } - } -} - -static Anope::string defconReverseModes(const Anope::string &modes) -{ - if (modes.empty()) - return ""; - Anope::string newmodes; - for (unsigned i = 0, end = modes.length(); i < end; ++i) - { - if (modes[i] == '+') - newmodes += '-'; - else if (modes[i] == '-') - newmodes += '+'; - else - newmodes += modes[i]; - } - return newmodes; -} - -MODULE_INIT(OSDefcon) diff --git a/modules/pseudoclients/botserv.h b/modules/pseudoclients/botserv.h new file mode 100644 index 000000000..5fa03f870 --- /dev/null +++ b/modules/pseudoclients/botserv.h @@ -0,0 +1,63 @@ +#ifndef BOTSERV_H +#define BOTSERV_H + +struct UserData +{ + UserData() + { + this->Clear(); + } + + void Clear() + { + last_use = last_start = Anope::CurTime; + lines = times = 0; + lastline.clear(); + } + + /* Data validity */ + time_t last_use; + + /* for flood kicker */ + int16 lines; + time_t last_start; + + /* for repeat kicker */ + Anope::string lastline; + Anope::string lasttarget; + int16 times; +}; + +struct BanData +{ + Anope::string mask; + time_t last_use; + int16 ttb[TTB_SIZE]; + + BanData() + { + this->Clear(); + } + + void Clear() + { + last_use = 0; + for (int i = 0; i < TTB_SIZE; ++i) + this->ttb[i] = 0; + } +}; + +class BotServService : public Service +{ + public: + BotServService(Module *m) : Service(m, "BotServ") { } + + virtual UserData *GetUserData(User *u, Channel *c) = 0; + + virtual BanData *GetBanData(User *u, Channel *c) = 0; +}; + +static service_reference botserv("BotServ"); + +#endif // BOTSERV_H + diff --git a/modules/pseudoclients/bs_main.cpp b/modules/pseudoclients/bs_main.cpp new file mode 100644 index 000000000..69eab6040 --- /dev/null +++ b/modules/pseudoclients/bs_main.cpp @@ -0,0 +1,204 @@ +/* BotServ core functions + * + * (C) 2003-2011 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 BotServCore : public Module +{ + Channel *fantasy_channel; + public: + BotServCore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), fantasy_channel(NULL) + { + this->SetAuthor("Anope"); + + BotInfo *BotServ = findbot(Config->BotServ); + if (BotServ == NULL) + throw ModuleException("No bot named " + Config->BotServ); + + Implementation i[] = { I_OnPrivmsg, I_OnPreCommand, I_OnJoinChannel, I_OnLeaveChannel, + I_OnPreHelp, I_OnPostHelp, I_OnChannelModeSet }; + ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); + + } + + void OnPrivmsg(User *u, Channel *c, Anope::string &msg) + { + if (!u || !c || !c->ci || !c->ci->bi || msg.empty()) + return; + + /* Answer to ping if needed */ + if (msg.substr(0, 6).equals_ci("\1PING ") && msg[msg.length() - 1] == '\1') + { + Anope::string ctcp = msg; + ctcp.erase(ctcp.begin()); + ctcp.erase(ctcp.length() - 1); + ircdproto->SendCTCP(c->ci->bi, u->nick, "%s", ctcp.c_str()); + } + + + Anope::string realbuf = msg; + + bool was_action = false; + if (realbuf.substr(0, 8).equals_ci("\1ACTION ") && realbuf[realbuf.length() - 1] == '\1') + { + realbuf.erase(0, 8); + realbuf.erase(realbuf.length() - 1); + was_action = true; + } + + if (realbuf.empty()) + return; + + /* Fantaisist commands */ + if (c->ci->botflags.HasFlag(BS_FANTASY) && realbuf[0] == Config->BSFantasyCharacter[0] && !was_action) + { + /* Strip off the fantasy character */ + realbuf.erase(realbuf.begin()); + + size_t space = realbuf.find(' '); + Anope::string command, rest; + if (space == Anope::string::npos) + command = realbuf; + else + { + command = realbuf.substr(0, space); + rest = realbuf.substr(space + 1); + } + + BotInfo *bi = findbot(Config->ChanServ); + if (bi == NULL) + bi = findbot(Config->BotServ); + if (bi == NULL || !bi->commands.count(command)) + return; + + if (c->ci->HasPriv(u, CA_FANTASIA)) + { + + this->fantasy_channel = c; + bi->OnMessage(u, realbuf); + this->fantasy_channel = NULL; + + FOREACH_MOD(I_OnBotFantasy, OnBotFantasy(command, u, c->ci, rest)); + } + else + { + FOREACH_MOD(I_OnBotNoFantasyAccess, OnBotNoFantasyAccess(command, u, c->ci, rest)); + } + } + } + + EventReturn OnPreCommand(CommandSource &source, Command *command, std::vector ¶ms) + { + if (this->fantasy_channel != NULL) + { + if (!command->HasFlag(CFLAG_STRIP_CHANNEL)) + params.insert(params.begin(), this->fantasy_channel->name); + source.c = this->fantasy_channel; + } + + return EVENT_CONTINUE; + } + + void OnJoinChannel(User *user, Channel *c) + { + if (c->ci && c->ci->bi) + { + /** + * We let the bot join even if it was an ignored user, as if we don't, + * and the ignored user doesnt just leave, the bot will never + * make it into the channel, leaving the channel botless even for + * legit users - Rob + **/ + if (c->users.size() >= Config->BSMinUsers && !c->FindUser(c->ci->bi)) + c->ci->bi->Join(c, &Config->BotModeList); + /* 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->FindUser(c->ci->bi) && c->ci->botflags.HasFlag(BS_GREET) && user->Account() && !user->Account()->greet.empty() && c->ci->HasPriv(user, CA_GREET) && user->server->IsSynced()) + { + ircdproto->SendPrivmsg(c->ci->bi, c->name, "[%s] %s", user->Account()->display.c_str(), user->Account()->greet.c_str()); + c->ci->bi->lastmsg = Anope::CurTime; + } + } + } + + void OnLeaveChannel(User *u, Channel *c) + { + /* Channel is persistant, it shouldn't be deleted and the service bot should stay */ + if (c->HasFlag(CH_PERSIST) || (c->ci && c->ci->HasFlag(CI_PERSIST))) + return; + + /* Channel is syncing from a netburst, don't destroy it as more users are probably wanting to join immediatly + * We also don't part the bot here either, if necessary we will part it after the sync + */ + if (c->HasFlag(CH_SYNCING)) + return; + + /* Additionally, do not delete this channel if ChanServ/a BotServ bot is inhabiting it */ + if (c->HasFlag(CH_INHABIT)) + return; + + if (c->ci && c->ci->bi && c->users.size() - 1 <= Config->BSMinUsers && c->FindUser(c->ci->bi)) + { + bool persist = c->HasFlag(CH_PERSIST); + c->SetFlag(CH_PERSIST); + c->ci->bi->Part(c->ci->c); + if (!persist) + c->UnsetFlag(CH_PERSIST); + } + } + + void OnPreHelp(CommandSource &source, const std::vector ¶ms) + { + if (!params.empty() || source.owner->nick != Config->BotServ) + return; + source.Reply(_("\002%s\002 allows you to have a bot on your own channel.\n" + "It has been created for users that can't host or\n" + "configure a bot, or for use on networks that don't\n" + "allow user bots. Available commands are listed \n" + "below; to use them, type \002%s%s \037command\037\002. For\n" + "more information on a specific command, type\n" + "\002%s%s %s \037command\037\002.\n "), + Config->BotServ.c_str(), Config->UseStrictPrivMsgString.c_str(), Config->BotServ.c_str(), + Config->UseStrictPrivMsgString.c_str(), Config->BotServ.c_str(), source.command.c_str()); + } + + void OnPostHelp(CommandSource &source, const std::vector ¶ms) + { + if (!params.empty() || source.owner->nick != Config->BotServ) + return; + source.Reply(_(" \n" + "Bot will join a channel whenever there is at least\n" + "\002%d\002 user(s) on it. Additionally, all %s commands\n" + "can be used if fantasy is enabled by prefixing the command\n" + "name with a %c."), Config->BSMinUsers, Config->ChanServ.c_str(), Config->BSFantasyCharacter[0]); + } + + EventReturn OnChannelModeSet(Channel *c, ChannelModeName Name, const Anope::string ¶m) + { + if (Config->BSSmartJoin && Name == CMODE_BAN && c->ci && c->ci->bi && c->FindUser(c->ci->bi)) + { + BotInfo *bi = c->ci->bi; + + Entry ban(CMODE_BAN, param); + if (ban.Matches(bi)) + c->RemoveMode(bi, CMODE_BAN, param); + } + + return EVENT_CONTINUE; + } +}; + +MODULE_INIT(BotServCore) + diff --git a/modules/pseudoclients/cs_main.cpp b/modules/pseudoclients/cs_main.cpp new file mode 100644 index 000000000..5c490e4ff --- /dev/null +++ b/modules/pseudoclients/cs_main.cpp @@ -0,0 +1,143 @@ +/* ChanServ core functions + * + * (C) 2003-2011 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 ChanServCore : public Module +{ + public: + ChanServCore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE) + { + this->SetAuthor("Anope"); + + BotInfo *ChanServ = findbot(Config->ChanServ); + if (ChanServ == NULL) + throw ModuleException("No bot named " + Config->ChanServ); + + Implementation i[] = { I_OnBotPrivmsg, I_OnDelCore, I_OnPreHelp, I_OnPostHelp }; + ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); + } + + EventReturn OnBotPrivmsg(User *u, BotInfo *bi, Anope::string &message) + { + if (Config->CSOpersOnly && !u->HasMode(UMODE_OPER) && bi->nick == Config->ChanServ) + { + u->SendMessage(bi, ACCESS_DENIED); + return EVENT_STOP; + } + + return EVENT_CONTINUE; + } + + void OnDelCore(NickCore *nc) + { + // XXX this is slightly inefficient + for (registered_channel_map::const_iterator it = RegisteredChannelList.begin(), it_end = RegisteredChannelList.end(); it != it_end;) + { + ChannelInfo *ci = it->second; + ++it; + + if (ci->GetFounder() == nc) + { + NickCore *newowner = NULL; + if (ci->successor && (ci->successor->IsServicesOper() || !Config->CSMaxReg || ci->successor->channelcount < Config->CSMaxReg)) + newowner = ci->successor; + else + { + ChanAccess *highest = NULL; + for (unsigned j = 0; j < ci->GetAccessCount(); ++j) + { + ChanAccess *ca = ci->GetAccess(j); + NickCore *anc = findcore(ca->mask); + + if (!anc || (!anc->IsServicesOper() && Config->CSMaxReg && anc->channelcount >= Config->CSMaxReg) || (anc == nc)) + continue; + if (!highest || *ca > *highest) + highest = ca; + } + if (highest) + newowner = findcore(highest->mask); + } + + if (newowner) + { + Log(LOG_NORMAL, "chanserv/expire") << "Transferring foundership of " << ci->name << " from deleted nick " << nc->display << " to " << newowner->display; + ci->SetFounder(newowner); + ci->successor = NULL; + } + else + { + Log(LOG_NORMAL, "chanserv/expire") << "Deleting channel " << ci->name << " owned by deleted nick " << nc->display; + + delete ci; + continue; + } + } + + if (ci->successor == nc) + ci->successor = NULL; + + for (unsigned j = 0; j < ci->GetAccessCount(); ++j) + { + ChanAccess *ca = ci->GetAccess(j); + NickCore *anc = findcore(ca->mask); + + if (anc && anc == nc) + { + ci->EraseAccess(j); + break; + } + } + + for (unsigned j = ci->GetAkickCount(); j > 0; --j) + { + AutoKick *akick = ci->GetAkick(j - 1); + if (akick->HasFlag(AK_ISNICK) && akick->nc == nc) + ci->EraseAkick(j - 1); + } + } + } + + void OnPreHelp(CommandSource &source, const std::vector ¶ms) + { + if (!params.empty() || source.owner->nick != Config->ChanServ) + return; + 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 "), + Config->ChanServ.c_str(), Config->ChanServ.c_str(), Config->UseStrictPrivMsgString.c_str(), Config->ChanServ.c_str(), Config->UseStrictPrivMsgString.c_str(), Config->ChanServ.c_str(), Config->ChanServ.c_str(), source.command.c_str()); + } + + void OnPostHelp(CommandSource &source, const std::vector ¶ms) + { + if (!params.empty() || source.owner->nick != Config->ChanServ) + return; + if (Config->CSExpire >= 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."), Config->CSExpire / 86400); + if (source.u->IsServicesOper()) + source.Reply(_(" \n" + "Services Operators can also drop any channel without needing\n" + "to identify via password, and may view the access, akick,\n" + "and level setting lists for any channel.")); + } +}; + +MODULE_INIT(ChanServCore) + diff --git a/modules/pseudoclients/gl_main.cpp b/modules/pseudoclients/gl_main.cpp new file mode 100644 index 000000000..c7ccf9209 --- /dev/null +++ b/modules/pseudoclients/gl_main.cpp @@ -0,0 +1,93 @@ +/* Global core functions + * + * (C) 2003-2011 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 "global.h" + +class MyGlobalService : public GlobalService +{ + void ServerGlobal(Server *s, const Anope::string &message) + { + if (s != Me && !s->HasFlag(SERVER_JUPED)) + notice_server(Config->Global, s, "%s", message.c_str()); + for (unsigned i = 0, j = s->GetLinks().size(); i < j; ++i) + this->ServerGlobal(s->GetLinks()[i], message); + } + + public: + MyGlobalService(Module *m) : GlobalService(m) { } + + void SendGlobal(BotInfo *sender, const Anope::string &source, const Anope::string &message) + { + if (Me->GetLinks().empty()) + return; + + Anope::string rmessage; + + if (!source.empty() && !Config->AnonymousGlobal) + rmessage = "[" + source + "] " + message; + else + rmessage = message; + + this->ServerGlobal(Me->GetLinks().front(), rmessage); + } +}; + +class GlobalCore : public Module +{ + MyGlobalService myglobalservice; + + public: + GlobalCore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + myglobalservice(this) + { + this->SetAuthor("Anope"); + + BotInfo *Global = findbot(Config->Global); + if (Global == NULL) + throw ModuleException("No bot named " + Config->Global); + + ModuleManager::RegisterService(&this->myglobalservice); + + Implementation i[] = { I_OnRestart, I_OnShutdown, I_OnNewServer, I_OnPreHelp }; + ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); + } + + void OnRestart() + { + if (Config->GlobalOnCycle) + global->SendGlobal(findbot(Config->Global), "", Config->GlobalOnCycleMessage); + } + + void OnShutdown() + { + if (Config->GlobalOnCycle) + global->SendGlobal(findbot(Config->Global), "", Config->GlobalOnCycleMessage); + } + + void OnNewServer(Server *s) + { + if (Config->GlobalOnCycle && !Config->GlobalOnCycleUP.empty()) + notice_server(Config->Global, s, "%s", Config->GlobalOnCycleUP.c_str()); + } + + void OnPreHelp(CommandSource &source, const std::vector ¶ms) + { + if (!params.empty() || source.owner->nick != Config->Global) + return; + source.Reply(_("%s commands:\n"), Config->Global.c_str()); + } +}; + +MODULE_INIT(GlobalCore) + diff --git a/modules/pseudoclients/global.h b/modules/pseudoclients/global.h new file mode 100644 index 000000000..454e879c2 --- /dev/null +++ b/modules/pseudoclients/global.h @@ -0,0 +1,20 @@ +#ifndef GLOBAL_H +#define GLOBAL_H + +class GlobalService : public Service +{ + public: + GlobalService(Module *m) : Service(m, "Global") { } + + /** Send out a global message to all users + * @param sender Our client which should send the global + * @param source The sender of the global + * @param message The message + */ + virtual void SendGlobal(BotInfo *sender, const Anope::string &source, const Anope::string &message) = 0; +}; + +static service_reference global("Global"); + +#endif // GLOBAL_H + diff --git a/modules/pseudoclients/hs_main.cpp b/modules/pseudoclients/hs_main.cpp new file mode 100644 index 000000000..a6cc6bbb7 --- /dev/null +++ b/modules/pseudoclients/hs_main.cpp @@ -0,0 +1,85 @@ +/* HostServ core functions + * + * (C) 2003-2011 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 +{ + public: + HostServCore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE) + { + this->SetAuthor("Anope"); + + if (!ircd || !ircd->vhost) + throw ModuleException("Your IRCd does not suppor vhosts"); + + BotInfo *HostServ = findbot(Config->HostServ); + if (HostServ == NULL) + throw ModuleException("No bot named " + Config->HostServ); + + Implementation i[] = { I_OnNickIdentify, I_OnNickUpdate, I_OnPreHelp }; + ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); + } + + void OnNickIdentify(User *u) + { + HostInfo *ho = NULL; + NickAlias *na = findnick(u->nick); + if (na && na->hostinfo.HasVhost()) + ho = &na->hostinfo; + else + { + na = findnick(u->Account()->display); + if (na && na->hostinfo.HasVhost()) + ho = &na->hostinfo; + } + if (ho == NULL) + return; + + if (u->vhost.empty() || !u->vhost.equals_cs(na->hostinfo.GetHost()) || (!na->hostinfo.GetIdent().empty() && !u->GetVIdent().equals_cs(na->hostinfo.GetIdent()))) + { + ircdproto->SendVhost(u, na->hostinfo.GetIdent(), na->hostinfo.GetHost()); + if (ircd->vhost) + { + u->vhost = na->hostinfo.GetHost(); + u->UpdateHost(); + } + if (ircd->vident && !na->hostinfo.GetIdent().empty()) + u->SetVIdent(na->hostinfo.GetIdent()); + + BotInfo *bi = findbot(Config->HostServ); + if (bi) + { + if (!na->hostinfo.GetIdent().empty()) + u->SendMessage(bi, _("Your vhost of \002%s\002@\002%s\002 is now activated."), na->hostinfo.GetIdent().c_str(), na->hostinfo.GetHost().c_str()); + else + u->SendMessage(bi, _("Your vhost of \002%s\002 is now activated."), na->hostinfo.GetHost().c_str()); + } + } + } + + void OnNickUpdate(User *u) + { + this->OnNickIdentify(u); + } + + void OnPreHelp(CommandSource &source, const std::vector ¶ms) + { + if (!params.empty() || source.owner->nick != Config->HostServ) + return; + source.Reply(_("%s commands:\n"), Config->HostServ.c_str()); + } +}; + +MODULE_INIT(HostServCore) + diff --git a/modules/pseudoclients/memoserv.h b/modules/pseudoclients/memoserv.h new file mode 100644 index 000000000..0bf0c79ca --- /dev/null +++ b/modules/pseudoclients/memoserv.h @@ -0,0 +1,41 @@ +#ifndef MEMOSERV_H +#define MEMOSERV_H + +class MemoServService : public Service +{ + public: + enum MemoResult + { + MEMO_SUCCESS, + MEMO_INVALID_TARGET, + MEMO_TOO_FAST, + MEMO_TARGET_FULL + }; + + MemoServService(Module *m) : Service(m, "MemoServ") { } + + /** Retrieve the memo info for a nick or channel + * @param target Target + * @param ischan Set to true if target is a channel + * @return A memoinfo structure or NULL + */ + virtual MemoInfo *GetMemoInfo(const Anope::string &target, bool &ischan) = 0; + + /** Sends a memo. + * @param source The source of the memo, can be anythin. + * @param target The target of the memo, nick or channel. + * @param message Memo text + * @param force true to force the memo, restrictions/delays etc are not checked + */ + virtual MemoResult Send(const Anope::string &source, const Anope::string &target, const Anope::string &message, bool force = false) = 0; + + /** Check for new memos and notify the user if there are any + * @param u The user + */ + virtual void Check(User *u) = 0; +}; + +static service_reference memoserv("MemoServ"); + +#endif // MEMOSERV_H + diff --git a/modules/pseudoclients/ms_main.cpp b/modules/pseudoclients/ms_main.cpp new file mode 100644 index 000000000..bb6d7847e --- /dev/null +++ b/modules/pseudoclients/ms_main.cpp @@ -0,0 +1,228 @@ +/* MemoServ core functions + * + * (C) 2003-2011 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 "memoserv.h" + +static BotInfo *MemoServ; + +static bool SendMemoMail(NickCore *nc, MemoInfo *mi, Memo *m) +{ + Anope::string message = Anope::printf(translate(nc, _( + "Hi %s\n" + " \n" + "You've just received a new memo from %s. This is memo number %d.\n" + " \n" + "Memo text:\n" + " \n" + "%s")), nc->display.c_str(), m->sender.c_str(), mi->GetIndex(m), m->text.c_str()); + + return Mail(nc, translate(nc, _("New memo")), message); +} + +class MyMemoServService : public MemoServService +{ + public: + MyMemoServService(Module *m) : MemoServService(m) { } + + MemoInfo *GetMemoInfo(const Anope::string &target, bool &ischan) + { + if (!target.empty() && target[0] == '#') + { + ischan = true; + ChannelInfo *ci = cs_findchan(target); + if (ci != NULL) + return &ci->memos; + } + else + { + ischan = false; + NickAlias *na = findnick(target); + if (na != NULL) + return &na->nc->memos; + } + + return NULL; + } + + MemoResult Send(const Anope::string &source, const Anope::string &target, const Anope::string &message, bool force) + { + bool ischan; + MemoInfo *mi = this->GetMemoInfo(target, ischan); + + if (mi == NULL) + return MEMO_INVALID_TARGET; + + User *sender = finduser(source); + if (sender != NULL && !sender->HasPriv("memoserv/no-limit") && !force) + { + if (Config->MSSendDelay > 0 && sender->lastmemosend + Config->MSSendDelay > Anope::CurTime) + return MEMO_TOO_FAST; + else if (!mi->memomax) + return MEMO_TARGET_FULL; + else if (mi->memomax > 0 && mi->memos.size() >= 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(); + mi->memos.push_back(m); + m->sender = source; + m->time = Anope::CurTime; + m->text = message; + m->SetFlag(MF_UNREAD); + + FOREACH_MOD(I_OnMemoSend, OnMemoSend(source, target, mi, m)); + + if (ischan) + { + ChannelInfo *ci = cs_findchan(target); + + if (ci->c) + { + for (CUserList::iterator it = ci->c->users.begin(), it_end = ci->c->users.end(); it != it_end; ++it) + { + UserContainer *cu = *it; + + if (ci->HasPriv(cu->user, CA_MEMO)) + { + if (cu->user->Account() && cu->user->Account()->HasFlag(NI_MEMO_RECEIVE)) + cu->user->SendMessage(MemoServ, MEMO_NEW_X_MEMO_ARRIVED, ci->name.c_str(), Config->UseStrictPrivMsgString.c_str(), Config->MemoServ.c_str(), ci->name.c_str(), mi->memos.size()); + } + } + } + } + else + { + NickCore *nc = findnick(target)->nc; + + if (nc->HasFlag(NI_MEMO_RECEIVE)) + { + for (std::list::iterator it = nc->aliases.begin(), it_end = nc->aliases.end(); it != it_end; ++it) + { + NickAlias *na = *it; + User *user = finduser(na->nick); + if (user && user->IsIdentified()) + user->SendMessage(MemoServ, MEMO_NEW_MEMO_ARRIVED, source.c_str(), Config->UseStrictPrivMsgString.c_str(), Config->MemoServ.c_str(), mi->memos.size()); + } + } + + /* let's get out the mail if set in the nickcore - certus */ + if (nc->HasFlag(NI_MEMO_MAIL)) + SendMemoMail(nc, mi, m); + } + + return MEMO_SUCCESS; + } + + void Check(User *u) + { + NickCore *nc = u->Account(); + if (!nc) + return; + + unsigned i = 0, end = nc->memos.memos.size(), newcnt = 0; + for (; i < end; ++i) + { + if (nc->memos.memos[i]->HasFlag(MF_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() >= nc->memos.memomax) + { + if (nc->memos.memos.size() > 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); + } + } +}; + +class MemoServCore : public Module +{ + MyMemoServService mymemoserv; + public: + MemoServCore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + mymemoserv(this) + { + this->SetAuthor("Anope"); + + MemoServ = findbot(Config->MemoServ); + if (MemoServ == NULL) + throw ModuleException("No bot named " + Config->MemoServ); + + Implementation i[] = { I_OnNickIdentify, I_OnJoinChannel, I_OnUserAway, I_OnNickUpdate, I_OnPreHelp, I_OnPostHelp }; + ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); + + ModuleManager::RegisterService(&this->mymemoserv); + } + + void OnNickIdentify(User *u) + { + this->mymemoserv.Check(u); + } + + void OnJoinChannel(User *u, Channel *c) + { + if (c->ci && c->ci->HasPriv(u, CA_MEMO) && c->ci->memos.memos.size() > 0) + { + 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) + { + if (message.empty()) + this->mymemoserv.Check(u); + } + + void OnNickUpdate(User *u) + { + this->mymemoserv.Check(u); + } + + void OnPreHelp(CommandSource &source, const std::vector ¶ms) + { + if (!params.empty() || source.owner->nick != Config->MemoServ) + return; + 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:"), Config->MemoServ.c_str(), Config->MemoServ.c_str()); + } + + void OnPostHelp(CommandSource &source, const std::vector ¶ms) + { + if (!params.empty() || source.owner->nick != Config->MemoServ) + return; + source.Reply(_(" \n" + "Type \002%s%s HELP \037command\037\002 for help on any of the\n" + "above commands.\n" + "(*) By default, any user with at least level 10 access on a\n" + " channel can read that channel's memos. This can be\n" + " changed with the %s \002LEVELS\002 command."), Config->UseStrictPrivMsgString.c_str(), Config->MemoServ.c_str(), Config->ChanServ.c_str()); + } +}; + +MODULE_INIT(MemoServCore) + diff --git a/modules/pseudoclients/nickserv.h b/modules/pseudoclients/nickserv.h new file mode 100644 index 000000000..b58dbd65f --- /dev/null +++ b/modules/pseudoclients/nickserv.h @@ -0,0 +1,15 @@ +#ifndef NICKSERV_H +#define NICKSERV_H + +class NickServService : public Service +{ + public: + NickServService(Module *m) : Service(m, "NickServ") { } + + virtual void Validate(User *u) = 0; +}; + +static service_reference nickserv("NickServ"); + +#endif // NICKSERV_H + diff --git a/modules/pseudoclients/ns_main.cpp b/modules/pseudoclients/ns_main.cpp new file mode 100644 index 000000000..57323d0a5 --- /dev/null +++ b/modules/pseudoclients/ns_main.cpp @@ -0,0 +1,307 @@ +/* NickServ core functions + * + * (C) 2003-2011 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 "nickserv.h" + +static BotInfo *NickServ; + +class MyNickServService : public NickServService +{ + public: + MyNickServService(Module *m) : NickServService(m) { } + + void Validate(User *u) + { + NickAlias *na = findnick(u->nick); + if (!na) + return; + + if (na->nc->HasFlag(NI_SUSPENDED)) + { + u->SendMessage(NickServ, NICK_X_SUSPENDED, u->nick.c_str()); + u->Collide(na); + return; + } + + if (!u->IsIdentified() && !u->fingerprint.empty() && na->nc->FindCert(u->fingerprint)) + { + u->SendMessage(NickServ, _("SSL Fingerprint accepted, you are now identified.")); + u->Identify(na); + return; + } + + if (!na->nc->HasFlag(NI_SECURE) && u->IsRecognized()) + { + na->last_seen = Anope::CurTime; + Anope::string last_usermask = u->GetIdent() + "@" + u->GetDisplayedHost(); + na->last_usermask = last_usermask; + na->last_realname = u->realname; + return; + } + + if (u->IsRecognized() || !na->nc->HasFlag(NI_KILL_IMMED)) + { + if (na->nc->HasFlag(NI_SECURE)) + u->SendMessage(NickServ, NICK_IS_SECURE, Config->UseStrictPrivMsgString.c_str(), Config->NickServ.c_str()); + else + u->SendMessage(NickServ, NICK_IS_REGISTERED, Config->UseStrictPrivMsgString.c_str(), Config->NickServ.c_str()); + } + + if (na->nc->HasFlag(NI_KILLPROTECT) && !u->IsRecognized()) + { + if (na->nc->HasFlag(NI_KILL_IMMED)) + { + u->SendMessage(NickServ, FORCENICKCHANGE_NOW); + u->Collide(na); + } + else if (na->nc->HasFlag(NI_KILL_QUICK)) + { + u->SendMessage(NickServ, _("If you do not change within 20 seconds, I will change your nick.")); + new NickServCollide(u, 20); + } + else + { + u->SendMessage(NickServ, _("If you do not change within one minute, I will change your nick.")); + new NickServCollide(u, 60); + } + } + + } +}; + +class ExpireCallback : public CallBack +{ + public: + ExpireCallback(Module *owner) : CallBack(owner, Config->ExpireTimeout, Anope::CurTime, true) { } + + void Tick(time_t) + { + if (noexpire || readonly) + return; + + for (nickalias_map::const_iterator it = NickAliasList.begin(), it_end = NickAliasList.end(); it != it_end; ) + { + NickAlias *na = it->second; + ++it; + + User *u = finduser(na->nick); + if (u && (na->nc->HasFlag(NI_SECURE) ? u->IsIdentified(true) : u->IsRecognized(true))) + na->last_seen = Anope::CurTime; + + bool expire = false; + + if (na->nc->HasFlag(NI_UNCONFIRMED)) + if (Config->NSUnconfirmedExpire && Anope::CurTime - na->time_registered >= Config->NSUnconfirmedExpire) + expire = true; + if (na->nc->HasFlag(NI_SUSPENDED)) + { + if (Config->NSSuspendExpire && Anope::CurTime - na->last_seen >= Config->NSSuspendExpire) + expire = true; + } + else if (Config->NSExpire && Anope::CurTime - na->last_seen >= Config->NSExpire) + expire = true; + if (na->HasFlag(NS_NO_EXPIRE)) + expire = false; + + if (expire) + { + EventReturn MOD_RESULT; + FOREACH_RESULT(I_OnPreNickExpire, OnPreNickExpire(na)); + if (MOD_RESULT == EVENT_STOP) + continue; + Anope::string extra; + if (na->nc->HasFlag(NI_SUSPENDED)) + extra = "suspended "; + Log(LOG_NORMAL, "expire") << "Expiring " << extra << "nickname " << na->nick << " (group: " << na->nc->display << ") (e-mail: " << (na->nc->email.empty() ? "none" : na->nc->email) << ")"; + FOREACH_MOD(I_OnNickExpire, OnNickExpire(na)); + delete na; + } + } + } +}; + +class NickServCore : public Module +{ + MyNickServService mynickserv; + ExpireCallback expires; + + public: + NickServCore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), mynickserv(this), expires(this) + { + this->SetAuthor("Anope"); + + NickServ = findbot(Config->NickServ); + if (NickServ == NULL) + throw ModuleException("No bot named " + Config->NickServ); + + Implementation i[] = { I_OnDelNick, I_OnDelCore, I_OnChangeCoreDisplay, I_OnNickIdentify, I_OnNickGroup, + I_OnNickUpdate, I_OnUserNickChange, I_OnPreHelp, I_OnPostHelp }; + ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); + + ModuleManager::RegisterService(&this->mynickserv); + } + + void OnDelNick(NickAlias *na) + { + User *u = finduser(na->nick); + if (u && u->Account() == na->nc) + { + u->RemoveMode(NickServ, UMODE_REGISTERED); + ircdproto->SendAccountLogout(u, u->Account()); + ircdproto->SendUnregisteredNick(u); + u->Logout(); + } + } + + void OnDelCore(NickCore *nc) + { + Log(NickServ, "nick") << "deleting nickname group " << nc->display; + + /* Clean up this nick core from any users online using it + * (ones that /nick but remain unidentified) + */ + for (std::list::iterator it = nc->Users.begin(); it != nc->Users.end();) + { + User *user = *it++; + ircdproto->SendAccountLogout(user, user->Account()); + user->RemoveMode(NickServ, UMODE_REGISTERED); + ircdproto->SendUnregisteredNick(user); + user->Logout(); + FOREACH_MOD(I_OnNickLogout, OnNickLogout(user)); + } + nc->Users.clear(); + } + + void OnChangeCoreDisplay(NickCore *nc, const Anope::string &newdisplay) + { + Log(LOG_NORMAL, "nick", NickServ) << "Changing " << nc->display << " nickname group display to " << newdisplay; + } + + void OnNickIdentify(User *u) + { + NickAlias *this_na = findnick(u->nick); + if (this_na && this_na->nc == u->Account() && u->Account()->HasFlag(NI_UNCONFIRMED) == false) + u->SetMode(NickServ, UMODE_REGISTERED); + + if (Config->NSModeOnID) + for (UChannelList::iterator it = u->chans.begin(), it_end = u->chans.end(); it != it_end; ++it) + { + ChannelContainer *cc = *it; + Channel *c = cc->chan; + if (c) + chan_set_correct_modes(u, c, 1); + } + + if (Config->NSForceEmail && 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->UseStrictPrivMsgString.c_str(), Config->NickServ.c_str()); + } + + if (u->Account()->HasFlag(NI_UNCONFIRMED)) + { + u->SendMessage(NickServ, _("Your email address is not confirmed. To confirm it, follow the instructions that were emailed to you when you registered.")); + this_na = findnick(u->Account()->display); + time_t time_registered = Anope::CurTime - this_na->time_registered; + if (Config->NSUnconfirmedExpire > time_registered) + u->SendMessage(NickServ, _("Your account will expire, if not confirmed, in %s"), duration(Config->NSUnconfirmedExpire - time_registered).c_str()); + } + } + + void OnNickGroup(User *u, NickAlias *target) + { + if (target->nc->HasFlag(NI_UNCONFIRMED) == false) + u->SetMode(NickServ, UMODE_REGISTERED); + } + + void OnNickUpdate(User *u) + { + for (UChannelList::iterator it = u->chans.begin(), it_end = u->chans.end(); it != it_end; ++it) + { + ChannelContainer *cc = *it; + Channel *c = cc->chan; + if (c) + chan_set_correct_modes(u, c, 1); + } + } + + void OnUserNickChange(User *u, const Anope::string &oldnick) + { + NickAlias *na = findnick(u->nick); + /* If the new nick isnt registerd or its registerd and not yours */ + if (!na || na->nc != u->Account()) + { + u->RemoveMode(NickServ, UMODE_REGISTERED); + ircdproto->SendUnregisteredNick(u); + + this->mynickserv.Validate(u); + } + else + { + if (na->nc->HasFlag(NI_UNCONFIRMED) == false) + { + u->SetMode(NickServ, UMODE_REGISTERED); + ircdproto->SetAutoIdentificationToken(u); + } + Log(NickServ) << u->GetMask() << " automatically identified for group " << u->Account()->display; + } + } + + void OnUserModeSet(User *u, UserModeName Name) + { + if (Name == UMODE_REGISTERED && !u->IsIdentified()) + u->RemoveMode(NickServ, Name); + } + + void OnPreHelp(CommandSource &source, const std::vector ¶ms) + { + if (!params.empty() || source.owner->nick != Config->NickServ) + return; + 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 "), Config->NickServ.c_str(), Config->UseStrictPrivMsgString.c_str(), Config->NickServ.c_str(), Config->UseStrictPrivMsgString.c_str(), Config->NickServ.c_str(), source.command.c_str()); + } + + void OnPostHelp(CommandSource &source, const std::vector ¶ms) + { + if (!params.empty() || source.owner->nick != Config->NickServ) + return; + if (source.u->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.")); + if (Config->NSExpire >= 86400) + source.Reply(_("Nicknames 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."), Config->NSExpire / 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)."), Config->NickServ.c_str()); + } +}; + +MODULE_INIT(NickServCore) + diff --git a/modules/pseudoclients/os_main.cpp b/modules/pseudoclients/os_main.cpp new file mode 100644 index 000000000..b555b9ede --- /dev/null +++ b/modules/pseudoclients/os_main.cpp @@ -0,0 +1,371 @@ +/* OperServ core functions + * + * (C) 2003-2011 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 BotInfo *OperServ; + +class SGLineManager : public XLineManager +{ + public: + SGLineManager(Module *creator) : XLineManager(creator, "xlinemanager/sgline", 'G') { } + + XLine *Add(const Anope::string &mask, const Anope::string &creator, time_t expires, const Anope::string &reason) + { + Anope::string realreason = reason; + if (!creator.empty() && Config->AddAkiller) + realreason = "[" + creator + "] " + reason; + + XLine *x = new XLine(mask, creator, expires, realreason); + + this->AddXLine(x); + + if (UplinkSock && Config->AkillOnAdd) + this->Send(NULL, x); + + return x; + } + + void Del(XLine *x) + { + ircdproto->SendAkillDel(x); + } + + void OnMatch(User *u, XLine *x) + { + if (u) + u->Kill(Config->OperServ, x->Reason); + ircdproto->SendAkill(u, x); + } + + void OnExpire(XLine *x) + { + if (Config->WallAkillExpire) + ircdproto->SendGlobops(OperServ, "AKILL on %s has expired", x->Mask.c_str()); + } + + void Send(User *u, XLine *x) + { + ircdproto->SendAkill(u, x); + } +}; + +class SZLineManager : public XLineManager +{ + public: + SZLineManager(Module *creator) : XLineManager(creator, "xlinemanager/szline", 'Z') { } + + XLine *Add(const Anope::string &mask, const Anope::string &creator, time_t expires, const Anope::string &reason) + { + XLine *x = new XLine(mask, creator, expires, reason); + + this->AddXLine(x); + + if (UplinkSock) + this->Send(NULL, x); + + return x; + } + + void Del(XLine *x) + { + ircdproto->SendSZLineDel(x); + } + + void OnMatch(User *u, XLine *x) + { + if (u) + { + Anope::string reason = "Z-Lined: " + x->Reason; + u->Kill(Config->OperServ, reason); + } + + ircdproto->SendSZLine(u, x); + } + + void OnExpire(XLine *x) + { + if (Config->WallSZLineExpire) + ircdproto->SendGlobops(OperServ, "SZLINE on \2%s\2 has expired", x->Mask.c_str()); + } + + void Send(User *u, XLine *x) + { + ircdproto->SendSZLine(u, x); + } +}; + +class SQLineManager : public XLineManager +{ + public: + SQLineManager(Module *creator) : XLineManager(creator, "xlinemanager/sqline", 'Q') { } + + XLine *Add(const Anope::string &mask, const Anope::string &creator, time_t expires, const Anope::string &reason) + { + XLine *x = new XLine(mask, creator, expires, reason); + + this->AddXLine(x); + + if (Config->KillonSQline) + { + 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)) + continue; + for (CUserList::iterator it = c->users.begin(), it_end = c->users.end(); it != it_end; ) + { + UserContainer *uc = *it; + ++it; + + if (uc->user->HasMode(UMODE_OPER) || uc->user->server == Me) + continue; + c->Kick(NULL, uc->user, "%s", reason.c_str()); + } + } + } + else + { + for (Anope::insensitive_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end();) + { + User *user = it->second; + ++it; + + if (!user->HasMode(UMODE_OPER) && user->server != Me && Anope::Match(user->nick, x->Mask)) + user->Kill(Config->ServerName, rreason); + } + } + } + + if (UplinkSock) + this->Send(NULL, x); + + return x; + } + + void Del(XLine *x) + { + ircdproto->SendSQLineDel(x); + } + + void OnMatch(User *u, XLine *x) + { + if (u) + { + Anope::string reason = "Q-Lined: " + x->Reason; + u->Kill(Config->OperServ, reason); + } + + this->Send(u, x); + } + + void OnExpire(XLine *x) + { + if (Config->WallSQLineExpire) + ircdproto->SendGlobops(OperServ, "SQLINE on \2%s\2 has expired", x->Mask.c_str()); + } + + void Send(User *u, XLine *x) + { + ircdproto->SendSQLine(u, x); + } + + bool CheckChannel(Channel *c) + { + if (ircd->chansqline) + for (std::vector::const_iterator it = this->GetList().begin(), it_end = this->GetList().end(); it != it_end; ++it) + if (Anope::Match(c->name, (*it)->Mask)) + return true; + return false; + } +}; + +class SNLineManager : public XLineManager +{ + public: + SNLineManager(Module *creator) : XLineManager(creator, "xlinemanager/snline", 'N') { } + + XLine *Add(const Anope::string &mask, const Anope::string &creator, time_t expires, const Anope::string &reason) + { + XLine *x = new XLine(mask, creator, expires, reason); + + this->AddXLine(x); + + if (Config->KillonSNline && !ircd->sglineenforce) + { + Anope::string rreason = "G-Lined: " + reason; + + for (Anope::insensitive_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end();) + { + User *user = it->second; + ++it; + + if (!user->HasMode(UMODE_OPER) && user->server != Me && Anope::Match(user->realname, x->Mask)) + user->Kill(Config->ServerName, rreason); + } + } + + return x; + } + + void Del(XLine *x) + { + ircdproto->SendSGLineDel(x); + } + + void OnMatch(User *u, XLine *x) + { + if (u) + { + Anope::string reason = "G-Lined: " + x->Reason; + u->Kill(Config->OperServ, reason); + } + this->Send(u, x); + } + + void OnExpire(XLine *x) + { + if (Config->WallSNLineExpire) + ircdproto->SendGlobops(OperServ, "SNLINE on \2%s\2 has expired", x->Mask.c_str()); + } + + void Send(User *u, XLine *x) + { + ircdproto->SendSGLine(u, x); + } + + XLine *Check(User *u) + { + for (unsigned i = this->XLines.size(); i > 0; --i) + { + XLine *x = this->XLines[i - 1]; + + if (x->Expires && x->Expires < Anope::CurTime) + { + this->OnExpire(x); + this->Del(x); + delete x; + this->XLines.erase(XLines.begin() + i - 1); + continue; + } + + if (Anope::Match(u->realname, x->Mask)) + { + this->OnMatch(u, x); + return x; + } + } + + return NULL; + } +}; + +class OperServCore : public Module +{ + SGLineManager sglines; + SZLineManager szlines; + SQLineManager sqlines; + SNLineManager snlines; + + public: + OperServCore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + sglines(this), szlines(this), sqlines(this), snlines(this) + { + this->SetAuthor("Anope"); + + OperServ = findbot(Config->OperServ); + if (OperServ == NULL) + throw ModuleException("No bot named " + Config->OperServ); + + Implementation i[] = { I_OnBotPrivmsg, I_OnServerQuit, I_OnUserModeSet, I_OnUserModeUnset, I_OnUserConnect, I_OnUserNickChange, I_OnPreHelp }; + ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); + + ModuleManager::RegisterService(&sglines); + ModuleManager::RegisterService(&szlines); + ModuleManager::RegisterService(&sqlines); + ModuleManager::RegisterService(&snlines); + + /* Yes, these are in this order for a reason. Most violent->least violent. */ + XLineManager::RegisterXLineManager(&sglines); + XLineManager::RegisterXLineManager(&szlines); + XLineManager::RegisterXLineManager(&sqlines); + XLineManager::RegisterXLineManager(&snlines); + } + + EventReturn OnBotPrivmsg(User *u, BotInfo *bi, Anope::string &message) + { + if (Config->OSOpersOnly && !u->HasMode(UMODE_OPER) && bi->nick == Config->OperServ) + { + u->SendMessage(bi, ACCESS_DENIED); + return EVENT_STOP; + } + + return EVENT_CONTINUE; + } + + void OnServerQuit(Server *server) + { + if (server->HasFlag(SERVER_JUPED)) + ircdproto->SendGlobops(OperServ, "Received SQUIT for juped server %s", server->GetName().c_str()); + } + + void OnUserModeSet(User *u, UserModeName Name) + { + if (Name == UMODE_OPER) + { + if (Config->WallOper) + ircdproto->SendGlobops(OperServ, "\2%s\2 is now an IRC operator.", u->nick.c_str()); + Log(OperServ) << u->nick << " is now an IRC operator"; + } + } + + void OnUserModeUnset(User *u, UserModeName Name) + { + if (Name == UMODE_OPER) + Log(OperServ) << u->nick << " is no longer an IRC operator"; + } + + void OnUserConnect(dynamic_reference &u, bool &exempt) + { + if (u && !exempt) + XLineManager::CheckAll(u); + } + + void OnUserNickChange(User *u, const Anope::string &oldnick) + { + if (ircd->sqline && !u->HasMode(UMODE_OPER)) + this->sqlines.Check(u); + } + + EventReturn OnCheckKick(User *u, ChannelInfo *ci, bool &kick) + { + if (this->sqlines.CheckChannel(ci->c)) + kick = true; + return EVENT_CONTINUE; + } + + void OnPreHelp(CommandSource &source, const std::vector ¶ms) + { + if (!params.empty() || source.owner->nick != Config->OperServ) + return; + source.Reply(_("%s commands:"), Config->OperServ.c_str()); + } +}; + +MODULE_INIT(OperServCore) + diff --git a/modules/third/README b/modules/third/README new file mode 100644 index 000000000..f960fba3e --- /dev/null +++ b/modules/third/README @@ -0,0 +1,2 @@ +Third party modules, eg, modules not shipped with Anope are to be placed here. +After doing so, run ./Config, make, make install and with some luck your module will work! diff --git a/modules/third/language/CMakeLists.txt b/modules/third/language/CMakeLists.txt new file mode 100644 index 000000000..af4dd90a6 --- /dev/null +++ b/modules/third/language/CMakeLists.txt @@ -0,0 +1,38 @@ +# Only do this if gettext is installed +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) + + foreach(LANG_PO ${LANG_SRCS_PO}) + # Get the domain for this language file + string(LENGTH ${LANG_PO} LANG_PO_LENGTH) + math(EXPR DOMAIN_LENGTH "${LANG_PO_LENGTH} - 9") + string(SUBSTRING ${LANG_PO} 0 ${DOMAIN_LENGTH} LANG_DOMAIN) + + # Get the language for this language file + math(EXPR DOMAIN_LENGTH "${LANG_PO_LENGTH} - 8") + string(SUBSTRING ${LANG_PO} ${DOMAIN_LENGTH} 5 LANG_LANG) + + # Get the .mo file name + string(REGEX REPLACE "\\.po$" ".mo" LANG_MO ${LANG_PO}) + # Add the .mo file to a list for use later with add_custom_target + set(LANG_SRCS_MO ${LANG_SRCS_MO} ${CMAKE_CURRENT_BINARY_DIR}/${LANG_MO}) + # Run msgfmt on the language file, depends on the .po file + add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${LANG_MO} + COMMAND ${GETTEXT_MSGFMT_EXECUTABLE} -c ${CMAKE_CURRENT_SOURCE_DIR}/${LANG_PO} -o ${CMAKE_CURRENT_BINARY_DIR}/${LANG_MO} + MAIN_DEPENDENCY ${LANG_PO} + ) + # Add to cpack ignored files if not on Windows. + if(NOT WIN32) + add_to_cpack_ignored_files("${LANG_MO}") + endif(NOT WIN32) + + # Install the new language file + install(CODE "FILE(MAKE_DIRECTORY \${CMAKE_INSTALL_PREFIX}/data/languages/${LANG_LANG}/LC_MESSAGES/)") + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${LANG_MO} DESTINATION data/languages/${LANG_LANG}/LC_MESSAGES RENAME ${LANG_DOMAIN}.mo PERMISSIONS ${PERMS}) + endforeach(LANG_PO) + + # Generate languages, depends on the mo files + add_custom_target(module_language DEPENDS ${LANG_SRCS_MO}) +endif(GETTEXT_FOUND) diff --git a/modules/third/language/README b/modules/third/language/README new file mode 100644 index 000000000..c86eeefba --- /dev/null +++ b/modules/third/language/README @@ -0,0 +1 @@ +Language files for third party modules are to go here. diff --git a/modules/third/language/update.sh b/modules/third/language/update.sh new file mode 100755 index 000000000..1f16ea961 --- /dev/null +++ b/modules/third/language/update.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +for f in `ls ../*.cpp` +do + BASE=`basename $f | cut -d'.' -f1` + xgettext -C -s -d $BASE -o $BASE.pot --from-code=utf-8 --keyword --keyword=_ $f +done + +for f in `ls *.po` +do + msgmerge -v -s -U $f `basename $f | cut -d'.' -f1`.pot +done + +rm -f *~ -- cgit