summaryrefslogtreecommitdiff
path: root/chanserv.c
diff options
context:
space:
mode:
Diffstat (limited to 'chanserv.c')
-rw-r--r--chanserv.c6187
1 files changed, 6187 insertions, 0 deletions
diff --git a/chanserv.c b/chanserv.c
new file mode 100644
index 000000000..796a7817a
--- /dev/null
+++ b/chanserv.c
@@ -0,0 +1,6187 @@
+/* ChanServ functions.
+ *
+ * (C) 2003 Anope Team
+ * Contact us at info@anope.org
+ *
+ * Please read COPYING and README for furhter details.
+ *
+ * Based on the original code of Epona by Lara.
+ * Based on the original code of Services by Andy Church.
+ *
+ * $Id: chanserv.c,v 1.55 2004/03/17 20:08:49 ribosome Exp $
+ *
+ */
+
+/*************************************************************************/
+
+#include "services.h"
+#include "pseudo.h"
+
+/*************************************************************************/
+/* *INDENT-OFF* */
+
+ChannelInfo *chanlists[256];
+
+static int def_levels[][2] = {
+ { CA_AUTOOP, 5 },
+ { CA_AUTOVOICE, 3 },
+ { CA_AUTODEOP, -1 },
+ { CA_NOJOIN, -2 },
+ { CA_INVITE, 5 },
+ { CA_AKICK, 10 },
+ { CA_SET, ACCESS_INVALID },
+ { CA_CLEAR, ACCESS_INVALID },
+ { CA_UNBAN, 5 },
+ { CA_OPDEOP, 5 },
+ { CA_ACCESS_LIST, 1 },
+ { CA_ACCESS_CHANGE, 10 },
+ { CA_MEMO, 10 },
+ { CA_ASSIGN, ACCESS_INVALID },
+ { CA_BADWORDS, 10 },
+ { CA_NOKICK, 1 },
+ { CA_FANTASIA, 3 },
+ { CA_SAY, 5 },
+ { CA_GREET, 5 },
+ { CA_VOICEME, 3 },
+ { CA_VOICE, 5 },
+ { CA_GETKEY, 5 },
+ { CA_AUTOHALFOP, 4 },
+ { CA_AUTOPROTECT, 10 },
+ { CA_OPDEOPME, 5 },
+ { CA_HALFOPME, 4 },
+ { CA_HALFOP, 5 },
+ { CA_PROTECTME, 10 },
+ { CA_PROTECT, ACCESS_INVALID },
+ { CA_KICKME, 5 },
+ { CA_KICK, 5 },
+ { CA_SIGNKICK, ACCESS_INVALID },
+ { CA_BANME, 5 },
+ { CA_BAN, 5 },
+ { CA_TOPIC, ACCESS_INVALID },
+ { CA_INFO, ACCESS_INVALID },
+ { -1 }
+};
+
+typedef struct {
+ int what;
+ char *name;
+ int desc;
+} LevelInfo;
+static LevelInfo levelinfo[] = {
+ { CA_AUTODEOP, "AUTODEOP", CHAN_LEVEL_AUTODEOP },
+#ifdef HAS_HALFOP
+ { CA_AUTOHALFOP, "AUTOHALFOP", CHAN_LEVEL_AUTOHALFOP },
+#endif
+ { CA_AUTOOP, "AUTOOP", CHAN_LEVEL_AUTOOP },
+#ifdef IRC_UNREAL
+ { CA_AUTOPROTECT, "AUTOPROTECT", CHAN_LEVEL_AUTOPROTECT },
+#endif
+#ifdef IRC_VIAGRA
+ { CA_AUTOPROTECT, "AUTOPROTECT", CHAN_LEVEL_AUTOPROTECT },
+#endif
+#ifdef IRC_ULTIMATE3
+ { CA_AUTOPROTECT, "AUTOADMIN", CHAN_LEVEL_AUTOPROTECT },
+#endif
+ { CA_AUTOVOICE, "AUTOVOICE", CHAN_LEVEL_AUTOVOICE },
+ { CA_NOJOIN, "NOJOIN", CHAN_LEVEL_NOJOIN },
+ { CA_SIGNKICK, "SIGNKICK", CHAN_LEVEL_SIGNKICK },
+
+ { CA_ACCESS_LIST, "ACC-LIST", CHAN_LEVEL_ACCESS_LIST },
+ { CA_ACCESS_CHANGE, "ACC-CHANGE", CHAN_LEVEL_ACCESS_CHANGE },
+ { CA_AKICK, "AKICK", CHAN_LEVEL_AKICK },
+ { CA_SET, "SET", CHAN_LEVEL_SET },
+
+ { CA_BAN, "BAN", CHAN_LEVEL_BAN },
+ { CA_BANME, "BANME", CHAN_LEVEL_BANME },
+ { CA_CLEAR, "CLEAR", CHAN_LEVEL_CLEAR },
+ { CA_GETKEY, "GETKEY", CHAN_LEVEL_GETKEY },
+#ifdef HAS_HALFOP
+ { CA_HALFOP, "HALFOP", CHAN_LEVEL_HALFOP },
+ { CA_HALFOPME, "HALFOPME", CHAN_LEVEL_HALFOPME },
+#endif
+ { CA_INFO, "INFO", CHAN_LEVEL_INFO },
+ { CA_KICK, "KICK", CHAN_LEVEL_KICK },
+ { CA_KICKME, "KICKME", CHAN_LEVEL_KICKME },
+ { CA_INVITE, "INVITE", CHAN_LEVEL_INVITE },
+ { CA_OPDEOP, "OPDEOP", CHAN_LEVEL_OPDEOP },
+ { CA_OPDEOPME, "OPDEOPME", CHAN_LEVEL_OPDEOPME },
+#ifdef IRC_UNREAL
+ { CA_PROTECT, "PROTECT", CHAN_LEVEL_PROTECT },
+ { CA_PROTECTME, "PROTECTME", CHAN_LEVEL_PROTECTME },
+#endif
+#ifdef IRC_VIAGRA
+ { CA_PROTECT, "PROTECT", CHAN_LEVEL_PROTECT },
+ { CA_PROTECTME, "PROTECTME", CHAN_LEVEL_PROTECTME },
+#endif
+#ifdef IRC_ULTIMATE3
+ { CA_PROTECT, "ADMIN", CHAN_LEVEL_PROTECT },
+ { CA_PROTECTME, "ADMINME", CHAN_LEVEL_PROTECTME },
+#endif
+ { CA_TOPIC, "TOPIC", CHAN_LEVEL_TOPIC },
+ { CA_UNBAN, "UNBAN", CHAN_LEVEL_UNBAN },
+ { CA_VOICE, "VOICE", CHAN_LEVEL_VOICE },
+ { CA_VOICEME, "VOICEME", CHAN_LEVEL_VOICEME },
+
+ { CA_MEMO, "MEMO", CHAN_LEVEL_MEMO },
+
+ { CA_ASSIGN, "ASSIGN", CHAN_LEVEL_ASSIGN },
+ { CA_BADWORDS, "BADWORDS", CHAN_LEVEL_BADWORDS },
+ { CA_FANTASIA, "FANTASIA", CHAN_LEVEL_FANTASIA },
+ { CA_GREET, "GREET", CHAN_LEVEL_GREET },
+ { CA_NOKICK, "NOKICK", CHAN_LEVEL_NOKICK },
+ { CA_SAY, "SAY", CHAN_LEVEL_SAY },
+
+ { -1 }
+};
+static int levelinfo_maxwidth = 0;
+
+CSModeUtil csmodeutils[] = {
+ { "DEOP", "!deop", "-o", CI_OPNOTICE, CA_OPDEOP, CA_OPDEOPME },
+ { "OP", "!op", "+o", CI_OPNOTICE, CA_OPDEOP, CA_OPDEOPME },
+ { "DEVOICE", "!devoice", "-v", 0 , CA_VOICE, CA_VOICEME },
+ { "VOICE", "!voice", "+v", 0 , CA_VOICE, CA_VOICEME },
+#ifdef HAS_HALFOP
+ { "DEHALFOP", "!dehalfop", "-h", 0 , CA_HALFOP, CA_HALFOPME },
+ { "HALFOP", "!halfop", "+h", 0 , CA_HALFOP, CA_HALFOPME },
+#endif
+#ifdef IRC_UNREAL
+ { "DEPROTECT", "!deprotect", "-a", 0 , CA_PROTECT, CA_PROTECTME },
+ { "PROTECT", "!protect", "+a", 0 , CA_PROTECT, CA_PROTECTME },
+#endif
+#ifdef IRC_VIAGRA
+ { "DEPROTECT", "!deprotect", "-a", 0 , CA_PROTECT, CA_PROTECTME },
+ { "PROTECT", "!protect", "+a", 0 , CA_PROTECT, CA_PROTECTME },
+#endif
+#ifdef IRC_ULTIMATE3
+ { "DEPROTECT", "!deadmin", "-a", 0 , CA_PROTECT, CA_PROTECTME },
+ { "PROTECT", "!admin", "+a", 0 , CA_PROTECT, CA_PROTECTME },
+#endif
+
+ { NULL }
+};
+
+int xop_msgs[4][14] = {
+ { CHAN_AOP_SYNTAX,
+ CHAN_AOP_DISABLED,
+ CHAN_AOP_NICKS_ONLY,
+ CHAN_AOP_ADDED,
+ CHAN_AOP_MOVED,
+ CHAN_AOP_NO_SUCH_ENTRY,
+ CHAN_AOP_NOT_FOUND,
+ CHAN_AOP_NO_MATCH,
+ CHAN_AOP_DELETED,
+ CHAN_AOP_DELETED_ONE,
+ CHAN_AOP_DELETED_SEVERAL,
+ CHAN_AOP_LIST_EMPTY,
+ CHAN_AOP_LIST_HEADER,
+ CHAN_AOP_CLEAR
+ },
+ { CHAN_SOP_SYNTAX,
+ CHAN_SOP_DISABLED,
+ CHAN_SOP_NICKS_ONLY,
+ CHAN_SOP_ADDED,
+ CHAN_SOP_MOVED,
+ CHAN_SOP_NO_SUCH_ENTRY,
+ CHAN_SOP_NOT_FOUND,
+ CHAN_SOP_NO_MATCH,
+ CHAN_SOP_DELETED,
+ CHAN_SOP_DELETED_ONE,
+ CHAN_SOP_DELETED_SEVERAL,
+ CHAN_SOP_LIST_EMPTY,
+ CHAN_SOP_LIST_HEADER,
+ CHAN_SOP_CLEAR
+ },
+ { CHAN_VOP_SYNTAX,
+ CHAN_VOP_DISABLED,
+ CHAN_VOP_NICKS_ONLY,
+ CHAN_VOP_ADDED,
+ CHAN_VOP_MOVED,
+ CHAN_VOP_NO_SUCH_ENTRY,
+ CHAN_VOP_NOT_FOUND,
+ CHAN_VOP_NO_MATCH,
+ CHAN_VOP_DELETED,
+ CHAN_VOP_DELETED_ONE,
+ CHAN_VOP_DELETED_SEVERAL,
+ CHAN_VOP_LIST_EMPTY,
+ CHAN_VOP_LIST_HEADER,
+ CHAN_VOP_CLEAR
+ },
+ { CHAN_HOP_SYNTAX,
+ CHAN_HOP_DISABLED,
+ CHAN_HOP_NICKS_ONLY,
+ CHAN_HOP_ADDED,
+ CHAN_HOP_MOVED,
+ CHAN_HOP_NO_SUCH_ENTRY,
+ CHAN_HOP_NOT_FOUND,
+ CHAN_HOP_NO_MATCH,
+ CHAN_HOP_DELETED,
+ CHAN_HOP_DELETED_ONE,
+ CHAN_HOP_DELETED_SEVERAL,
+ CHAN_HOP_LIST_EMPTY,
+ CHAN_HOP_LIST_HEADER,
+ CHAN_HOP_CLEAR
+ }
+};
+
+/* *INDENT-ON* */
+/*************************************************************************/
+
+static void alpha_insert_chan(ChannelInfo * ci);
+static ChannelInfo *makechan(const char *chan);
+static int delchan(ChannelInfo * ci);
+static void reset_levels(ChannelInfo * ci);
+static int is_real_founder(User * user, ChannelInfo * ci);
+static int is_identified(User * user, ChannelInfo * ci);
+static void make_unidentified(User * u, ChannelInfo * ci);
+
+static int do_help(User * u);
+static int do_register(User * u);
+static int do_identify(User * u);
+static int do_logout(User * u);
+static int do_drop(User * u);
+static int do_set(User * u);
+static int do_set_founder(User * u, ChannelInfo * ci, char *param);
+static int do_set_successor(User * u, ChannelInfo * ci, char *param);
+static int do_set_password(User * u, ChannelInfo * ci, char *param);
+static int do_set_desc(User * u, ChannelInfo * ci, char *param);
+static int do_set_url(User * u, ChannelInfo * ci, char *param);
+static int do_set_email(User * u, ChannelInfo * ci, char *param);
+static int do_set_entrymsg(User * u, ChannelInfo * ci, char *param);
+static int do_set_bantype(User * u, ChannelInfo * ci, char *param);
+static int do_set_mlock(User * u, ChannelInfo * ci, char *param);
+static int do_set_keeptopic(User * u, ChannelInfo * ci, char *param);
+static int do_set_topiclock(User * u, ChannelInfo * ci, char *param);
+static int do_set_private(User * u, ChannelInfo * ci, char *param);
+static int do_set_secureops(User * u, ChannelInfo * ci, char *param);
+static int do_set_securefounder(User * u, ChannelInfo * ci, char *param);
+static int do_set_restricted(User * u, ChannelInfo * ci, char *param);
+static int do_set_secure(User * u, ChannelInfo * ci, char *param);
+static int do_set_signkick(User * u, ChannelInfo * ci, char *param);
+static int do_set_opnotice(User * u, ChannelInfo * ci, char *param);
+static int do_set_xop(User * u, ChannelInfo * ci, char *param);
+static int do_set_peace(User * u, ChannelInfo * ci, char *param);
+static int do_set_noexpire(User * u, ChannelInfo * ci, char *param);
+static int do_xop(User * u, char *xname, int xlev, int *xmsgs);
+static int do_aop(User * u);
+#ifdef HAS_HALFOP
+static int do_hop(User * u);
+#endif
+static int do_sop(User * u);
+static int do_vop(User * u);
+static int do_access(User * u);
+static int do_akick(User * u);
+static int do_info(User * u);
+static int do_list(User * u);
+static int do_invite(User * u);
+static int do_levels(User * u);
+static int do_util(User * u, CSModeUtil * util);
+static int do_op(User * u);
+static int do_deop(User * u);
+static int do_voice(User * u);
+static int do_devoice(User * u);
+#ifdef HAS_HALFOP
+static int do_halfop(User * u);
+static int do_dehalfop(User * u);
+#endif
+#ifdef IRC_UNREAL
+static int do_protect(User * u);
+static int do_deprotect(User * u);
+static int do_owner(User * u);
+static int do_deowner(User * u);
+#endif
+#ifdef IRC_VIAGRA
+static int do_protect(User * u);
+static int do_deprotect(User * u);
+static int do_owner(User * u);
+static int do_deowner(User * u);
+#endif
+#ifdef IRC_ULTIMATE3
+static int do_protect(User * u);
+static int do_deprotect(User * u);
+#endif
+static int do_cs_kick(User * u);
+static int do_ban(User * u);
+static int do_cs_topic(User * u);
+static int do_unban(User * u);
+static int do_clear(User * u);
+static int do_getkey(User * u);
+static int do_getpass(User * u);
+static int do_sendpass(User * u);
+static int do_forbid(User * u);
+static int do_suspend(User * u);
+static int do_unsuspend(User * u);
+static int do_status(User * u);
+void moduleAddChanServCmds(void);
+/*************************************************************************/
+/* *INDENT-OFF* */
+void moduleAddChanServCmds(void) {
+ Command *c;
+ c = createCommand("HELP", do_help, NULL, -1, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("REGISTER", do_register, NULL, CHAN_HELP_REGISTER, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("IDENTIFY", do_identify, NULL, CHAN_HELP_IDENTIFY, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("LOGOUT", do_logout, NULL, -1,CHAN_HELP_LOGOUT, CHAN_SERVADMIN_HELP_LOGOUT,CHAN_SERVADMIN_HELP_LOGOUT, CHAN_SERVADMIN_HELP_LOGOUT); addCoreCommand(CHANSERV,c);
+ c = createCommand("DROP", do_drop, NULL, -1,CHAN_HELP_DROP, CHAN_SERVADMIN_HELP_DROP,CHAN_SERVADMIN_HELP_DROP, CHAN_SERVADMIN_HELP_DROP); addCoreCommand(CHANSERV,c);
+ c = createCommand("SET", do_set, NULL, CHAN_HELP_SET,-1, CHAN_SERVADMIN_HELP_SET,CHAN_SERVADMIN_HELP_SET, CHAN_SERVADMIN_HELP_SET); addCoreCommand(CHANSERV,c);
+ c = createCommand("SET FOUNDER", NULL, NULL, CHAN_HELP_SET_FOUNDER, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("SET SUCCESSOR", NULL, NULL, CHAN_HELP_SET_SUCCESSOR, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("SET PASSWORD", NULL, NULL, CHAN_HELP_SET_PASSWORD, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("SET DESC", NULL, NULL, CHAN_HELP_SET_DESC, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("SET URL", NULL, NULL, CHAN_HELP_SET_URL, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("SET EMAIL", NULL, NULL, CHAN_HELP_SET_EMAIL, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("SET ENTRYMSG", NULL, NULL, CHAN_HELP_SET_ENTRYMSG, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("SET BANTYPE", NULL, NULL, CHAN_HELP_SET_BANTYPE, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("SET PRIVATE", NULL, NULL, CHAN_HELP_SET_PRIVATE, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("SET KEEPTOPIC", NULL, NULL, CHAN_HELP_SET_KEEPTOPIC, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("SET TOPICLOCK", NULL, NULL, CHAN_HELP_SET_TOPICLOCK, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("SET MLOCK", NULL, NULL, CHAN_HELP_SET_MLOCK, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("SET RESTRICTED", NULL, NULL, CHAN_HELP_SET_RESTRICTED, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("SET SECURE", NULL, NULL, CHAN_HELP_SET_SECURE, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("SET SECUREOPS", NULL, NULL, CHAN_HELP_SET_SECUREOPS, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("SET SECUREFOUNDER", NULL, NULL, CHAN_HELP_SET_SECUREFOUNDER, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("SET SIGNKICK", NULL, NULL, CHAN_HELP_SET_SIGNKICK, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("SET OPNOTICE", NULL, NULL, CHAN_HELP_SET_OPNOTICE, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("SET XOP", NULL, NULL, CHAN_HELP_SET_XOP, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("SET PEACE", NULL, NULL, CHAN_HELP_SET_PEACE, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("SET NOEXPIRE", NULL, NULL, -1, -1,CHAN_SERVADMIN_HELP_SET_NOEXPIRE,CHAN_SERVADMIN_HELP_SET_NOEXPIRE,CHAN_SERVADMIN_HELP_SET_NOEXPIRE); addCoreCommand(CHANSERV,c);
+ c = createCommand("AOP", do_aop, NULL, CHAN_HELP_AOP, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+#ifdef HAS_HALFOP
+ c = createCommand("HOP", do_hop, NULL, CHAN_HELP_HOP, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+#endif
+ c = createCommand("SOP", do_sop, NULL, CHAN_HELP_SOP, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("VOP", do_vop, NULL, CHAN_HELP_VOP, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("ACCESS", do_access, NULL, CHAN_HELP_ACCESS, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("ACCESS LEVELS", NULL, NULL, CHAN_HELP_ACCESS_LEVELS, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("AKICK", do_akick, NULL, CHAN_HELP_AKICK, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("LEVELS", do_levels, NULL, CHAN_HELP_LEVELS, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("INFO", do_info, NULL, CHAN_HELP_INFO,-1, CHAN_SERVADMIN_HELP_INFO, CHAN_SERVADMIN_HELP_INFO,CHAN_SERVADMIN_HELP_INFO); addCoreCommand(CHANSERV,c);
+ c = createCommand("LIST", do_list, NULL, -1,CHAN_HELP_LIST, CHAN_SERVADMIN_HELP_LIST,CHAN_SERVADMIN_HELP_LIST, CHAN_SERVADMIN_HELP_LIST); addCoreCommand(CHANSERV,c);
+ c = createCommand("OP", do_op, NULL, CHAN_HELP_OP, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("DEOP", do_deop, NULL, CHAN_HELP_DEOP, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("VOICE", do_voice, NULL, CHAN_HELP_VOICE, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("DEVOICE", do_devoice, NULL, CHAN_HELP_DEVOICE, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+#ifdef HAS_HALFOP
+ c = createCommand("HALFOP", do_halfop, NULL, CHAN_HELP_HALFOP, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("DEHALFOP", do_dehalfop, NULL, CHAN_HELP_DEHALFOP, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+#endif
+#ifdef IRC_UNREAL
+ c = createCommand("PROTECT", do_protect, NULL, CHAN_HELP_PROTECT, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("DEPROTECT",do_deprotect,NULL, CHAN_HELP_DEPROTECT, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("OWNER", do_owner, NULL, CHAN_HELP_OWNER, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("DEOWNER", do_deowner, NULL, CHAN_HELP_DEOWNER, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+#endif
+#ifdef IRC_VIAGRA
+ c = createCommand("PROTECT", do_protect, NULL, CHAN_HELP_PROTECT, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("DEPROTECT",do_deprotect,NULL, CHAN_HELP_DEPROTECT, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("OWNER", do_owner, NULL, CHAN_HELP_OWNER, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("DEOWNER", do_deowner, NULL, CHAN_HELP_DEOWNER, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+#endif
+#ifdef IRC_ULTIMATE3
+ c = createCommand("ADMIN", do_protect, NULL, CHAN_HELP_PROTECT, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("DEADMIN",do_deprotect,NULL, CHAN_HELP_DEPROTECT, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+#endif
+ c = createCommand("KICK", do_cs_kick, NULL, CHAN_HELP_KICK, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("BAN", do_ban, NULL, CHAN_HELP_BAN, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("TOPIC", do_cs_topic, NULL, CHAN_HELP_TOPIC, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("INVITE", do_invite, NULL, CHAN_HELP_INVITE, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("UNBAN", do_unban, NULL, CHAN_HELP_UNBAN, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("CLEAR", do_clear, NULL, CHAN_HELP_CLEAR, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("GETKEY", do_getkey, NULL, CHAN_HELP_GETKEY, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("SENDPASS", do_sendpass, NULL, CHAN_HELP_SENDPASS, -1,-1,-1,-1); addCoreCommand(CHANSERV,c);
+ c = createCommand("GETPASS", do_getpass, is_services_admin, -1,-1, CHAN_SERVADMIN_HELP_GETPASS,CHAN_SERVADMIN_HELP_GETPASS, CHAN_SERVADMIN_HELP_GETPASS); addCoreCommand(CHANSERV,c);
+ c = createCommand("FORBID", do_forbid, is_services_admin, -1,-1, CHAN_SERVADMIN_HELP_FORBID,CHAN_SERVADMIN_HELP_FORBID, CHAN_SERVADMIN_HELP_FORBID); addCoreCommand(CHANSERV,c);
+ c = createCommand("SUSPEND", do_suspend, is_services_admin, -1,-1, CHAN_SERVADMIN_HELP_SUSPEND,CHAN_SERVADMIN_HELP_SUSPEND, CHAN_SERVADMIN_HELP_SUSPEND); addCoreCommand(CHANSERV,c);
+ c = createCommand("UNSUSPEND", do_unsuspend, is_services_admin, -1,-1, CHAN_SERVADMIN_HELP_UNSUSPEND,CHAN_SERVADMIN_HELP_UNSUSPEND, CHAN_SERVADMIN_HELP_UNSUSPEND); addCoreCommand(CHANSERV,c);
+ c = createCommand("STATUS", do_status, is_services_admin, -1,-1, CHAN_SERVADMIN_HELP_STATUS,CHAN_SERVADMIN_HELP_STATUS, CHAN_SERVADMIN_HELP_STATUS); addCoreCommand(CHANSERV,c);
+}
+
+/* *INDENT-ON* */
+/*************************************************************************/
+/*************************************************************************/
+
+/* Returns modes for mlock in a nice way. */
+
+static char *get_mlock_modes(ChannelInfo * ci, int complete)
+{
+ static char res[BUFSIZE];
+
+ char *end = res;
+
+ if (ci->mlock_on || ci->mlock_off) {
+ int n = 0;
+ CBModeInfo *cbmi = cbmodeinfos;
+
+ if (ci->mlock_on) {
+ *end++ = '+';
+ n++;
+
+ do {
+ if (ci->mlock_on & cbmi->flag)
+ *end++ = cbmi->mode;
+ } while ((++cbmi)->flag != 0 && ++n < sizeof(res) - 1);
+
+ cbmi = cbmodeinfos;
+ }
+
+ if (ci->mlock_off) {
+ *end++ = '-';
+ n++;
+
+ do {
+ if (ci->mlock_off & cbmi->flag)
+ *end++ = cbmi->mode;
+ } while ((++cbmi)->flag != 0 && ++n < sizeof(res) - 1);
+
+ cbmi = cbmodeinfos;
+ }
+
+ if (ci->mlock_on && complete) {
+ do {
+ if (cbmi->csgetvalue && (ci->mlock_on & cbmi->flag)) {
+ char *value = cbmi->csgetvalue(ci);
+
+ if (value) {
+ *end++ = ' ';
+ while (*value)
+ *end++ = *value++;
+ }
+ }
+ } while ((++cbmi)->flag != 0 && ++n < sizeof(res) - 1);
+ }
+ }
+
+ *end = 0;
+
+ return res;
+}
+
+/* Display total number of registered channels and info about each; or, if
+ * a specific channel is given, display information about that channel
+ * (like /msg ChanServ INFO <channel>). If count_only != 0, then only
+ * display the number of registered channels (the channel parameter is
+ * ignored).
+ */
+
+void listchans(int count_only, const char *chan)
+{
+ int count = 0;
+ ChannelInfo *ci;
+ int i;
+
+ if (count_only) {
+
+ for (i = 0; i < 256; i++) {
+ for (ci = chanlists[i]; ci; ci = ci->next)
+ count++;
+ }
+ printf("%d channels registered.\n", count);
+
+ } else if (chan) {
+
+ struct tm *tm;
+ char buf[BUFSIZE];
+
+ if (!(ci = cs_findchan(chan))) {
+ printf("Channel %s not registered.\n", chan);
+ return;
+ }
+ if (ci->flags & CI_VERBOTEN) {
+ printf("Channel %s is FORBIDden.\n", ci->name);
+ } else {
+ printf("Information about channel %s:\n", ci->name);
+ printf(" Founder: %s\n", ci->founder->display);
+ printf(" Description: %s\n", ci->desc);
+ tm = localtime(&ci->time_registered);
+ strftime(buf, sizeof(buf),
+ getstring(NULL, STRFTIME_DATE_TIME_FORMAT), tm);
+ printf(" Registered: %s\n", buf);
+ tm = localtime(&ci->last_used);
+ strftime(buf, sizeof(buf),
+ getstring(NULL, STRFTIME_DATE_TIME_FORMAT), tm);
+ printf(" Last used: %s\n", buf);
+ if (ci->last_topic) {
+ printf(" Last topic: %s\n", ci->last_topic);
+ printf(" Topic set by: %s\n", ci->last_topic_setter);
+ }
+ if (ci->url)
+ printf(" URL: %s\n", ci->url);
+ if (ci->email)
+ printf(" E-mail address: %s\n", ci->email);
+ printf(" Options: ");
+ if (!ci->flags) {
+ printf("None\n");
+ } else {
+ int need_comma = 0;
+ static const char commastr[] = ", ";
+ if (ci->flags & CI_PRIVATE) {
+ printf("Private");
+ need_comma = 1;
+ }
+ if (ci->flags & CI_KEEPTOPIC) {
+ printf("%sTopic Retention",
+ need_comma ? commastr : "");
+ need_comma = 1;
+ }
+ if (ci->flags & CI_TOPICLOCK) {
+ printf("%sTopic Lock", need_comma ? commastr : "");
+ need_comma = 1;
+ }
+ if (ci->flags & CI_SECUREOPS) {
+ printf("%sSecure Ops", need_comma ? commastr : "");
+ need_comma = 1;
+ }
+ if (ci->flags & CI_RESTRICTED) {
+ printf("%sRestricted Access",
+ need_comma ? commastr : "");
+ need_comma = 1;
+ }
+ if (ci->flags & CI_SECURE) {
+ printf("%sSecure", need_comma ? commastr : "");
+ need_comma = 1;
+ }
+ if (ci->flags & CI_NO_EXPIRE) {
+ printf("%sNo Expire", need_comma ? commastr : "");
+ need_comma = 1;
+ }
+ printf("\n");
+ }
+ if (ci->mlock_on || ci->mlock_off)
+ printf(" Mode lock: %s\n", get_mlock_modes(ci, 1));
+ }
+
+ } else {
+
+ for (i = 0; i < 256; i++) {
+ for (ci = chanlists[i]; ci; ci = ci->next) {
+ printf(" %s %-20s %s\n",
+ ci->flags & CI_NO_EXPIRE ? "!" : " ", ci->name,
+ ci->
+ flags & CI_VERBOTEN ? "Disallowed (FORBID)" : ci->
+ desc);
+ count++;
+ }
+ }
+ printf("%d channels registered.\n", count);
+
+ }
+}
+
+/*************************************************************************/
+
+/* Return information on memory use. Assumes pointers are valid. */
+
+void get_chanserv_stats(long *nrec, long *memuse)
+{
+ long count = 0, mem = 0;
+ int i, j;
+ ChannelInfo *ci;
+
+ for (i = 0; i < 256; i++) {
+ for (ci = chanlists[i]; ci; ci = ci->next) {
+ count++;
+ mem += sizeof(*ci);
+ if (ci->desc)
+ mem += strlen(ci->desc) + 1;
+ if (ci->url)
+ mem += strlen(ci->url) + 1;
+ if (ci->email)
+ mem += strlen(ci->email) + 1;
+ mem += ci->accesscount * sizeof(ChanAccess);
+ mem += ci->akickcount * sizeof(AutoKick);
+ for (j = 0; j < ci->akickcount; j++) {
+ if (!(ci->akick[j].flags & AK_ISNICK)
+ && ci->akick[j].u.mask)
+ mem += strlen(ci->akick[j].u.mask) + 1;
+ if (ci->akick[j].reason)
+ mem += strlen(ci->akick[j].reason) + 1;
+ if (ci->akick[j].creator)
+ mem += strlen(ci->akick[j].creator) + 1;
+ }
+ if (ci->mlock_key)
+ mem += strlen(ci->mlock_key) + 1;
+#ifdef HAS_FMODE
+ if (ci->mlock_flood)
+ mem += strlen(ci->mlock_flood) + 1;
+#endif
+#ifdef HAS_LMODE
+ if (ci->mlock_redirect)
+ mem += strlen(ci->mlock_redirect) + 1;
+#endif
+ if (ci->last_topic)
+ mem += strlen(ci->last_topic) + 1;
+ if (ci->entry_message)
+ mem += strlen(ci->entry_message) + 1;
+ if (ci->forbidby)
+ mem += strlen(ci->forbidby) + 1;
+ if (ci->forbidreason)
+ mem += strlen(ci->forbidreason) + 1;
+ if (ci->levels)
+ mem += sizeof(*ci->levels) * CA_SIZE;
+ mem += ci->memos.memocount * sizeof(Memo);
+ for (j = 0; j < ci->memos.memocount; j++) {
+ if (ci->memos.memos[j].text)
+ mem += strlen(ci->memos.memos[j].text) + 1;
+ }
+ if (ci->ttb)
+ mem += sizeof(*ci->ttb) * TTB_SIZE;
+ mem += ci->bwcount * sizeof(BadWord);
+ for (j = 0; j < ci->bwcount; j++)
+ if (ci->badwords[j].word)
+ mem += strlen(ci->badwords[j].word) + 1;
+ }
+ }
+ *nrec = count;
+ *memuse = mem;
+}
+
+/*************************************************************************/
+/*************************************************************************/
+
+/* ChanServ initialization. */
+
+void cs_init(void)
+{
+ Command *cmd;
+ moduleAddChanServCmds();
+ cmd = findCommand(CHANSERV, "REGISTER");
+ if (cmd)
+ cmd->help_param1 = s_NickServ;
+ cmd = findCommand(CHANSERV, "SET SECURE");
+ if (cmd)
+ cmd->help_param1 = s_NickServ;
+ cmd = findCommand(CHANSERV, "SET SUCCESSOR");
+ if (cmd)
+ cmd->help_param1 = (char *) (long) CSMaxReg;
+}
+
+/*************************************************************************/
+
+/* Main ChanServ routine. */
+
+void chanserv(User * u, char *buf)
+{
+ char *cmd, *s;
+
+ cmd = strtok(buf, " ");
+
+ if (!cmd) {
+ return;
+ } else if (stricmp(cmd, "\1PING") == 0) {
+ if (!(s = strtok(NULL, "")))
+ s = "\1";
+ notice(s_ChanServ, u->nick, "\1PING %s", s);
+ } else if (skeleton) {
+ notice_lang(s_ChanServ, u, SERVICE_OFFLINE, s_ChanServ);
+ } else {
+ mod_run_cmd(s_ChanServ, u, CHANSERV, cmd);
+ }
+}
+
+/*************************************************************************/
+
+/* Load/save data files. */
+
+
+#define SAFE(x) do { \
+ if ((x) < 0) { \
+ if (!forceload) \
+ fatal("Read error on %s", ChanDBName); \
+ failed = 1; \
+ break; \
+ } \
+} while (0)
+
+void load_cs_dbase(void)
+{
+ dbFILE *f;
+ int ver, i, j, c;
+ ChannelInfo *ci, **last, *prev;
+ int failed = 0;
+
+ if (!(f = open_db(s_ChanServ, ChanDBName, "r", CHAN_VERSION)))
+ return;
+
+ ver = get_file_version(f);
+
+ for (i = 0; i < 256 && !failed; i++) {
+ int16 tmp16;
+ int32 tmp32;
+ int n_levels;
+ char *s;
+ NickAlias *na;
+
+ last = &chanlists[i];
+ prev = NULL;
+ while ((c = getc_db(f)) != 0) {
+ if (c != 1)
+ fatal("Invalid format in %s", ChanDBName);
+ ci = scalloc(sizeof(ChannelInfo), 1);
+ *last = ci;
+ last = &ci->next;
+ ci->prev = prev;
+ prev = ci;
+ SAFE(read_buffer(ci->name, f));
+ SAFE(read_string(&s, f));
+ if (s) {
+ if (ver >= 13)
+ ci->founder = findcore(s);
+ else {
+ na = findnick(s);
+ if (na)
+ ci->founder = na->nc;
+ else
+ ci->founder = NULL;
+ }
+ free(s);
+ } else
+ ci->founder = NULL;
+ if (ver >= 7) {
+ SAFE(read_string(&s, f));
+ if (s) {
+ if (ver >= 13)
+ ci->successor = findcore(s);
+ else {
+ na = findnick(s);
+ if (na)
+ ci->successor = na->nc;
+ else
+ ci->successor = NULL;
+ }
+ free(s);
+ } else
+ ci->successor = NULL;
+ } else {
+ ci->successor = NULL;
+ }
+ SAFE(read_buffer(ci->founderpass, f));
+ SAFE(read_string(&ci->desc, f));
+ if (!ci->desc)
+ ci->desc = sstrdup("");
+ SAFE(read_string(&ci->url, f));
+ SAFE(read_string(&ci->email, f));
+ SAFE(read_int32(&tmp32, f));
+ ci->time_registered = tmp32;
+ SAFE(read_int32(&tmp32, f));
+ ci->last_used = tmp32;
+ SAFE(read_string(&ci->last_topic, f));
+ SAFE(read_buffer(ci->last_topic_setter, f));
+ SAFE(read_int32(&tmp32, f));
+ ci->last_topic_time = tmp32;
+ SAFE(read_int32(&ci->flags, f));
+#ifdef USE_ENCRYPTION
+ if (!(ci->flags & (CI_ENCRYPTEDPW | CI_VERBOTEN))) {
+ if (debug)
+ alog("debug: %s: encrypting password for %s on load",
+ s_ChanServ, ci->name);
+ if (encrypt_in_place(ci->founderpass, PASSMAX) < 0)
+ fatal("%s: load database: Can't encrypt %s password!",
+ s_ChanServ, ci->name);
+ ci->flags |= CI_ENCRYPTEDPW;
+ }
+#else
+ if (ci->flags & CI_ENCRYPTEDPW) {
+ /* Bail: it makes no sense to continue with encrypted
+ * passwords, since we won't be able to verify them */
+ fatal("%s: load database: password for %s encrypted "
+ "but encryption disabled, aborting",
+ s_ChanServ, ci->name);
+ }
+#endif
+ /* Leaveops cleanup */
+ if (ver <= 13 && (ci->flags & 0x00000020))
+ ci->flags &= ~0x00000020;
+ /* Temporary flags cleanup */
+ ci->flags &= ~CI_INHABIT;
+
+ if (ver >= 9) {
+ SAFE(read_string(&ci->forbidby, f));
+ SAFE(read_string(&ci->forbidreason, f));
+ } else {
+ ci->forbidreason = NULL;
+ ci->forbidby = NULL;
+ }
+ if (ver >= 9)
+ SAFE(read_int16(&tmp16, f));
+ else
+ tmp16 = CSDefBantype;
+ ci->bantype = tmp16;
+ SAFE(read_int16(&tmp16, f));
+ n_levels = tmp16;
+ ci->levels = scalloc(2 * CA_SIZE, 1);
+ reset_levels(ci);
+ for (j = 0; j < n_levels; j++) {
+ if (j < CA_SIZE)
+ SAFE(read_int16(&ci->levels[j], f));
+ else
+ SAFE(read_int16(&tmp16, f));
+ }
+ /* To avoid levels list silly hacks */
+ if (ver < 10)
+ ci->levels[CA_OPDEOPME] = ci->levels[CA_OPDEOP];
+ if (ver < 11) {
+ ci->levels[CA_KICKME] = ci->levels[CA_OPDEOP];
+ ci->levels[CA_KICK] = ci->levels[CA_OPDEOP];
+ }
+ if (ver < 15) {
+
+ /* Old Ultimate levels import */
+ /* We now conveniently use PROTECT internals for Ultimate's ADMIN support - ShadowMaster */
+ /* Doh, must of course be done before we change the values were trying to import - ShadowMaster */
+ ci->levels[CA_AUTOPROTECT] = ci->levels[32];
+ ci->levels[CA_PROTECTME] = ci->levels[33];
+ ci->levels[CA_PROTECT] = ci->levels[34];
+
+ ci->levels[CA_BANME] = ci->levels[CA_OPDEOP];
+ ci->levels[CA_BAN] = ci->levels[CA_OPDEOP];
+ ci->levels[CA_TOPIC] = ACCESS_INVALID;
+
+
+ }
+
+ SAFE(read_int16(&ci->accesscount, f));
+ if (ci->accesscount) {
+ ci->access = scalloc(ci->accesscount, sizeof(ChanAccess));
+ for (j = 0; j < ci->accesscount; j++) {
+ SAFE(read_int16(&ci->access[j].in_use, f));
+ if (ci->access[j].in_use) {
+ SAFE(read_int16(&ci->access[j].level, f));
+ SAFE(read_string(&s, f));
+ if (s) {
+ if (ver >= 13)
+ ci->access[j].nc = findcore(s);
+ else {
+ na = findnick(s);
+ if (na)
+ ci->access[j].nc = na->nc;
+ else
+ ci->access[j].nc = NULL;
+ }
+ free(s);
+ }
+ if (ci->access[j].nc == NULL)
+ ci->access[j].in_use = 0;
+ if (ver >= 11) {
+ SAFE(read_int32(&tmp32, f));
+ ci->access[j].last_seen = tmp32;
+ } else {
+ ci->access[j].last_seen = 0; /* Means we have never seen the user */
+ }
+ }
+ }
+ } else {
+ ci->access = NULL;
+ }
+
+ SAFE(read_int16(&ci->akickcount, f));
+ if (ci->akickcount) {
+ ci->akick = scalloc(ci->akickcount, sizeof(AutoKick));
+ for (j = 0; j < ci->akickcount; j++) {
+ if (ver >= 15) {
+ SAFE(read_int16(&ci->akick[j].flags, f));
+ } else {
+ SAFE(read_int16(&tmp16, f));
+ if (tmp16)
+ ci->akick[j].flags |= AK_USED;
+ }
+ if (ci->akick[j].flags & AK_USED) {
+ if (ver < 15) {
+ SAFE(read_int16(&tmp16, f));
+ if (tmp16)
+ ci->akick[j].flags |= AK_ISNICK;
+ }
+ SAFE(read_string(&s, f));
+ if (ci->akick[j].flags & AK_ISNICK) {
+ if (ver >= 13) {
+ ci->akick[j].u.nc = findcore(s);
+ } else {
+ na = findnick(s);
+ if (na)
+ ci->akick[j].u.nc = na->nc;
+ else
+ ci->akick[j].u.nc = NULL;
+ }
+ if (!ci->akick[j].u.nc)
+ ci->akick[j].flags &= ~AK_USED;
+ free(s);
+ } else {
+ ci->akick[j].u.mask = s;
+ }
+ SAFE(read_string(&s, f));
+ if (ci->akick[j].flags & AK_USED)
+ ci->akick[j].reason = s;
+ else if (s)
+ free(s);
+ if (ver >= 9) {
+ SAFE(read_string(&s, f));
+ if (ci->akick[j].flags & AK_USED) {
+ ci->akick[j].creator = s;
+ } else if (s) {
+ free(s);
+ }
+ SAFE(read_int32(&tmp32, f));
+ if (ci->akick[j].flags & AK_USED)
+ ci->akick[j].addtime = tmp32;
+ } else {
+ ci->akick[j].creator = NULL;
+ ci->akick[j].addtime = 0;
+ }
+ }
+
+ /* Bugfix */
+ if ((ver == 15) && ci->akick[j].flags > 8) {
+ ci->akick[j].flags = 0;
+ ci->akick[j].u.nc = NULL;
+ ci->akick[j].u.nc = NULL;
+ ci->akick[j].addtime = 0;
+ ci->akick[j].creator = NULL;
+ ci->akick[j].reason = NULL;
+ }
+ }
+ } else {
+ ci->akick = NULL;
+ }
+
+ if (ver >= 10) {
+ SAFE(read_int32(&ci->mlock_on, f));
+ SAFE(read_int32(&ci->mlock_off, f));
+ } else {
+ SAFE(read_int16(&tmp16, f));
+ ci->mlock_on = tmp16;
+ SAFE(read_int16(&tmp16, f));
+ ci->mlock_off = tmp16;
+ }
+ SAFE(read_int32(&ci->mlock_limit, f));
+ SAFE(read_string(&ci->mlock_key, f));
+ if (ver >= 10) {
+#ifdef HAS_FMODE
+ SAFE(read_string(&ci->mlock_flood, f));
+#else
+ SAFE(read_string(&s, f));
+ if (s)
+ free(s);
+#endif
+#ifdef HAS_LMODE
+ SAFE(read_string(&ci->mlock_redirect, f));
+#else
+ SAFE(read_string(&s, f));
+ if (s)
+ free(s);
+#endif
+ }
+
+ SAFE(read_int16(&ci->memos.memocount, f));
+ SAFE(read_int16(&ci->memos.memomax, f));
+ if (ci->memos.memocount) {
+ Memo *memos;
+ memos = scalloc(sizeof(Memo) * ci->memos.memocount, 1);
+ ci->memos.memos = memos;
+ for (j = 0; j < ci->memos.memocount; j++, memos++) {
+ SAFE(read_int32(&memos->number, f));
+ SAFE(read_int16(&memos->flags, f));
+ SAFE(read_int32(&tmp32, f));
+ memos->time = tmp32;
+ SAFE(read_buffer(memos->sender, f));
+ SAFE(read_string(&memos->text, f));
+ }
+ }
+
+ SAFE(read_string(&ci->entry_message, f));
+
+ ci->c = NULL;
+
+ /* Some cleanup */
+ if (ver <= 11) {
+ /* Cleanup: Founder must be != than successor */
+ if (!(ci->flags & CI_VERBOTEN)
+ && ci->successor == ci->founder) {
+ alog("Warning: founder and successor of %s are equal. Cleaning up.", ci->name);
+ ci->successor = NULL;
+ }
+ }
+
+ /* BotServ options */
+
+ if (ver >= 8) {
+ int n_ttb;
+
+ SAFE(read_string(&s, f));
+ if (s) {
+ ci->bi = findbot(s);
+ free(s);
+ } else
+ ci->bi = NULL;
+
+ SAFE(read_int32(&tmp32, f));
+ ci->botflags = tmp32;
+ SAFE(read_int16(&tmp16, f));
+ n_ttb = tmp16;
+ ci->ttb = scalloc(2 * TTB_SIZE, 1);
+ for (j = 0; j < n_ttb; j++) {
+ if (j < TTB_SIZE)
+ SAFE(read_int16(&ci->ttb[j], f));
+ else
+ SAFE(read_int16(&tmp16, f));
+ }
+ for (j = n_ttb; j < TTB_SIZE; j++)
+ ci->ttb[j] = 0;
+ SAFE(read_int16(&tmp16, f));
+ ci->capsmin = tmp16;
+ SAFE(read_int16(&tmp16, f));
+ ci->capspercent = tmp16;
+ SAFE(read_int16(&tmp16, f));
+ ci->floodlines = tmp16;
+ SAFE(read_int16(&tmp16, f));
+ ci->floodsecs = tmp16;
+ SAFE(read_int16(&tmp16, f));
+ ci->repeattimes = tmp16;
+
+ SAFE(read_int16(&ci->bwcount, f));
+ if (ci->bwcount) {
+ ci->badwords = scalloc(ci->bwcount, sizeof(BadWord));
+ for (j = 0; j < ci->bwcount; j++) {
+ SAFE(read_int16(&ci->badwords[j].in_use, f));
+ if (ci->badwords[j].in_use) {
+ SAFE(read_string(&ci->badwords[j].word, f));
+ SAFE(read_int16(&ci->badwords[j].type, f));
+ }
+ }
+ } else {
+ ci->badwords = NULL;
+ }
+ } else {
+ ci->bi = NULL;
+ ci->botflags = 0;
+ ci->ttb = scalloc(2 * TTB_SIZE, 1);
+ for (j = 0; j < TTB_SIZE; j++)
+ ci->ttb[j] = 0;
+ ci->bwcount = 0;
+ ci->badwords = NULL;
+ }
+
+ } /* while (getc_db(f) != 0) */
+
+ *last = NULL;
+
+ } /* for (i) */
+
+ close_db(f);
+
+ /* Check for non-forbidden channels with no founder.
+ Makes also other essential tasks. */
+ for (i = 0; i < 256; i++) {
+ ChannelInfo *next;
+ for (ci = chanlists[i]; ci; ci = next) {
+ next = ci->next;
+ if (!(ci->flags & CI_VERBOTEN) && !ci->founder) {
+ alog("%s: database load: Deleting founderless channel %s",
+ s_ChanServ, ci->name);
+ delchan(ci);
+ continue;
+ }
+ if (ver < 13) {
+ ChanAccess *access, *access2;
+ AutoKick *akick, *akick2;
+ int k;
+
+ if (ci->flags & CI_VERBOTEN)
+ continue;
+ /* Need to regenerate the channel count for the founder */
+ ci->founder->channelcount++;
+ /* Check for eventual double entries in access/akick lists. */
+ for (j = 0, access = ci->access; j < ci->accesscount;
+ j++, access++) {
+ if (!access->in_use)
+ continue;
+ for (k = 0, access2 = ci->access; k < j;
+ k++, access2++) {
+ if (access2->in_use && access2->nc == access->nc) {
+ alog("%s: deleting %s channel access entry of %s because it is already in the list (this is OK).", s_ChanServ, access->nc->display, ci->name);
+ memset(access, 0, sizeof(ChanAccess));
+ break;
+ }
+ }
+ }
+ for (j = 0, akick = ci->akick; j < ci->akickcount;
+ j++, akick++) {
+ if (!(akick->flags & AK_USED)
+ || !(akick->flags & AK_ISNICK))
+ continue;
+ for (k = 0, akick2 = ci->akick; k < j; k++, akick2++) {
+ if ((akick2->flags & AK_USED)
+ && (akick2->flags & AK_ISNICK)
+ && akick2->u.nc == akick->u.nc) {
+ alog("%s: deleting %s channel akick entry of %s because it is already in the list (this is OK).", s_ChanServ, akick->u.nc->display, ci->name);
+ if (akick->reason)
+ free(akick->reason);
+ if (akick->creator)
+ free(akick->creator);
+ memset(akick, 0, sizeof(AutoKick));
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+#undef SAFE
+
+/*************************************************************************/
+
+#define SAFE(x) do { \
+ if ((x) < 0) { \
+ restore_db(f); \
+ log_perror("Write error on %s", ChanDBName); \
+ if (time(NULL) - lastwarn > WarningTimeout) { \
+ wallops(NULL, "Write error on %s: %s", ChanDBName, \
+ strerror(errno)); \
+ lastwarn = time(NULL); \
+ } \
+ return; \
+ } \
+} while (0)
+
+void save_cs_dbase(void)
+{
+ dbFILE *f;
+ int i, j;
+ ChannelInfo *ci;
+ Memo *memos;
+ static time_t lastwarn = 0;
+
+ if (!(f = open_db(s_ChanServ, ChanDBName, "w", CHAN_VERSION)))
+ return;
+
+ for (i = 0; i < 256; i++) {
+ int16 tmp16;
+
+ for (ci = chanlists[i]; ci; ci = ci->next) {
+ SAFE(write_int8(1, f));
+ SAFE(write_buffer(ci->name, f));
+ if (ci->founder)
+ SAFE(write_string(ci->founder->display, f));
+ else
+ SAFE(write_string(NULL, f));
+ if (ci->successor)
+ SAFE(write_string(ci->successor->display, f));
+ else
+ SAFE(write_string(NULL, f));
+ SAFE(write_buffer(ci->founderpass, f));
+ SAFE(write_string(ci->desc, f));
+ SAFE(write_string(ci->url, f));
+ SAFE(write_string(ci->email, f));
+ SAFE(write_int32(ci->time_registered, f));
+ SAFE(write_int32(ci->last_used, f));
+ SAFE(write_string(ci->last_topic, f));
+ SAFE(write_buffer(ci->last_topic_setter, f));
+ SAFE(write_int32(ci->last_topic_time, f));
+ SAFE(write_int32(ci->flags, f));
+ SAFE(write_string(ci->forbidby, f));
+ SAFE(write_string(ci->forbidreason, f));
+ SAFE(write_int16(ci->bantype, f));
+
+ tmp16 = CA_SIZE;
+ SAFE(write_int16(tmp16, f));
+ for (j = 0; j < CA_SIZE; j++)
+ SAFE(write_int16(ci->levels[j], f));
+
+ SAFE(write_int16(ci->accesscount, f));
+ for (j = 0; j < ci->accesscount; j++) {
+ SAFE(write_int16(ci->access[j].in_use, f));
+ if (ci->access[j].in_use) {
+ SAFE(write_int16(ci->access[j].level, f));
+ SAFE(write_string(ci->access[j].nc->display, f));
+ SAFE(write_int32(ci->access[j].last_seen, f));
+ }
+ }
+
+ SAFE(write_int16(ci->akickcount, f));
+ for (j = 0; j < ci->akickcount; j++) {
+ SAFE(write_int16(ci->akick[j].flags, f));
+ if (ci->akick[j].flags & AK_USED) {
+ if (ci->akick[j].flags & AK_ISNICK)
+ SAFE(write_string(ci->akick[j].u.nc->display, f));
+ else
+ SAFE(write_string(ci->akick[j].u.mask, f));
+ SAFE(write_string(ci->akick[j].reason, f));
+ SAFE(write_string(ci->akick[j].creator, f));
+ SAFE(write_int32(ci->akick[j].addtime, f));
+ }
+ }
+
+ SAFE(write_int32(ci->mlock_on, f));
+ SAFE(write_int32(ci->mlock_off, f));
+ SAFE(write_int32(ci->mlock_limit, f));
+ SAFE(write_string(ci->mlock_key, f));
+#ifdef HAS_FMODE
+ SAFE(write_string(ci->mlock_flood, f));
+#else
+ SAFE(write_string(NULL, f));
+#endif
+#ifdef HAS_LMODE
+ SAFE(write_string(ci->mlock_redirect, f));
+#else
+ SAFE(write_string(NULL, f));
+#endif
+
+ SAFE(write_int16(ci->memos.memocount, f));
+ SAFE(write_int16(ci->memos.memomax, f));
+ memos = ci->memos.memos;
+ for (j = 0; j < ci->memos.memocount; j++, memos++) {
+ SAFE(write_int32(memos->number, f));
+ SAFE(write_int16(memos->flags, f));
+ SAFE(write_int32(memos->time, f));
+ SAFE(write_buffer(memos->sender, f));
+ SAFE(write_string(memos->text, f));
+ }
+
+ SAFE(write_string(ci->entry_message, f));
+
+ if (ci->bi)
+ SAFE(write_string(ci->bi->nick, f));
+ else
+ SAFE(write_string(NULL, f));
+
+ SAFE(write_int32(ci->botflags, f));
+
+ tmp16 = TTB_SIZE;
+ SAFE(write_int16(tmp16, f));
+ for (j = 0; j < TTB_SIZE; j++)
+ SAFE(write_int16(ci->ttb[j], f));
+
+ SAFE(write_int16(ci->capsmin, f));
+ SAFE(write_int16(ci->capspercent, f));
+ SAFE(write_int16(ci->floodlines, f));
+ SAFE(write_int16(ci->floodsecs, f));
+ SAFE(write_int16(ci->repeattimes, f));
+
+ SAFE(write_int16(ci->bwcount, f));
+ for (j = 0; j < ci->bwcount; j++) {
+ SAFE(write_int16(ci->badwords[j].in_use, f));
+ if (ci->badwords[j].in_use) {
+ SAFE(write_string(ci->badwords[j].word, f));
+ SAFE(write_int16(ci->badwords[j].type, f));
+ }
+ }
+ } /* for (chanlists[i]) */
+
+ SAFE(write_int8(0, f));
+
+ } /* for (i) */
+
+ close_db(f);
+
+}
+
+#undef SAFE
+
+/*************************************************************************/
+
+void save_cs_rdb_dbase(void)
+{
+#ifdef USE_RDB
+ int i;
+ ChannelInfo *ci;
+
+ if (!rdb_open())
+ return;
+
+ rdb_tag_table("anope_cs_info");
+ rdb_scrub_table("anope_ms_info", "serv='CHAN'");
+ rdb_clear_table("anope_cs_access");
+ rdb_clear_table("anope_cs_levels");
+ rdb_clear_table("anope_cs_akicks");
+ rdb_clear_table("anope_cs_badwords");
+
+ for (i = 0; i < 256; i++) {
+ for (ci = chanlists[i]; ci; ci = ci->next) {
+ rdb_save_cs_info(ci);
+ } /* for (chanlists[i]) */
+ } /* for (i) */
+
+ rdb_scrub_table("anope_cs_info", "active='0'");
+ rdb_close();
+#endif
+}
+
+/*************************************************************************/
+
+/* Check the current modes on a channel; if they conflict with a mode lock,
+ * fix them. */
+
+void check_modes(Channel * c)
+{
+ char modebuf[64], argbuf[BUFSIZE], *end = modebuf, *end2 = argbuf;
+ uint32 modes;
+ ChannelInfo *ci;
+ CBModeInfo *cbmi;
+ CBMode *cbm;
+
+ if (c->bouncy_modes)
+ return;
+
+ /* Check for mode bouncing */
+ if (c->server_modecount >= 3 && c->chanserv_modecount >= 3) {
+ wallops(NULL, "Warning: unable to set modes on channel %s. "
+ "Are your servers' U:lines configured correctly?",
+ c->name);
+ alog("%s: Bouncy modes on channel %s", s_ChanServ, c->name);
+ c->bouncy_modes = 1;
+ return;
+ }
+
+ if (c->chanserv_modetime != time(NULL)) {
+ c->chanserv_modecount = 0;
+ c->chanserv_modetime = time(NULL);
+ }
+ c->chanserv_modecount++;
+
+ if (!(ci = c->ci)) {
+#ifndef IRC_HYBRID
+ if (c->mode & CMODE_r) {
+ c->mode &= ~CMODE_r;
+ send_cmd(whosends(ci), "MODE %s -r", c->name);
+ }
+#endif
+ return;
+ }
+
+ modes = ~c->mode & ci->mlock_on;
+
+ *end++ = '+';
+ cbmi = cbmodeinfos;
+
+ do {
+ if (modes & cbmi->flag) {
+ *end++ = cbmi->mode;
+ c->mode |= cbmi->flag;
+
+ /* Add the eventual parameter and modify the Channel structure */
+ if (cbmi->getvalue && cbmi->csgetvalue) {
+ char *value = cbmi->csgetvalue(ci);
+
+ cbm = &cbmodes[(int) cbmi->mode];
+ cbm->setvalue(c, value);
+
+ if (value) {
+ *end2++ = ' ';
+ while (*value)
+ *end2++ = *value++;
+ }
+ }
+ } else if (cbmi->getvalue && cbmi->csgetvalue
+ && (ci->mlock_on & cbmi->flag)
+ && (c->mode & cbmi->flag)) {
+ char *value = cbmi->getvalue(c);
+ char *csvalue = cbmi->csgetvalue(ci);
+
+ /* Lock and actual values don't match, so fix the mode */
+ if (value && csvalue && strcmp(value, csvalue)) {
+ *end++ = cbmi->mode;
+
+ cbm = &cbmodes[(int) cbmi->mode];
+ cbm->setvalue(c, csvalue);
+
+ *end2++ = ' ';
+ while (*csvalue)
+ *end2++ = *csvalue++;
+ }
+ }
+ } while ((++cbmi)->flag != 0);
+
+ if (*(end - 1) == '+')
+ end--;
+
+ modes = c->mode & ci->mlock_off;
+
+ if (modes) {
+ *end++ = '-';
+ cbmi = cbmodeinfos;
+
+ do {
+ if (modes & cbmi->flag) {
+ *end++ = cbmi->mode;
+ c->mode &= ~cbmi->flag;
+
+ /* Add the eventual parameter and clean up the Channel structure */
+ if (cbmi->getvalue) {
+ cbm = &cbmodes[(int) cbmi->mode];
+
+ if (!(cbm->flags & CBM_MINUS_NO_ARG)) {
+ char *value = cbmi->getvalue(c);
+
+ if (value) {
+ *end2++ = ' ';
+ while (*value)
+ *end2++ = *value++;
+ }
+ }
+
+ cbm->setvalue(c, NULL);
+ }
+ }
+ } while ((++cbmi)->flag != 0);
+ }
+
+ if (end == modebuf)
+ return;
+
+ *end = 0;
+ *end2 = 0;
+
+ send_cmd(whosends(ci), "MODE %s %s%s", c->name, modebuf,
+ (end2 == argbuf ? "" : argbuf));
+}
+
+/*************************************************************************/
+
+#ifdef IRC_ULTIMATE3
+
+
+int check_valid_admin(User * user, Channel * chan, int servermode)
+{
+ if (!chan->ci)
+ return 1;
+
+ /* They will be kicked; no need to deop, no need to update our internal struct too */
+ if (chan->ci->flags & CI_VERBOTEN)
+ return 0;
+
+ if (servermode && !check_access(user, chan->ci, CA_AUTOPROTECT)) {
+ notice_lang(s_ChanServ, user, CHAN_IS_REGISTERED, s_ChanServ);
+ send_cmd(whosends(chan->ci), "MODE %s -a %s", chan->name,
+ user->nick);
+ return 0;
+ }
+
+ if (check_access(user, chan->ci, CA_AUTODEOP)) {
+ send_cmd(whosends(chan->ci), "MODE %s -a %s", chan->name,
+ user->nick);
+ return 0;
+ }
+
+ return 1;
+}
+#endif
+
+/*************************************************************************/
+
+/* Check whether a user is allowed to be opped on a channel; if they
+ * aren't, deop them. If serverop is 1, the +o was done by a server.
+ * Return 1 if the user is allowed to be opped, 0 otherwise. */
+
+int check_valid_op(User * user, Channel * chan, int servermode)
+{
+ if (!chan->ci)
+ return 1;
+
+ /* They will be kicked; no need to deop, no need to update our internal struct too */
+ if (chan->ci->flags & CI_VERBOTEN)
+ return 0;
+
+ if (servermode && !check_access(user, chan->ci, CA_AUTOOP)) {
+ notice_lang(s_ChanServ, user, CHAN_IS_REGISTERED, s_ChanServ);
+#ifdef HAS_HALFOP
+# if defined(IRC_UNREAL)
+ if (check_access(user, chan->ci, CA_AUTOHALFOP)) {
+ send_cmd(whosends(chan->ci), "MODE %s -aoq %s %s %s",
+ chan->name, user->nick, user->nick, user->nick);
+ } else {
+ send_cmd(whosends(chan->ci), "MODE %s -ahoq %s %s %s %s",
+ chan->name, user->nick, user->nick, user->nick,
+ user->nick);
+ }
+# elif defined(IRC_ULTIMATE3)
+ if (check_access(user, chan->ci, CA_AUTOHALFOP)) {
+ send_cmd(whosends(chan->ci), "MODE %s -ao %s %s",
+ chan->name, user->nick, user->nick);
+ } else {
+ send_cmd(whosends(chan->ci), "MODE %s -aoh %s %s %s",
+ chan->name, user->nick, user->nick, user->nick);
+ }
+# else
+ if (check_access(user, chan->ci, CA_AUTOHALFOP)) {
+ send_cmd(whosends(chan->ci), "MODE %s -o %s", chan->name,
+ user->nick);
+ } else {
+ send_cmd(whosends(chan->ci), "MODE %s -ho %s %s", chan->name,
+ user->nick, user->nick);
+ }
+# endif
+#else
+ send_cmd(whosends(chan->ci), "MODE %s -o %s", chan->name,
+ user->nick);
+#endif
+ return 0;
+ }
+
+ if (check_access(user, chan->ci, CA_AUTODEOP)) {
+#ifdef HAS_HALFOP
+# ifdef IRC_UNREAL
+ send_cmd(whosends(chan->ci), "MODE %s -ahoq %s %s %s %s",
+ chan->name, user->nick, user->nick, user->nick,
+ user->nick);
+# else
+ send_cmd(whosends(chan->ci), "MODE %s -ho %s %s", chan->name,
+ user->nick, user->nick);
+# endif
+#else
+ send_cmd(whosends(chan->ci), "MODE %s -o %s", chan->name,
+ user->nick);
+#endif
+ return 0;
+ }
+
+ return 1;
+}
+
+/*************************************************************************/
+
+/* Check whether a user should be opped on a channel, and if so, do it.
+ * Return 1 if the user was opped, 0 otherwise. (Updates the channel's
+ * last used time if the user was opped.) */
+
+int check_should_op(User * user, const char *chan)
+{
+ ChannelInfo *ci = cs_findchan(chan);
+
+ if (!ci || (ci->flags & CI_VERBOTEN) || *chan == '+')
+ return 0;
+
+ if ((ci->flags & CI_SECURE) && !nick_identified(user))
+ return 0;
+
+ if (check_access(user, ci, CA_AUTOOP)) {
+ send_cmd(whosends(ci), "MODE %s +o %s", chan, user->nick);
+ return 1;
+ }
+
+ return 0;
+}
+
+/*************************************************************************/
+
+/* Check whether a user should be voiced on a channel, and if so, do it.
+ * Return 1 if the user was voiced, 0 otherwise. */
+
+int check_should_voice(User * user, const char *chan)
+{
+ ChannelInfo *ci = cs_findchan(chan);
+
+ if (!ci || (ci->flags & CI_VERBOTEN) || *chan == '+')
+ return 0;
+
+ if ((ci->flags & CI_SECURE) && !nick_identified(user))
+ return 0;
+
+ if (check_access(user, ci, CA_AUTOVOICE)) {
+ send_cmd(whosends(ci), "MODE %s +v %s", chan, user->nick);
+ return 1;
+ }
+
+ return 0;
+}
+
+/*************************************************************************/
+
+#ifdef HAS_HALFOP
+
+int check_should_halfop(User * user, const char *chan)
+{
+ ChannelInfo *ci = cs_findchan(chan);
+
+ if (!ci || (ci->flags & CI_VERBOTEN) || *chan == '+')
+ return 0;
+
+ if (check_access(user, ci, CA_AUTOHALFOP)) {
+ send_cmd(whosends(ci), "MODE %s +h %s", chan, user->nick);
+ return 1;
+ }
+
+ return 0;
+}
+
+#endif
+
+/*************************************************************************/
+
+#if defined(IRC_UNREAL) || defined(IRC_VIAGRA)
+
+int check_should_owner(User * user, const char *chan)
+{
+ ChannelInfo *ci = cs_findchan(chan);
+
+ if (!ci || (ci->flags & CI_VERBOTEN) || *chan == '+')
+ return 0;
+
+ if (((ci->flags & CI_SECUREFOUNDER) && is_real_founder(user, ci))
+ || (!(ci->flags & CI_SECUREFOUNDER) && is_founder(user, ci))) {
+ send_cmd(whosends(ci), "MODE %s +oq %s %s", chan, user->nick,
+ user->nick);
+ return 1;
+ }
+
+ return 0;
+}
+
+#endif
+
+/*************************************************************************/
+
+#if defined(IRC_UNREAL) || defined(IRC_VIAGRA) || defined(IRC_ULTIMATE3)
+
+int check_should_protect(User * user, const char *chan)
+{
+ ChannelInfo *ci = cs_findchan(chan);
+
+ if (!ci || (ci->flags & CI_VERBOTEN) || *chan == '+')
+ return 0;
+
+ if (check_access(user, ci, CA_AUTOPROTECT)) {
+ send_cmd(whosends(ci), "MODE %s +oa %s %s", chan, user->nick,
+ user->nick);
+ return 1;
+ }
+
+ return 0;
+}
+
+#endif
+
+/*************************************************************************/
+
+/* Tiny helper routine to get ChanServ out of a channel after it went in. */
+
+static void timeout_leave(Timeout * to)
+{
+ char *chan = to->data;
+ ChannelInfo *ci = cs_findchan(chan);
+
+ if (ci) /* Check cos the channel may be dropped in the meantime */
+ ci->flags &= ~CI_INHABIT;
+
+ send_cmd(s_ChanServ, "PART %s", chan);
+ free(to->data);
+}
+
+
+/* Check whether a user is permitted to be on a channel. If so, return 0;
+ * else, kickban the user with an appropriate message (could be either
+ * AKICK or restricted access) and return 1. Note that this is called
+ * _before_ the user is added to internal channel lists (so do_kick() is
+ * not called).
+ */
+
+int check_kick(User * user, char *chan)
+{
+ ChannelInfo *ci = cs_findchan(chan);
+ Channel *c;
+ AutoKick *akick;
+ int i;
+ NickCore *nc;
+ char *av[3];
+ char mask[BUFSIZE];
+ const char *reason;
+ Timeout *t;
+
+ if (!ci)
+ return 0;
+
+ if (is_oper(user) || is_services_admin(user))
+ return 0;
+
+ if (ci->flags & CI_VERBOTEN) {
+ get_idealban(ci, user, mask, sizeof(mask));
+ reason =
+ ci->forbidreason ? ci->forbidreason : getstring(user->na,
+ CHAN_MAY_NOT_BE_USED);
+ goto kick;
+ }
+
+ if (ci->flags & CI_SUSPENDED) {
+ get_idealban(ci, user, mask, sizeof(mask));
+ reason =
+ ci->forbidreason ? ci->forbidreason : getstring(user->na,
+ CHAN_MAY_NOT_BE_USED);
+ goto kick;
+ }
+
+ if (nick_recognized(user))
+ nc = user->na->nc;
+ else
+ nc = NULL;
+
+#if defined (IRC_ULTIMATE) || defined(IRC_ULTIMATE3) || defined(IRC_UNREAL) || defined(IRC_VIAGRA) || defined(IRC_HYBRID)
+ /*
+ * Before we go through akick lists, see if they're excepted FIRST
+ * We cannot kick excempted users that are akicked or not on the channel access list
+ * as that will start services <-> server wars which ends up as a DoS against services.
+ *
+ * UltimateIRCd 3.x at least informs channel staff when a joining user is matching an exempt.
+ */
+ if (is_excepted(ci, user) == 1) {
+ return 0;
+ }
+#endif
+
+ for (akick = ci->akick, i = 0; i < ci->akickcount; akick++, i++) {
+ if (!(akick->flags & AK_USED))
+ continue;
+ if ((akick->flags & AK_ISNICK && akick->u.nc == nc)
+ || (!(akick->flags & AK_ISNICK)
+ && match_usermask(akick->u.mask, user))) {
+ if (debug >= 2)
+ alog("debug: %s matched akick %s", user->nick,
+ (akick->flags & AK_ISNICK) ? akick->u.nc->
+ display : akick->u.mask);
+ if (akick->flags & AK_ISNICK)
+ get_idealban(ci, user, mask, sizeof(mask));
+ else
+ strcpy(mask, akick->u.mask);
+ reason = akick->reason ? akick->reason : CSAutokickReason;
+ goto kick;
+ }
+ }
+
+ if (check_access(user, ci, CA_NOJOIN)) {
+ get_idealban(ci, user, mask, sizeof(mask));
+ reason = getstring(user->na, CHAN_NOT_ALLOWED_TO_JOIN);
+ goto kick;
+ }
+
+ return 0;
+
+ kick:
+ if (debug)
+ alog("debug: channel: AutoKicking %s!%s@%s from %s", user->nick,
+ user->username, GetHost(user), chan);
+
+ /* Remember that the user has not been added to our channel user list
+ * yet, so we check whether the channel does not exist OR has no user
+ * on it (before SJOIN would have created the channel structure, while
+ * JOIN would not). */
+ /* Don't check for CI_INHABIT before for the Channel record cos else
+ * c may be NULL even if it exists */
+ if ((!(c = findchan(chan)) || c->usercount == 0)
+ && !(ci->flags & CI_INHABIT)) {
+#if defined(IRC_BAHAMUT)
+ send_cmd(s_ChanServ, "SJOIN %lu %s",
+ (c ? c->creation_time : time(NULL)), chan);
+#elif defined(IRC_HYBRID)
+ send_cmd(NULL, "SJOIN %ld %s + :@%s",
+ time(NULL), chan, s_ChanServ);
+#else
+ send_cmd(s_ChanServ, "JOIN %s", chan);
+#endif
+ t = add_timeout(CSInhabit, timeout_leave, 0);
+ t->data = sstrdup(chan);
+ ci->flags |= CI_INHABIT;
+ }
+
+ if (c) {
+ av[0] = chan;
+ av[1] = sstrdup("+b");
+ av[2] = mask;
+ do_cmode(whosends(ci), 3, av);
+ free(av[1]);
+ }
+
+ send_cmd(whosends(ci), "MODE %s +b %s %lu", chan, mask, time(NULL));
+ send_cmd(whosends(ci), "KICK %s %s :%s", chan, user->nick, reason);
+
+ return 1;
+}
+
+/*************************************************************************/
+
+/* Record the current channel topic in the ChannelInfo structure. */
+
+void record_topic(const char *chan)
+{
+ Channel *c;
+ ChannelInfo *ci;
+
+ if (readonly)
+ return;
+ c = findchan(chan);
+ if (!c || !(ci = c->ci))
+ return;
+ if (ci->last_topic)
+ free(ci->last_topic);
+ if (c->topic)
+ ci->last_topic = sstrdup(c->topic);
+ else
+ ci->last_topic = NULL;
+ strscpy(ci->last_topic_setter, c->topic_setter, NICKMAX);
+ ci->last_topic_time = c->topic_time;
+}
+
+/*************************************************************************/
+
+/* Restore the topic in a channel when it's created, if we should. */
+
+void restore_topic(const char *chan)
+{
+ Channel *c = findchan(chan);
+ ChannelInfo *ci;
+
+ if (!c || !(ci = c->ci) || !(ci->flags & CI_KEEPTOPIC))
+ return;
+ if (c->topic)
+ free(c->topic);
+ if (ci->last_topic) {
+ c->topic = sstrdup(ci->last_topic);
+ strscpy(c->topic_setter, ci->last_topic_setter, NICKMAX);
+ c->topic_time = ci->last_topic_time;
+ } else {
+ c->topic = NULL;
+ strscpy(c->topic_setter, s_ChanServ, NICKMAX);
+ }
+#ifdef IRC_HYBRID
+ if (whosends(ci) == s_ChanServ) {
+ send_cmd(NULL, "SJOIN %ld %s + :%s", time(NULL), chan, s_ChanServ);
+ send_cmd(NULL, "MODE %s +o %s", chan, s_ChanServ);
+ }
+ send_cmd(whosends(ci), "TOPIC %s :%s", chan, c->topic ? c->topic : "");
+ if (whosends(ci) == s_ChanServ) {
+ send_cmd(s_ChanServ, "PART %s", chan);
+ }
+#else
+ send_cmd(whosends(ci), "TOPIC %s %s %lu :%s", c->name, c->topic_setter,
+ c->topic_time, c->topic ? c->topic : "");
+#endif
+}
+
+/*************************************************************************/
+
+/* See if the topic is locked on the given channel, and return 1 (and fix
+ * the topic) if so. */
+
+int check_topiclock(Channel * c, time_t topic_time)
+{
+ ChannelInfo *ci;
+
+ if (!(ci = c->ci) || !(ci->flags & CI_TOPICLOCK))
+ return 0;
+
+ if (c->topic)
+ free(c->topic);
+ if (ci->last_topic)
+ c->topic = sstrdup(ci->last_topic);
+ else
+ c->topic = NULL;
+
+ strscpy(c->topic_setter, ci->last_topic_setter, NICKMAX);
+#ifdef IRC_UNREAL
+ /* Because older timestamps are rejected */
+ c->topic_time = topic_time + 1;
+#else
+ c->topic_time = ci->last_topic_time;
+#endif
+
+#ifdef IRC_HYBRID
+ if (whosends(ci) == s_ChanServ) {
+ send_cmd(NULL, "SJOIN %ld %s + :%s", time(NULL), c->name,
+ s_ChanServ);
+ send_cmd(NULL, "MODE %s +o %s", c->name, s_ChanServ);
+ }
+ send_cmd(whosends(ci), "TOPIC %s :%s", c->name,
+ c->topic ? c->topic : "");
+ if (whosends(ci) == s_ChanServ) {
+ send_cmd(s_ChanServ, "PART %s", c->name);
+ }
+#else
+ send_cmd(whosends(ci), "TOPIC %s %s %lu :%s", c->name, c->topic_setter,
+ c->topic_time, c->topic ? c->topic : "");
+#endif
+ return 1;
+}
+
+/*************************************************************************/
+
+/* Remove all channels which have expired. */
+
+void expire_chans()
+{
+ ChannelInfo *ci, *next;
+ int i;
+ time_t now = time(NULL);
+
+ if (!CSExpire)
+ return;
+
+ for (i = 0; i < 256; i++) {
+ for (ci = chanlists[i]; ci; ci = next) {
+ next = ci->next;
+ if (!ci->c && now - ci->last_used >= CSExpire
+ && !(ci->flags & (CI_VERBOTEN | CI_NO_EXPIRE))) {
+ alog("Expiring channel %s (founder: %s)", ci->name,
+ (ci->founder ? ci->founder->display : "(none)"));
+ delchan(ci);
+ }
+ }
+ }
+}
+
+/*************************************************************************/
+
+/* Remove a (deleted or expired) nickname from all channel lists. */
+
+void cs_remove_nick(const NickCore * nc)
+{
+ int i, j;
+ ChannelInfo *ci, *next;
+ ChanAccess *ca;
+ AutoKick *akick;
+
+ for (i = 0; i < 256; i++) {
+ for (ci = chanlists[i]; ci; ci = next) {
+ next = ci->next;
+ if (ci->founder == nc) {
+ if (ci->successor) {
+ NickCore *nc2 = ci->successor;
+ if (!nick_is_services_admin(nc2) && nc2->channelmax > 0
+ && nc2->channelcount >= nc2->channelmax) {
+ alog("%s: Successor (%s) of %s owns too many channels, " "deleting channel", s_ChanServ, nc2->display, ci->name);
+ delchan(ci);
+ continue;
+ } else {
+ alog("%s: Transferring foundership of %s from deleted " "nick %s to successor %s", s_ChanServ, ci->name, nc->display, nc2->display);
+ ci->founder = nc2;
+ ci->successor = NULL;
+ nc2->channelcount++;
+#ifdef USE_RDB
+ if (rdb_open()) {
+ rdb_cs_set_founder(ci->name, nc2->display);
+ rdb_close();
+ }
+#endif
+ }
+ } else {
+ alog("%s: Deleting channel %s owned by deleted nick %s", s_ChanServ, ci->name, nc->display);
+#ifndef IRC_HYBRID
+ /* Maybe move this to delchan() ? */
+ if ((ci->c) && (ci->c->mode & CMODE_r)) {
+ ci->c->mode &= ~CMODE_r;
+ send_cmd(whosends(ci), "MODE %s -r", ci->name);
+ }
+#endif
+
+ delchan(ci);
+ continue;
+ }
+ }
+
+ if (ci->successor == nc)
+ ci->successor = NULL;
+
+ for (ca = ci->access, j = ci->accesscount; j > 0; ca++, j--) {
+ if (ca->in_use && ca->nc == nc) {
+ ca->in_use = 0;
+ ca->nc = NULL;
+ }
+ }
+
+ for (akick = ci->akick, j = ci->akickcount; j > 0;
+ akick++, j--) {
+ if ((akick->flags & AK_USED) && (akick->flags & AK_ISNICK)
+ && akick->u.nc == nc) {
+ if (akick->creator) {
+ free(akick->creator);
+ akick->creator = NULL;
+ }
+ if (akick->reason) {
+ free(akick->reason);
+ akick->reason = NULL;
+ }
+ akick->flags = 0;
+ akick->u.nc = NULL;
+ }
+ }
+ }
+ }
+#ifdef USE_RDB
+ if (rdb_open()) {
+ rdb_cs_deluser(nc->display);
+ rdb_close();
+ }
+#endif
+}
+
+/*************************************************************************/
+
+/* Removes any reference to a bot */
+
+void cs_remove_bot(const BotInfo * bi)
+{
+ int i;
+ ChannelInfo *ci;
+
+ for (i = 0; i < 256; i++)
+ for (ci = chanlists[i]; ci; ci = ci->next)
+ if (ci->bi == bi)
+ ci->bi = NULL;
+}
+
+/*************************************************************************/
+
+/* Return the ChannelInfo structure for the given channel, or NULL if the
+ * channel isn't registered. */
+
+ChannelInfo *cs_findchan(const char *chan)
+{
+ ChannelInfo *ci;
+
+ for (ci = chanlists[tolower(chan[1])]; ci; ci = ci->next) {
+ if (stricmp(ci->name, chan) == 0)
+ return ci;
+ }
+ return NULL;
+}
+
+/*************************************************************************/
+
+/* Return 1 if the user's access level on the given channel falls into the
+ * given category, 0 otherwise. Note that this may seem slightly confusing
+ * in some cases: for example, check_access(..., CA_NOJOIN) returns true if
+ * the user does _not_ have access to the channel (i.e. matches the NOJOIN
+ * criterion). */
+
+int check_access(User * user, ChannelInfo * ci, int what)
+{
+ int level = get_access(user, ci);
+ int limit = ci->levels[what];
+
+ /* Resetting the last used time */
+ if (level > 0)
+ ci->last_used = time(NULL);
+
+ if (level == ACCESS_FOUNDER)
+ return (what == CA_AUTODEOP || what == CA_NOJOIN) ? 0 : 1;
+ /* Hacks to make flags work */
+ if (what == CA_AUTODEOP && (ci->flags & CI_SECUREOPS) && level == 0)
+ return 1;
+ if (limit == ACCESS_INVALID)
+ return 0;
+ if (what == CA_AUTODEOP || what == CA_NOJOIN)
+ return level <= ci->levels[what];
+ else
+ return level >= ci->levels[what];
+}
+
+/*************************************************************************/
+/*********************** ChanServ private routines ***********************/
+/*************************************************************************/
+
+/* Insert a channel alphabetically into the database. */
+
+static void alpha_insert_chan(ChannelInfo * ci)
+{
+ ChannelInfo *ptr, *prev;
+ char *chan = ci->name;
+
+ for (prev = NULL, ptr = chanlists[tolower(chan[1])];
+ ptr != NULL && stricmp(ptr->name, chan) < 0;
+ prev = ptr, ptr = ptr->next);
+ ci->prev = prev;
+ ci->next = ptr;
+ if (!prev)
+ chanlists[tolower(chan[1])] = ci;
+ else
+ prev->next = ci;
+ if (ptr)
+ ptr->prev = ci;
+}
+
+/*************************************************************************/
+
+/* Add a channel to the database. Returns a pointer to the new ChannelInfo
+ * structure if the channel was successfully registered, NULL otherwise.
+ * Assumes channel does not already exist. */
+
+static ChannelInfo *makechan(const char *chan)
+{
+ int i;
+ ChannelInfo *ci;
+
+ ci = scalloc(sizeof(ChannelInfo), 1);
+ strscpy(ci->name, chan, CHANMAX);
+ ci->time_registered = time(NULL);
+ reset_levels(ci);
+ ci->ttb = scalloc(2 * TTB_SIZE, 1);
+ for (i = 0; i < TTB_SIZE; i++)
+ ci->ttb[i] = 0;
+ alpha_insert_chan(ci);
+ return ci;
+}
+
+/*************************************************************************/
+
+/* Remove a channel from the ChanServ database. Return 1 on success, 0
+ * otherwise. */
+
+static int delchan(ChannelInfo * ci)
+{
+ int i;
+ NickCore *nc = ci->founder;
+
+ if (ci->bi) {
+ ci->bi->chancount--;
+ }
+ if (ci->c) {
+ if (ci->bi && ci->c->usercount >= BSMinUsers) {
+ send_cmd(ci->bi->nick, "PART %s", ci->c->name);
+ }
+ ci->c->ci = NULL;
+ }
+#ifdef USE_RDB
+ if (rdb_open()) {
+ rdb_cs_delchan(ci);
+ rdb_close();
+ }
+#endif
+ if (ci->next)
+ ci->next->prev = ci->prev;
+ if (ci->prev)
+ ci->prev->next = ci->next;
+ else
+ chanlists[tolower(ci->name[1])] = ci->next;
+ if (ci->desc)
+ free(ci->desc);
+ if (ci->mlock_key)
+ free(ci->mlock_key);
+#ifdef HAS_FMODE
+ if (ci->mlock_flood)
+ free(ci->mlock_flood);
+#endif
+#ifdef HAS_LMODE
+ if (ci->mlock_redirect)
+ free(ci->mlock_redirect);
+#endif
+ if (ci->last_topic)
+ free(ci->last_topic);
+ if (ci->forbidby)
+ free(ci->forbidby);
+ if (ci->forbidreason)
+ free(ci->forbidreason);
+ if (ci->access)
+ free(ci->access);
+ for (i = 0; i < ci->akickcount; i++) {
+ if (!(ci->akick[i].flags & AK_ISNICK) && ci->akick[i].u.mask)
+ free(ci->akick[i].u.mask);
+ if (ci->akick[i].reason)
+ free(ci->akick[i].reason);
+ if (ci->akick[i].creator)
+ free(ci->akick[i].creator);
+ }
+ if (ci->akick)
+ free(ci->akick);
+ if (ci->levels)
+ free(ci->levels);
+ if (ci->memos.memos) {
+ for (i = 0; i < ci->memos.memocount; i++) {
+ if (ci->memos.memos[i].text)
+ free(ci->memos.memos[i].text);
+ }
+ free(ci->memos.memos);
+ }
+ if (ci->ttb)
+ free(ci->ttb);
+ for (i = 0; i < ci->bwcount; i++) {
+ if (ci->badwords[i].word)
+ free(ci->badwords[i].word);
+ }
+ if (ci->badwords)
+ free(ci->badwords);
+ free(ci);
+ if (nc)
+ nc->channelcount--;
+
+ return 1;
+}
+
+/*************************************************************************/
+
+/* Reset channel access level values to their default state. */
+
+static void reset_levels(ChannelInfo * ci)
+{
+ int i;
+
+ if (ci->levels)
+ free(ci->levels);
+ ci->levels = scalloc(CA_SIZE * sizeof(*ci->levels), 1);
+ for (i = 0; def_levels[i][0] >= 0; i++)
+ ci->levels[def_levels[i][0]] = def_levels[i][1];
+}
+
+/*************************************************************************/
+
+/* Does the given user have founder access to the channel? */
+
+int is_founder(User * user, ChannelInfo * ci)
+{
+ if (user->isSuperAdmin) {
+ return 1;
+ }
+
+ if (user->na && user->na->nc == ci->founder) {
+ if ((nick_identified(user)
+ || (nick_recognized(user) && !(ci->flags & CI_SECURE))))
+ return 1;
+ }
+ if (is_identified(user, ci))
+ return 1;
+ return 0;
+}
+
+/*************************************************************************/
+
+static int is_real_founder(User * user, ChannelInfo * ci)
+{
+ if (user->isSuperAdmin) {
+ return 1;
+ }
+
+ if (user->na && user->na->nc == ci->founder) {
+ if ((nick_identified(user)
+ || (nick_recognized(user) && !(ci->flags & CI_SECURE))))
+ return 1;
+ }
+ return 0;
+}
+
+/*************************************************************************/
+
+/* Has the given user password-identified as founder for the channel? */
+
+static int is_identified(User * user, ChannelInfo * ci)
+{
+ struct u_chaninfolist *c;
+
+ for (c = user->founder_chans; c; c = c->next) {
+ if (c->chan == ci)
+ return 1;
+ }
+ return 0;
+}
+
+/*************************************************************************/
+
+/* Returns the ChanAccess entry for an user */
+
+ChanAccess *get_access_entry(NickCore * nc, ChannelInfo * ci)
+{
+ ChanAccess *access;
+ int i;
+
+ for (access = ci->access, i = 0; i < ci->accesscount; access++, i++)
+ if (access->in_use && access->nc == nc)
+ return access;
+
+ return NULL;
+}
+
+/*************************************************************************/
+
+/* Return the access level the given user has on the channel. If the
+ * channel doesn't exist, the user isn't on the access list, or the channel
+ * is CS_SECURE and the user hasn't IDENTIFY'd with NickServ, return 0. */
+
+int get_access(User * user, ChannelInfo * ci)
+{
+ ChanAccess *access;
+
+ if (!ci)
+ return 0;
+
+ if (is_founder(user, ci))
+ return ACCESS_FOUNDER;
+
+ if (!user->na)
+ return 0;
+
+ if (nick_identified(user)
+ || (nick_recognized(user) && !(ci->flags & CI_SECURE)))
+ if ((access = get_access_entry(user->na->nc, ci)))
+ return access->level;
+
+ return 0;
+}
+
+/*************************************************************************/
+
+void update_cs_lastseen(User * user, ChannelInfo * ci)
+{
+ ChanAccess *access;
+
+ if (!ci || !user->na)
+ return;
+
+ if (is_founder(user, ci) || nick_identified(user)
+ || (nick_recognized(user) && !(ci->flags & CI_SECURE)))
+ if ((access = get_access_entry(user->na->nc, ci)))
+ access->last_seen = time(NULL);
+}
+
+/*************************************************************************/
+
+/* Returns the best ban possible for an user depending of the bantype
+ value. */
+
+int get_idealban(ChannelInfo * ci, User * u, char *ret, int retlen)
+{
+ char *mask;
+
+ if (!ci || !u || !ret || retlen == 0)
+ return 0;
+
+ switch (ci->bantype) {
+ case 0:
+ snprintf(ret, retlen, "*!%s@%s", GetIdent(u), GetHost(u));
+ return 1;
+ case 1:
+ snprintf(ret, retlen, "*!%s%s@%s",
+ (strlen(GetIdent(u)) <
+ (*(GetIdent(u)) ==
+ '~' ? USERMAX + 1 : USERMAX) ? "*" : ""),
+ (*(GetIdent(u)) == '~' ? GetIdent(u) + 1 : GetIdent(u)),
+ GetHost(u));
+ return 1;
+ case 2:
+ snprintf(ret, retlen, "*!*@%s", GetHost(u));
+ return 1;
+ case 3:
+ mask = create_mask(u);
+ snprintf(ret, retlen, "*!%s", mask);
+ free(mask);
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+/*************************************************************************/
+
+static void make_unidentified(User * u, ChannelInfo * ci)
+{
+ struct u_chaninfolist *uci;
+
+ if (!u || !ci)
+ return;
+
+ for (uci = u->founder_chans; uci; uci = uci->next) {
+ if (uci->chan == ci) {
+ if (uci->next)
+ uci->next->prev = uci->prev;
+ if (uci->prev)
+ uci->prev->next = uci->next;
+ else
+ u->founder_chans = uci->next;
+ free(uci);
+ break;
+ }
+ }
+}
+
+/*************************************************************************/
+
+#ifdef HAS_FMODE
+
+char *cs_get_flood(ChannelInfo * ci)
+{
+ return ci->mlock_flood;
+}
+
+#endif
+
+/*************************************************************************/
+
+char *cs_get_key(ChannelInfo * ci)
+{
+ return ci->mlock_key;
+}
+
+/*************************************************************************/
+
+char *cs_get_limit(ChannelInfo * ci)
+{
+ static char limit[16];
+
+ if (ci->mlock_limit == 0)
+ return NULL;
+
+ snprintf(limit, sizeof(limit), "%lu", ci->mlock_limit);
+ return limit;
+}
+
+/*************************************************************************/
+
+#ifdef HAS_LMODE
+
+char *cs_get_redirect(ChannelInfo * ci)
+{
+ return ci->mlock_redirect;
+}
+
+#endif
+
+/*************************************************************************/
+
+#ifdef HAS_FMODE
+
+void cs_set_flood(ChannelInfo * ci, char *value)
+{
+ char *dp, *end;
+
+ if (ci->mlock_flood)
+ free(ci->mlock_flood);
+
+ /* This looks ugly, but it works ;) */
+ if (value && *value != ':'
+ && (strtoul((*value == '*' ? value + 1 : value), &dp, 10) > 0)
+ && (*dp == ':') && (*(++dp) != 0) && (strtoul(dp, &end, 10) > 0)
+ && (*end == 0)) {
+ ci->mlock_flood = sstrdup(value);
+ } else {
+ ci->mlock_on &= ~CMODE_f;
+ ci->mlock_flood = NULL;
+ }
+}
+
+#endif
+
+/*************************************************************************/
+
+void cs_set_key(ChannelInfo * ci, char *value)
+{
+ if (ci->mlock_key)
+ free(ci->mlock_key);
+
+ /* Don't allow keys with a coma */
+ if (value && *value != ':' && !strchr(value, ',')) {
+ ci->mlock_key = sstrdup(value);
+ } else {
+ ci->mlock_on &= ~CMODE_k;
+ ci->mlock_key = NULL;
+ }
+}
+
+/*************************************************************************/
+
+void cs_set_limit(ChannelInfo * ci, char *value)
+{
+ ci->mlock_limit = value ? strtoul(value, NULL, 10) : 0;
+
+ if (ci->mlock_limit <= 0)
+ ci->mlock_on &= ~CMODE_l;
+}
+
+/*************************************************************************/
+
+#ifdef HAS_LMODE
+
+void cs_set_redirect(ChannelInfo * ci, char *value)
+{
+ if (ci->mlock_redirect)
+ free(ci->mlock_redirect);
+
+ /* Don't allow keys with a coma */
+ if (value && *value == '#') {
+ ci->mlock_redirect = sstrdup(value);
+ } else {
+ ci->mlock_on &= ~CMODE_L;
+ ci->mlock_redirect = NULL;
+ }
+}
+
+#endif
+
+int get_access_level(ChannelInfo * ci, NickAlias * na)
+{
+ ChanAccess *access;
+ int num;
+
+ if (na->nc == ci->founder) {
+ return ACCESS_FOUNDER;
+ }
+
+ for (num = 0; num < ci->accesscount; num++) {
+
+ access = &ci->access[num];
+
+ if (!access->in_use)
+ return 0;
+
+ if (access->nc == na->nc) {
+ return access->level;
+ }
+
+ }
+
+ return 0;
+
+}
+
+char *get_xop_level(int level)
+{
+
+ if (level < ACCESS_VOP) {
+ return "Err";
+#ifdef HAS_HALFOP
+ } else if (level < ACCESS_HOP) {
+ return "VOP";
+ } else if (level < ACCESS_AOP) {
+ return "HOP";
+#else
+ } else if (level < ACCESS_AOP) {
+ return "VOP";
+#endif
+ } else if (level < ACCESS_SOP) {
+ return "AOP";
+ } else if (level < ACCESS_FOUNDER) {
+ return "SOP";
+ } else {
+ return "Founder";
+ }
+
+}
+
+/*************************************************************************/
+/*********************** ChanServ command routines ***********************/
+/*************************************************************************/
+
+static int do_help(User * u)
+{
+ char *cmd = strtok(NULL, "");
+
+ if (!cmd) {
+ notice_help(s_ChanServ, u, CHAN_HELP);
+#ifdef IRC_UNREAL
+ notice_help(s_ChanServ, u, CHAN_HELP_UNREAL);
+#endif
+#ifdef IRC_VIAGRA
+ notice_help(s_ChanServ, u, CHAN_HELP_UNREAL);
+#endif
+#ifdef IRC_ULTIMATE
+ notice_help(s_ChanServ, u, CHAN_HELP_ULTIMATE);
+#endif
+#ifdef IRC_ULTIMATE3
+ notice_help(s_ChanServ, u, CHAN_HELP_ULTIMATE3);
+#endif
+ if (CSExpire >= 86400)
+ notice_help(s_ChanServ, u, CHAN_HELP_EXPIRES,
+ CSExpire / 86400);
+ if (is_services_oper(u))
+ notice_help(s_ChanServ, u, CHAN_SERVADMIN_HELP);
+ moduleDisplayHelp(2, u);
+ } else if (stricmp(cmd, "LEVELS DESC") == 0) {
+ int i;
+ notice_help(s_ChanServ, u, CHAN_HELP_LEVELS_DESC);
+ if (!levelinfo_maxwidth) {
+ for (i = 0; levelinfo[i].what >= 0; i++) {
+ int len = strlen(levelinfo[i].name);
+ if (len > levelinfo_maxwidth)
+ levelinfo_maxwidth = len;
+ }
+ }
+ for (i = 0; levelinfo[i].what >= 0; i++) {
+ notice_help(s_ChanServ, u, CHAN_HELP_LEVELS_DESC_FORMAT,
+ levelinfo_maxwidth, levelinfo[i].name,
+ getstring(u->na, levelinfo[i].desc));
+ }
+ } else {
+ mod_help_cmd(s_ChanServ, u, CHANSERV, cmd);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_register(User * u)
+{
+ char *chan = strtok(NULL, " ");
+ char *pass = strtok(NULL, " ");
+ char *desc = strtok(NULL, "");
+ NickCore *nc;
+ Channel *c;
+ ChannelInfo *ci;
+ struct u_chaninfolist *uc;
+ int is_servadmin = is_services_admin(u);
+#ifdef USE_ENCRYPTION
+ char founderpass[PASSMAX + 1];
+#endif
+
+ if (readonly) {
+ notice_lang(s_ChanServ, u, CHAN_REGISTER_DISABLED);
+ return MOD_CONT;
+ }
+
+ if (checkDefCon(DEFCON_NO_NEW_CHANNELS)) {
+ notice_lang(s_ChanServ, u, OPER_DEFCON_DENIED);
+ return MOD_CONT;
+ }
+
+ if (!desc) {
+ syntax_error(s_ChanServ, u, "REGISTER", CHAN_REGISTER_SYNTAX);
+ } else if (*chan == '&') {
+ notice_lang(s_ChanServ, u, CHAN_REGISTER_NOT_LOCAL);
+ } else if (!u->na || !(nc = u->na->nc)) {
+ notice_lang(s_ChanServ, u, CHAN_MUST_REGISTER_NICK, s_NickServ);
+ } else if (!nick_recognized(u)) {
+ notice_lang(s_ChanServ, u, CHAN_MUST_IDENTIFY_NICK, s_NickServ,
+ s_NickServ);
+ } else if ((ci = cs_findchan(chan)) != NULL) {
+ if (ci->flags & CI_VERBOTEN) {
+ alog("%s: Attempt to register FORBIDden channel %s by %s!%s@%s", s_ChanServ, ci->name, u->nick, u->username, GetHost(u));
+ notice_lang(s_ChanServ, u, CHAN_MAY_NOT_BE_REGISTERED, chan);
+ } else {
+ notice_lang(s_ChanServ, u, CHAN_ALREADY_REGISTERED, chan);
+ }
+ } else if (!stricmp(chan, "#")) {
+ notice_lang(s_ChanServ, u, CHAN_MAY_NOT_BE_REGISTERED, chan);
+ } else if (!(c = findchan(chan))
+ || !chan_has_user_status(c, u, CUS_OP)) {
+ notice_lang(s_ChanServ, u, CHAN_MUST_BE_CHANOP);
+
+ } else if (!is_servadmin && nc->channelmax > 0
+ && nc->channelcount >= nc->channelmax) {
+ notice_lang(s_ChanServ, u,
+ nc->channelcount >
+ nc->
+ channelmax ? CHAN_EXCEEDED_CHANNEL_LIMIT :
+ CHAN_REACHED_CHANNEL_LIMIT, nc->channelmax);
+
+ } else if (!(ci = makechan(chan))) {
+ alog("%s: makechan() failed for REGISTER %s", s_ChanServ, chan);
+ notice_lang(s_ChanServ, u, CHAN_REGISTRATION_FAILED);
+
+#ifdef USE_ENCRYPTION
+ } else if (strscpy(founderpass, pass, PASSMAX + 1),
+ encrypt_in_place(founderpass, PASSMAX) < 0) {
+ alog("%s: Couldn't encrypt password for %s (REGISTER)",
+ s_ChanServ, chan);
+ notice_lang(s_ChanServ, u, CHAN_REGISTRATION_FAILED);
+ delchan(ci);
+#endif
+
+ } else {
+ c->ci = ci;
+ ci->c = c;
+ ci->bantype = CSDefBantype;
+ ci->flags = CSDefFlags;
+#ifdef IRC_HYBRID
+ ci->mlock_on = CMODE_n | CMODE_t;
+#else
+ ci->mlock_on = CMODE_n | CMODE_t | CMODE_r;
+#endif
+ ci->memos.memomax = MSMaxMemos;
+ ci->last_used = ci->time_registered;
+ ci->founder = nc;
+#ifdef USE_ENCRYPTION
+ if (strlen(pass) > PASSMAX)
+ notice_lang(s_ChanServ, u, PASSWORD_TRUNCATED, PASSMAX);
+ memset(pass, 0, strlen(pass));
+ memcpy(ci->founderpass, founderpass, PASSMAX);
+ ci->flags |= CI_ENCRYPTEDPW;
+#else
+ if (strlen(pass) > PASSMAX - 1) /* -1 for null byte */
+ notice_lang(s_ChanServ, u, PASSWORD_TRUNCATED, PASSMAX - 1);
+ strscpy(ci->founderpass, pass, PASSMAX);
+#endif
+ ci->desc = sstrdup(desc);
+ if (c->topic) {
+ ci->last_topic = sstrdup(c->topic);
+ strscpy(ci->last_topic_setter, c->topic_setter, NICKMAX);
+ ci->last_topic_time = c->topic_time;
+ }
+ ci->bi = NULL;
+ ci->botflags = BSDefFlags;
+ ci->founder->channelcount++;
+ alog("%s: Channel '%s' registered by %s!%s@%s", s_ChanServ, chan,
+ u->nick, u->username, GetHost(u));
+ notice_lang(s_ChanServ, u, CHAN_REGISTERED, chan, u->nick);
+#ifndef USE_ENCRYPTION
+ notice_lang(s_ChanServ, u, CHAN_PASSWORD_IS, ci->founderpass);
+#endif
+ uc = scalloc(sizeof(*uc), 1);
+ uc->next = u->founder_chans;
+ uc->prev = NULL;
+ if (u->founder_chans)
+ u->founder_chans->prev = uc;
+ u->founder_chans = uc;
+ uc->chan = ci;
+ /* Implement new mode lock */
+ check_modes(c);
+#ifdef IRC_ULTIMATE3
+ send_cmd(s_ChanServ, "MODE %s +a %s", chan, u->nick);
+#endif
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_identify(User * u)
+{
+ char *chan = strtok(NULL, " ");
+ char *pass = strtok(NULL, " ");
+ ChannelInfo *ci;
+ struct u_chaninfolist *uc;
+
+ if (!pass) {
+ syntax_error(s_ChanServ, u, "IDENTIFY", CHAN_IDENTIFY_SYNTAX);
+ } else if (!(ci = cs_findchan(chan))) {
+ notice_lang(s_ChanServ, u, CHAN_X_NOT_REGISTERED, chan);
+ } else if (ci->flags & CI_VERBOTEN) {
+ notice_lang(s_ChanServ, u, CHAN_X_FORBIDDEN, chan);
+ } else if (!nick_identified(u)) {
+ notice_lang(s_ChanServ, u, NICK_IDENTIFY_REQUIRED, s_NickServ);
+ } else if (is_founder(u, ci)) {
+ notice_lang(s_ChanServ, u, NICK_ALREADY_IDENTIFIED);
+ } else {
+ int res;
+
+ if ((res = check_password(pass, ci->founderpass)) == 1) {
+ if (!is_identified(u, ci)) {
+ uc = scalloc(sizeof(*uc), 1);
+ uc->next = u->founder_chans;
+ if (u->founder_chans)
+ u->founder_chans->prev = uc;
+ u->founder_chans = uc;
+ uc->chan = ci;
+ alog("%s: %s!%s@%s identified for %s", s_ChanServ, u->nick,
+ u->username, GetHost(u), ci->name);
+ }
+
+ notice_lang(s_ChanServ, u, CHAN_IDENTIFY_SUCCEEDED, chan);
+ } else if (res < 0) {
+ alog("%s: check_password failed for %s", s_ChanServ, ci->name);
+ notice_lang(s_ChanServ, u, CHAN_IDENTIFY_FAILED);
+ } else {
+ alog("%s: Failed IDENTIFY for %s by %s!%s@%s",
+ s_ChanServ, ci->name, u->nick, u->username, GetHost(u));
+ notice_lang(s_ChanServ, u, PASSWORD_INCORRECT);
+ bad_password(u);
+ }
+
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_logout(User * u)
+{
+ char *chan = strtok(NULL, " ");
+ char *nick = strtok(NULL, " ");
+ ChannelInfo *ci;
+ User *u2 = NULL;
+ int is_servadmin = is_services_admin(u);
+
+ if (!chan || (!nick && !is_servadmin)) {
+ syntax_error(s_ChanServ, u, "LOGOUT",
+ (!is_servadmin ? CHAN_LOGOUT_SYNTAX :
+ CHAN_LOGOUT_SERVADMIN_SYNTAX));
+ } else if (!(ci = cs_findchan(chan))) {
+ notice_lang(s_ChanServ, u, CHAN_X_NOT_REGISTERED, chan);
+ } else if (!is_servadmin & (ci->flags & CI_VERBOTEN)) {
+ notice_lang(s_ChanServ, u, CHAN_X_FORBIDDEN, chan);
+ } else if (nick && !(u2 = finduser(nick))) {
+ notice_lang(s_ChanServ, u, NICK_X_NOT_IN_USE, nick);
+ } else if (!is_servadmin && u2 != u && !is_real_founder(u, ci)) {
+ notice_lang(s_ChanServ, u, ACCESS_DENIED);
+ } else {
+ if (u2) {
+ make_unidentified(u2, ci);
+ notice_lang(s_ChanServ, u, CHAN_LOGOUT_SUCCEEDED, nick, chan);
+ } else {
+ int i;
+ for (i = 0; i < 1024; i++)
+ for (u2 = userlist[i]; u2; u2 = u2->next)
+ make_unidentified(u2, ci);
+ notice_lang(s_ChanServ, u, CHAN_LOGOUT_ALL_SUCCEEDED, chan);
+ }
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_drop(User * u)
+{
+ char *chan = strtok(NULL, " ");
+ ChannelInfo *ci;
+ int is_servadmin = is_services_admin(u);
+
+ if (readonly && !is_servadmin) {
+ notice_lang(s_ChanServ, u, CHAN_DROP_DISABLED);
+ return MOD_CONT;
+ }
+
+ if (!chan) {
+ syntax_error(s_ChanServ, u, "DROP", CHAN_DROP_SYNTAX);
+ } else if (!(ci = cs_findchan(chan))) {
+ notice_lang(s_ChanServ, u, CHAN_X_NOT_REGISTERED, chan);
+ } else if (!is_servadmin && (ci->flags & CI_VERBOTEN)) {
+ notice_lang(s_ChanServ, u, CHAN_X_FORBIDDEN, chan);
+ } else if (!is_servadmin && (ci->flags & CI_SUSPENDED)) {
+ notice_lang(s_ChanServ, u, CHAN_X_FORBIDDEN, chan);
+ } else if (!is_servadmin
+ && (ci->
+ flags & CI_SECUREFOUNDER ? !is_real_founder(u,
+ ci) :
+ !is_founder(u, ci))) {
+ notice_lang(s_ChanServ, u, ACCESS_DENIED);
+ } else {
+ int level = get_access(u, ci);
+
+ if (readonly) /* in this case we know they're a Services admin */
+ notice_lang(s_ChanServ, u, READ_ONLY_MODE);
+#ifndef IRC_HYBRID
+ if (ci->c) {
+ ci->c->mode &= ~CMODE_r;
+ send_cmd(whosends(ci), "MODE %s -r", ci->name);
+ }
+#endif
+ alog("%s: Channel %s dropped by %s!%s@%s (founder: %s)",
+ s_ChanServ, ci->name, u->nick, u->username, GetHost(u),
+ (ci->founder ? ci->founder->display : "(none)"));
+
+ delchan(ci);
+
+ /* We must make sure that the Services admin has not normally the right to
+ * drop the channel before issuing the wallops.
+ */
+ if (WallDrop && is_servadmin && level < ACCESS_FOUNDER)
+ wallops(s_ChanServ, "\2%s\2 used DROP on channel \2%s\2",
+ u->nick, chan);
+
+ notice_lang(s_ChanServ, u, CHAN_DROPPED, chan);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+/* Main SET routine. Calls other routines as follows:
+ * do_set_command(User *command_sender, ChannelInfo *ci, char *param);
+ * The parameter passed is the first space-delimited parameter after the
+ * option name, except in the case of DESC, TOPIC, and ENTRYMSG, in which
+ * it is the entire remainder of the line. Additional parameters beyond
+ * the first passed in the function call can be retrieved using
+ * strtok(NULL, toks).
+ */
+static int do_set(User * u)
+{
+ char *chan = strtok(NULL, " ");
+ char *cmd = strtok(NULL, " ");
+ char *param;
+ ChannelInfo *ci;
+ int is_servadmin = is_services_admin(u);
+
+ if (readonly) {
+ notice_lang(s_ChanServ, u, CHAN_SET_DISABLED);
+ return MOD_CONT;
+ }
+
+ if (cmd) {
+ if (stricmp(cmd, "DESC") == 0 || stricmp(cmd, "ENTRYMSG") == 0)
+ param = strtok(NULL, "");
+ else
+ param = strtok(NULL, " ");
+ } else {
+ param = NULL;
+ }
+
+ if (!param && (!cmd || (stricmp(cmd, "SUCCESSOR") != 0 &&
+ stricmp(cmd, "URL") != 0 &&
+ stricmp(cmd, "EMAIL") != 0 &&
+ stricmp(cmd, "ENTRYMSG") != 0))) {
+ syntax_error(s_ChanServ, u, "SET", CHAN_SET_SYNTAX);
+ } else if (!(ci = cs_findchan(chan))) {
+ notice_lang(s_ChanServ, u, CHAN_X_NOT_REGISTERED, chan);
+ } else if (ci->flags & CI_VERBOTEN) {
+ notice_lang(s_ChanServ, u, CHAN_X_FORBIDDEN, chan);
+ } else if (!is_servadmin && !check_access(u, ci, CA_SET)) {
+ notice_lang(s_ChanServ, u, ACCESS_DENIED);
+ } else if (stricmp(cmd, "FOUNDER") == 0) {
+ if (!is_servadmin
+ && (ci->
+ flags & CI_SECUREFOUNDER ? !is_real_founder(u,
+ ci) :
+ !is_founder(u, ci))) {
+ notice_lang(s_ChanServ, u, ACCESS_DENIED);
+ } else {
+ do_set_founder(u, ci, param);
+ }
+ } else if (stricmp(cmd, "SUCCESSOR") == 0) {
+ if (!is_servadmin
+ && (ci->
+ flags & CI_SECUREFOUNDER ? !is_real_founder(u,
+ ci) :
+ !is_founder(u, ci))) {
+ notice_lang(s_ChanServ, u, ACCESS_DENIED);
+ } else {
+ do_set_successor(u, ci, param);
+ }
+ } else if (stricmp(cmd, "PASSWORD") == 0) {
+ if (!is_servadmin
+ && (ci->
+ flags & CI_SECUREFOUNDER ? !is_real_founder(u,
+ ci) :
+ !is_founder(u, ci))) {
+ notice_lang(s_ChanServ, u, ACCESS_DENIED);
+ } else {
+ do_set_password(u, ci, param);
+ }
+ } else if (stricmp(cmd, "DESC") == 0) {
+ do_set_desc(u, ci, param);
+ } else if (stricmp(cmd, "URL") == 0) {
+ do_set_url(u, ci, param);
+ } else if (stricmp(cmd, "EMAIL") == 0) {
+ do_set_email(u, ci, param);
+ } else if (stricmp(cmd, "ENTRYMSG") == 0) {
+ do_set_entrymsg(u, ci, param);
+ } else if (stricmp(cmd, "TOPIC") == 0) {
+ notice_lang(s_ChanServ, u, OBSOLETE_COMMAND, "TOPIC");
+ } else if (stricmp(cmd, "BANTYPE") == 0) {
+ do_set_bantype(u, ci, param);
+ } else if (stricmp(cmd, "MLOCK") == 0) {
+ do_set_mlock(u, ci, param);
+ } else if (stricmp(cmd, "KEEPTOPIC") == 0) {
+ do_set_keeptopic(u, ci, param);
+ } else if (stricmp(cmd, "TOPICLOCK") == 0) {
+ do_set_topiclock(u, ci, param);
+ } else if (stricmp(cmd, "PRIVATE") == 0) {
+ do_set_private(u, ci, param);
+ } else if (stricmp(cmd, "SECUREOPS") == 0) {
+ do_set_secureops(u, ci, param);
+ } else if (stricmp(cmd, "SECUREFOUNDER") == 0) {
+ if (!is_servadmin
+ && (ci->
+ flags & CI_SECUREFOUNDER ? !is_real_founder(u,
+ ci) :
+ !is_founder(u, ci))) {
+ notice_lang(s_ChanServ, u, ACCESS_DENIED);
+ } else {
+ do_set_securefounder(u, ci, param);
+ }
+ } else if (stricmp(cmd, "RESTRICTED") == 0) {
+ do_set_restricted(u, ci, param);
+ } else if (stricmp(cmd, "SECURE") == 0) {
+ do_set_secure(u, ci, param);
+ } else if (stricmp(cmd, "SIGNKICK") == 0) {
+ do_set_signkick(u, ci, param);
+ } else if (stricmp(cmd, "OPNOTICE") == 0) {
+ do_set_opnotice(u, ci, param);
+ } else if (stricmp(cmd, "XOP") == 0) {
+ do_set_xop(u, ci, param);
+ } else if (stricmp(cmd, "PEACE") == 0) {
+ do_set_peace(u, ci, param);
+ } else if (stricmp(cmd, "NOEXPIRE") == 0) {
+ do_set_noexpire(u, ci, param);
+ } else {
+ notice_lang(s_ChanServ, u, CHAN_SET_UNKNOWN_OPTION, cmd);
+ notice_lang(s_ChanServ, u, MORE_INFO, s_ChanServ, "SET");
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_set_founder(User * u, ChannelInfo * ci, char *param)
+{
+ NickAlias *na = findnick(param);
+ NickCore *nc, *nc0 = ci->founder;
+
+ if (!na) {
+ notice_lang(s_ChanServ, u, NICK_X_NOT_REGISTERED, param);
+ return MOD_CONT;
+ } else if (na->status & NS_VERBOTEN) {
+ notice_lang(s_ChanServ, u, NICK_X_FORBIDDEN, param);
+ return MOD_CONT;
+ }
+
+ nc = na->nc;
+ if (nc->channelmax > 0 && nc->channelcount >= nc->channelmax
+ && !is_services_admin(u)) {
+ notice_lang(s_ChanServ, u, CHAN_SET_FOUNDER_TOO_MANY_CHANS, param);
+ return MOD_CONT;
+ }
+
+ alog("%s: Changing founder of %s from %s to %s by %s!%s@%s",
+ s_ChanServ, ci->name, ci->founder->display, nc->display, u->nick,
+ u->username, GetHost(u));
+
+ /* Founder and successor must not be the same group */
+ if (nc == ci->successor)
+ ci->successor = NULL;
+
+ nc0->channelcount--;
+ ci->founder = nc;
+ nc->channelcount++;
+
+ notice_lang(s_ChanServ, u, CHAN_FOUNDER_CHANGED, ci->name, param);
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_set_successor(User * u, ChannelInfo * ci, char *param)
+{
+ NickAlias *na;
+ NickCore *nc;
+
+ if (param) {
+ na = findnick(param);
+
+ if (!na) {
+ notice_lang(s_ChanServ, u, NICK_X_NOT_REGISTERED, param);
+ return MOD_CONT;
+ }
+ if (na->status & NS_VERBOTEN) {
+ notice_lang(s_ChanServ, u, NICK_X_FORBIDDEN, param);
+ return MOD_CONT;
+ }
+ if (na->nc == ci->founder) {
+ notice_lang(s_ChanServ, u, CHAN_SUCCESSOR_IS_FOUNDER, param,
+ ci->name);
+ return MOD_CONT;
+ }
+ nc = na->nc;
+
+ } else {
+ nc = NULL;
+ }
+
+ alog("%s: Changing successor of %s from %s to %s by %s!%s@%s",
+ s_ChanServ, ci->name,
+ (ci->successor ? ci->successor->display : "none"),
+ (nc ? nc->display : "none"), u->nick, u->username, GetHost(u));
+
+ ci->successor = nc;
+
+ if (nc)
+ notice_lang(s_ChanServ, u, CHAN_SUCCESSOR_CHANGED, ci->name,
+ param);
+ else
+ notice_lang(s_ChanServ, u, CHAN_SUCCESSOR_UNSET, ci->name);
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_set_password(User * u, ChannelInfo * ci, char *param)
+{
+#ifdef USE_ENCRYPTION
+ int len = strlen(param);
+
+ if (len > PASSMAX) {
+ len = PASSMAX;
+ param[len] = 0;
+ notice_lang(s_ChanServ, u, PASSWORD_TRUNCATED, PASSMAX);
+ }
+
+ if (encrypt(param, len, ci->founderpass, PASSMAX) < 0) {
+ memset(param, 0, strlen(param));
+ alog("%s: Failed to encrypt password for %s (set)", s_ChanServ,
+ ci->name);
+ notice_lang(s_ChanServ, u, CHAN_SET_PASSWORD_FAILED);
+ return MOD_CONT;
+ }
+
+ memset(param, 0, strlen(param));
+ notice_lang(s_ChanServ, u, CHAN_PASSWORD_CHANGED, ci->name);
+
+#else /* !USE_ENCRYPTION */
+ if (strlen(param) > PASSMAX - 1) /* -1 for null byte */
+ notice_lang(s_ChanServ, u, PASSWORD_TRUNCATED, PASSMAX - 1);
+ strscpy(ci->founderpass, param, PASSMAX);
+ notice_lang(s_ChanServ, u, CHAN_PASSWORD_CHANGED_TO, ci->name,
+ ci->founderpass);
+#endif /* USE_ENCRYPTION */
+
+ if (get_access(u, ci) < ACCESS_FOUNDER) {
+ alog("%s: %s!%s@%s set password as Services admin for %s",
+ s_ChanServ, u->nick, u->username, GetHost(u), ci->name);
+ if (WallSetpass)
+ wallops(s_ChanServ,
+ "\2%s\2 set password as Services admin for channel \2%s\2",
+ u->nick, ci->name);
+ } else {
+ alog("%s: %s!%s@%s changed password of %s (founder: %s)",
+ s_ChanServ, u->nick, u->username, GetHost(u),
+ ci->name, ci->founder->display);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_set_desc(User * u, ChannelInfo * ci, char *param)
+{
+ if (ci->desc)
+ free(ci->desc);
+ ci->desc = sstrdup(param);
+ notice_lang(s_ChanServ, u, CHAN_DESC_CHANGED, ci->name, param);
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_set_url(User * u, ChannelInfo * ci, char *param)
+{
+ if (ci->url)
+ free(ci->url);
+ if (param) {
+ ci->url = sstrdup(param);
+ notice_lang(s_ChanServ, u, CHAN_URL_CHANGED, ci->name, param);
+ } else {
+ ci->url = NULL;
+ notice_lang(s_ChanServ, u, CHAN_URL_UNSET, ci->name);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_set_email(User * u, ChannelInfo * ci, char *param)
+{
+ if (ci->email)
+ free(ci->email);
+ if (param) {
+ ci->email = sstrdup(param);
+ notice_lang(s_ChanServ, u, CHAN_EMAIL_CHANGED, ci->name, param);
+ } else {
+ ci->email = NULL;
+ notice_lang(s_ChanServ, u, CHAN_EMAIL_UNSET, ci->name);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_set_entrymsg(User * u, ChannelInfo * ci, char *param)
+{
+ if (ci->entry_message)
+ free(ci->entry_message);
+ if (param) {
+ ci->entry_message = sstrdup(param);
+ notice_lang(s_ChanServ, u, CHAN_ENTRY_MSG_CHANGED, ci->name,
+ param);
+ } else {
+ ci->entry_message = NULL;
+ notice_lang(s_ChanServ, u, CHAN_ENTRY_MSG_UNSET, ci->name);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_set_mlock(User * u, ChannelInfo * ci, char *param)
+{
+ int add = -1; /* 1 if adding, 0 if deleting, -1 if neither */
+ unsigned char mode;
+ CBMode *cbm;
+
+ if (checkDefCon(DEFCON_NO_MLOCK_CHANGE)) {
+ notice_lang(s_ChanServ, u, OPER_DEFCON_DENIED);
+ return MOD_CONT;
+ }
+
+ /* Reinitialize everything */
+#ifdef IRC_HYBRID
+ ci->mlock_on = 0;
+#else
+ ci->mlock_on = CMODE_r;
+#endif
+ ci->mlock_off = ci->mlock_limit = 0;
+ ci->mlock_key = NULL;
+#ifdef HAS_FMODE
+ ci->mlock_flood = NULL;
+#endif
+#ifdef HAS_LMODE
+ ci->mlock_redirect = NULL;
+#endif
+
+ while ((mode = *param++)) {
+ switch (mode) {
+ case '+':
+ add = 1;
+ continue;
+ case '-':
+ add = 0;
+ continue;
+ default:
+ if (add < 0)
+ continue;
+ }
+
+ if ((int) mode < 128 && (cbm = &cbmodes[(int) mode])->flag != 0) {
+ if ((cbm->flags & CBM_NO_MLOCK)
+ || ((cbm->flags & CBM_NO_USER_MLOCK) && !is_oper(u))) {
+ notice_lang(s_ChanServ, u, CHAN_SET_MLOCK_IMPOSSIBLE_CHAR,
+ mode);
+ } else if (add) {
+ ci->mlock_on |= cbm->flag;
+ ci->mlock_off &= ~cbm->flag;
+ if (cbm->cssetvalue)
+ cbm->cssetvalue(ci, strtok(NULL, " "));
+ } else {
+ ci->mlock_off |= cbm->flag;
+ if (ci->mlock_on & cbm->flag) {
+ ci->mlock_on &= ~cbm->flag;
+ if (cbm->cssetvalue)
+ cbm->cssetvalue(ci, NULL);
+ }
+ }
+ } else {
+ notice_lang(s_ChanServ, u, CHAN_SET_MLOCK_UNKNOWN_CHAR, mode);
+ }
+ } /* while (*param) */
+
+#ifdef HAS_LMODE
+ /* We can't mlock +L if +l is not mlocked as well. */
+ if ((ci->mlock_on & CMODE_L) && !(ci->mlock_on & CMODE_l)) {
+ ci->mlock_on &= ~CMODE_L;
+ free(ci->mlock_redirect);
+ notice_lang(s_ChanServ, u, CHAN_SET_MLOCK_L_REQUIRED);
+ }
+#endif
+
+#if defined(IRC_ULTIMATE) || defined(IRC_UNREAL) || defined(IRC_ULTIMATE3)
+ /* We can't mlock +K if +i is not mlocked as well. */
+ if ((ci->mlock_on & CMODE_K) && !(ci->mlock_on & CMODE_i)) {
+ ci->mlock_on &= ~CMODE_K;
+ notice_lang(s_ChanServ, u, CHAN_SET_MLOCK_K_REQUIRED);
+ }
+#endif
+
+ /* Since we always enforce mode r there is no way to have no
+ * mode lock at all.
+ */
+#if defined(IRC_HYBRID)
+ /* James: Hybrid doesn't HAVE mode r, so now you have to check :P */
+ if (get_mlock_modes(ci, 0))
+#endif
+ notice_lang(s_ChanServ, u, CHAN_MLOCK_CHANGED, ci->name,
+ get_mlock_modes(ci, 0));
+
+
+ /* Implement the new lock. */
+ if (ci->c)
+ check_modes(ci->c);
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_set_bantype(User * u, ChannelInfo * ci, char *param)
+{
+ char *endptr;
+
+ int16 bantype = strtol(param, &endptr, 10);
+
+ if (*endptr != 0 || bantype < 0 || bantype > 3) {
+ notice_lang(s_ChanServ, u, CHAN_SET_BANTYPE_INVALID, param);
+ } else {
+ ci->bantype = bantype;
+ notice_lang(s_ChanServ, u, CHAN_SET_BANTYPE_CHANGED, ci->name,
+ ci->bantype);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_set_keeptopic(User * u, ChannelInfo * ci, char *param)
+{
+ if (stricmp(param, "ON") == 0) {
+ ci->flags |= CI_KEEPTOPIC;
+ notice_lang(s_ChanServ, u, CHAN_SET_KEEPTOPIC_ON);
+ } else if (stricmp(param, "OFF") == 0) {
+ ci->flags &= ~CI_KEEPTOPIC;
+ notice_lang(s_ChanServ, u, CHAN_SET_KEEPTOPIC_OFF);
+ } else {
+ syntax_error(s_ChanServ, u, "SET KEEPTOPIC",
+ CHAN_SET_KEEPTOPIC_SYNTAX);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_set_topiclock(User * u, ChannelInfo * ci, char *param)
+{
+ if (stricmp(param, "ON") == 0) {
+ ci->flags |= CI_TOPICLOCK;
+ notice_lang(s_ChanServ, u, CHAN_SET_TOPICLOCK_ON);
+ } else if (stricmp(param, "OFF") == 0) {
+ ci->flags &= ~CI_TOPICLOCK;
+ notice_lang(s_ChanServ, u, CHAN_SET_TOPICLOCK_OFF);
+ } else {
+ syntax_error(s_ChanServ, u, "SET TOPICLOCK",
+ CHAN_SET_TOPICLOCK_SYNTAX);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_set_private(User * u, ChannelInfo * ci, char *param)
+{
+ if (stricmp(param, "ON") == 0) {
+ ci->flags |= CI_PRIVATE;
+ notice_lang(s_ChanServ, u, CHAN_SET_PRIVATE_ON);
+ } else if (stricmp(param, "OFF") == 0) {
+ ci->flags &= ~CI_PRIVATE;
+ notice_lang(s_ChanServ, u, CHAN_SET_PRIVATE_OFF);
+ } else {
+ syntax_error(s_ChanServ, u, "SET PRIVATE",
+ CHAN_SET_PRIVATE_SYNTAX);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_set_secureops(User * u, ChannelInfo * ci, char *param)
+{
+ if (stricmp(param, "ON") == 0) {
+ ci->flags |= CI_SECUREOPS;
+ notice_lang(s_ChanServ, u, CHAN_SET_SECUREOPS_ON);
+ } else if (stricmp(param, "OFF") == 0) {
+ ci->flags &= ~CI_SECUREOPS;
+ notice_lang(s_ChanServ, u, CHAN_SET_SECUREOPS_OFF);
+ } else {
+ syntax_error(s_ChanServ, u, "SET SECUREOPS",
+ CHAN_SET_SECUREOPS_SYNTAX);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_set_securefounder(User * u, ChannelInfo * ci, char *param)
+{
+ if (stricmp(param, "ON") == 0) {
+ ci->flags |= CI_SECUREFOUNDER;
+ notice_lang(s_ChanServ, u, CHAN_SET_SECUREFOUNDER_ON);
+ } else if (stricmp(param, "OFF") == 0) {
+ ci->flags &= ~CI_SECUREFOUNDER;
+ notice_lang(s_ChanServ, u, CHAN_SET_SECUREFOUNDER_OFF);
+ } else {
+ syntax_error(s_ChanServ, u, "SET SECUREFOUNDER",
+ CHAN_SET_SECUREFOUNDER_SYNTAX);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_set_restricted(User * u, ChannelInfo * ci, char *param)
+{
+ if (stricmp(param, "ON") == 0) {
+ ci->flags |= CI_RESTRICTED;
+ if (ci->levels[CA_NOJOIN] < 0)
+ ci->levels[CA_NOJOIN] = 0;
+ notice_lang(s_ChanServ, u, CHAN_SET_RESTRICTED_ON);
+ } else if (stricmp(param, "OFF") == 0) {
+ ci->flags &= ~CI_RESTRICTED;
+ if (ci->levels[CA_NOJOIN] >= 0)
+ ci->levels[CA_NOJOIN] = -2;
+ notice_lang(s_ChanServ, u, CHAN_SET_RESTRICTED_OFF);
+ } else {
+ syntax_error(s_ChanServ, u, "SET RESTRICTED",
+ CHAN_SET_RESTRICTED_SYNTAX);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_set_secure(User * u, ChannelInfo * ci, char *param)
+{
+ if (stricmp(param, "ON") == 0) {
+ ci->flags |= CI_SECURE;
+ notice_lang(s_ChanServ, u, CHAN_SET_SECURE_ON);
+ } else if (stricmp(param, "OFF") == 0) {
+ ci->flags &= ~CI_SECURE;
+ notice_lang(s_ChanServ, u, CHAN_SET_SECURE_OFF);
+ } else {
+ syntax_error(s_ChanServ, u, "SET SECURE", CHAN_SET_SECURE_SYNTAX);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_set_signkick(User * u, ChannelInfo * ci, char *param)
+{
+ if (stricmp(param, "ON") == 0) {
+ ci->flags |= CI_SIGNKICK;
+ ci->flags &= ~CI_SIGNKICK_LEVEL;
+ notice_lang(s_ChanServ, u, CHAN_SET_SIGNKICK_ON);
+ } else if (stricmp(param, "LEVEL") == 0) {
+ ci->flags |= CI_SIGNKICK_LEVEL;
+ ci->flags &= ~CI_SIGNKICK;
+ notice_lang(s_ChanServ, u, CHAN_SET_SIGNKICK_LEVEL);
+ } else if (stricmp(param, "OFF") == 0) {
+ ci->flags &= ~(CI_SIGNKICK | CI_SIGNKICK_LEVEL);
+ notice_lang(s_ChanServ, u, CHAN_SET_SIGNKICK_OFF);
+ } else {
+ syntax_error(s_ChanServ, u, "SET SIGNKICK",
+ CHAN_SET_SIGNKICK_SYNTAX);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_set_opnotice(User * u, ChannelInfo * ci, char *param)
+{
+ if (stricmp(param, "ON") == 0) {
+ ci->flags |= CI_OPNOTICE;
+ notice_lang(s_ChanServ, u, CHAN_SET_OPNOTICE_ON);
+ } else if (stricmp(param, "OFF") == 0) {
+ ci->flags &= ~CI_OPNOTICE;
+ notice_lang(s_ChanServ, u, CHAN_SET_OPNOTICE_OFF);
+ } else {
+ syntax_error(s_ChanServ, u, "SET OPNOTICE",
+ CHAN_SET_OPNOTICE_SYNTAX);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+#define CHECKLEV(lev) ((ci->levels[(lev)] != ACCESS_INVALID) && (access->level >= ci->levels[(lev)]))
+
+static int do_set_xop(User * u, ChannelInfo * ci, char *param)
+{
+ if (stricmp(param, "ON") == 0) {
+ if (!(ci->flags & CI_XOP)) {
+ int i;
+ ChanAccess *access;
+
+ for (access = ci->access, i = 0; i < ci->accesscount;
+ access++, i++) {
+ if (!access->in_use)
+ continue;
+ /* This will probably cause wrong levels to be set, but hey,
+ * it's better than losing it altogether.
+ */
+ if (CHECKLEV(CA_AKICK) || CHECKLEV(CA_SET)) {
+ access->level = ACCESS_SOP;
+ } else if (CHECKLEV(CA_AUTOOP) || CHECKLEV(CA_OPDEOP)
+ || CHECKLEV(CA_OPDEOPME)) {
+ access->level = ACCESS_AOP;
+#ifdef HAS_HALFOP
+ } else if (CHECKLEV(CA_AUTOHALFOP) || CHECKLEV(CA_HALFOP)
+ || CHECKLEV(CA_HALFOPME)) {
+ access->level = ACCESS_HOP;
+#endif
+ } else if (CHECKLEV(CA_AUTOVOICE) || CHECKLEV(CA_VOICE)
+ || CHECKLEV(CA_VOICEME)) {
+ access->level = ACCESS_VOP;
+ } else {
+ access->in_use = 0;
+ access->nc = NULL;
+ }
+ }
+
+ reset_levels(ci);
+ ci->flags |= CI_XOP;
+ }
+
+ alog("%s: %s!%s@%s enabled XOP for %s", s_ChanServ, u->nick,
+ u->username, GetHost(u), ci->name);
+ notice_lang(s_ChanServ, u, CHAN_SET_XOP_ON);
+ } else if (stricmp(param, "OFF") == 0) {
+ ci->flags &= ~CI_XOP;
+
+ alog("%s: %s!%s@%s disabled XOP for %s", s_ChanServ, u->nick,
+ u->username, GetHost(u), ci->name);
+ notice_lang(s_ChanServ, u, CHAN_SET_XOP_OFF);
+ } else {
+ syntax_error(s_ChanServ, u, "SET XOP", CHAN_SET_XOP_SYNTAX);
+ }
+ return MOD_CONT;
+}
+
+#undef CHECKLEV
+
+/*************************************************************************/
+
+static int do_set_peace(User * u, ChannelInfo * ci, char *param)
+{
+ if (stricmp(param, "ON") == 0) {
+ ci->flags |= CI_PEACE;
+ notice_lang(s_ChanServ, u, CHAN_SET_PEACE_ON);
+ } else if (stricmp(param, "OFF") == 0) {
+ ci->flags &= ~CI_PEACE;
+ notice_lang(s_ChanServ, u, CHAN_SET_PEACE_OFF);
+ } else {
+ syntax_error(s_ChanServ, u, "SET PEACE", CHAN_SET_PEACE_SYNTAX);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_set_noexpire(User * u, ChannelInfo * ci, char *param)
+{
+ if (!is_services_admin(u)) {
+ notice_lang(s_ChanServ, u, PERMISSION_DENIED);
+ return MOD_CONT;
+ }
+ if (stricmp(param, "ON") == 0) {
+ ci->flags |= CI_NO_EXPIRE;
+ notice_lang(s_ChanServ, u, CHAN_SET_NOEXPIRE_ON, ci->name);
+ } else if (stricmp(param, "OFF") == 0) {
+ ci->flags &= ~CI_NO_EXPIRE;
+ notice_lang(s_ChanServ, u, CHAN_SET_NOEXPIRE_OFF, ci->name);
+ } else {
+ syntax_error(s_ChanServ, u, "SET NOEXPIRE",
+ CHAN_SET_NOEXPIRE_SYNTAX);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+/* `last' is set to the last index this routine was called with
+ * `perm' is incremented whenever a permission-denied error occurs
+ */
+
+static int xop_del(User * u, ChanAccess * access, int *perm, int uacc,
+ int xlev)
+{
+ if (!access->in_use || access->level != xlev)
+ return 0;
+ if (!is_services_admin(u) && uacc <= access->level) {
+ (*perm)++;
+ return 0;
+ }
+ access->nc = NULL;
+ access->in_use = 0;
+ return 1;
+}
+
+static int xop_del_callback(User * u, int num, va_list args)
+{
+ ChannelInfo *ci = va_arg(args, ChannelInfo *);
+ int *last = va_arg(args, int *);
+ int *perm = va_arg(args, int *);
+ int uacc = va_arg(args, int);
+ int xlev = va_arg(args, int);
+
+ if (num < 1 || num > ci->accesscount)
+ return 0;
+ *last = num;
+
+ return xop_del(u, &ci->access[num - 1], perm, uacc, xlev);
+}
+
+
+static int xop_list(User * u, int index, ChannelInfo * ci,
+ int *sent_header, int xlev, int xmsg)
+{
+ ChanAccess *access = &ci->access[index];
+
+ if (!access->in_use || access->level != xlev)
+ return 0;
+
+ if (!*sent_header) {
+ notice_lang(s_ChanServ, u, xmsg, ci->name);
+ *sent_header = 1;
+ }
+
+ notice_lang(s_ChanServ, u, CHAN_XOP_LIST_FORMAT, index + 1,
+ access->nc->display);
+ return 1;
+}
+
+static int xop_list_callback(User * u, int num, va_list args)
+{
+ ChannelInfo *ci = va_arg(args, ChannelInfo *);
+ int *sent_header = va_arg(args, int *);
+ int xlev = va_arg(args, int);
+ int xmsg = va_arg(args, int);
+
+ if (num < 1 || num > ci->accesscount)
+ return 0;
+
+ return xop_list(u, num - 1, ci, sent_header, xlev, xmsg);
+}
+
+
+static int do_xop(User * u, char *xname, int xlev, int *xmsgs)
+{
+ char *chan = strtok(NULL, " ");
+ char *cmd = strtok(NULL, " ");
+ char *nick = strtok(NULL, " ");
+
+ ChannelInfo *ci;
+ NickAlias *na;
+ NickCore *nc;
+
+ int i;
+ int change = 0;
+ short ulev;
+ int is_list = (cmd && stricmp(cmd, "LIST") == 0);
+ int is_servadmin = is_services_admin(u);
+ ChanAccess *access;
+
+ /* If CLEAR, we don't need any parameters.
+ * If LIST, we don't *require* any parameters, but we can take any.
+ * If DEL or ADD we require a nick. */
+ if (!cmd || ((is_list || !stricmp(cmd, "CLEAR")) ? 0 : !nick)) {
+ syntax_error(s_ChanServ, u, xname, xmsgs[0]);
+ } else if (!(ci = cs_findchan(chan))) {
+ notice_lang(s_ChanServ, u, CHAN_X_NOT_REGISTERED, chan);
+ } else if (ci->flags & CI_VERBOTEN) {
+ notice_lang(s_ChanServ, u, CHAN_X_FORBIDDEN, chan);
+ } else if (!(ci->flags & CI_XOP)) {
+ notice_lang(s_ChanServ, u, CHAN_XOP_ACCESS, s_ChanServ);
+ } else if (stricmp(cmd, "ADD") == 0) {
+ if (readonly) {
+ notice_lang(s_ChanServ, u, xmsgs[1]);
+ return MOD_CONT;
+ }
+
+ ulev = get_access(u, ci);
+
+ if (!is_servadmin && (xlev >= ulev || ulev < ACCESS_AOP)) {
+ notice_lang(s_ChanServ, u, PERMISSION_DENIED);
+ return MOD_CONT;
+ }
+
+ na = findnick(nick);
+ if (!na) {
+ notice_lang(s_ChanServ, u, xmsgs[2]);
+ return MOD_CONT;
+ } else if (na->status & NS_VERBOTEN) {
+ notice_lang(s_ChanServ, u, NICK_X_FORBIDDEN, na->nick);
+ return MOD_CONT;
+ }
+
+ nc = na->nc;
+ for (access = ci->access, i = 0; i < ci->accesscount;
+ access++, i++) {
+ if (access->nc == nc) {
+ /**
+ * Patch provided by PopCorn to prevert AOP's reducing SOP's levels
+ **/
+ if ((access->level >= ulev) && (!u->isSuperAdmin)) {
+ notice_lang(s_ChanServ, u, PERMISSION_DENIED);
+ return MOD_CONT;
+ }
+ change++;
+ break;
+ }
+ }
+
+ if (!change) {
+ for (i = 0; i < ci->accesscount; i++)
+ if (!ci->access[i].in_use)
+ break;
+
+ if (i == ci->accesscount) {
+ if (i < CSAccessMax) {
+ ci->accesscount++;
+ ci->access =
+ srealloc(ci->access,
+ sizeof(ChanAccess) * ci->accesscount);
+ } else {
+ notice_lang(s_ChanServ, u, CHAN_XOP_REACHED_LIMIT,
+ CSAccessMax);
+ return MOD_CONT;
+ }
+ }
+
+ access = &ci->access[i];
+ access->nc = nc;
+ }
+
+ access->in_use = 1;
+ access->level = xlev;
+ access->last_seen = 0;
+
+ alog("%s: %s!%s@%s (level %d) %s access level %d to %s (group %s) on channel %s", s_ChanServ, u->nick, u->username, GetHost(u), ulev, change ? "changed" : "set", access->level, na->nick, nc->display, ci->name);
+
+ if (!change) {
+ notice_lang(s_ChanServ, u, xmsgs[3], access->nc->display,
+ ci->name);
+ } else {
+ notice_lang(s_ChanServ, u, xmsgs[4], access->nc->display,
+ ci->name);
+ }
+
+ } else if (stricmp(cmd, "DEL") == 0) {
+ if (readonly) {
+ notice_lang(s_ChanServ, u, xmsgs[1]);
+ return MOD_CONT;
+ }
+
+ if (ci->accesscount == 0) {
+ notice_lang(s_ChanServ, u, xmsgs[11], chan);
+ return MOD_CONT;
+ }
+
+ ulev = get_access(u, ci);
+
+ /* Special case: is it a number/list? Only do search if it isn't. */
+ if (isdigit(*nick) && strspn(nick, "1234567890,-") == strlen(nick)) {
+ int count, deleted, last = -1, perm = 0;
+ deleted =
+ process_numlist(nick, &count, xop_del_callback, u, ci,
+ &last, &perm, ulev, xlev);
+ if (!deleted) {
+ if (perm) {
+ notice_lang(s_ChanServ, u, PERMISSION_DENIED);
+ } else if (count == 1) {
+ notice_lang(s_ChanServ, u, xmsgs[5], last, ci->name);
+ } else {
+ notice_lang(s_ChanServ, u, xmsgs[7], ci->name);
+ }
+ } else if (deleted == 1) {
+ notice_lang(s_ChanServ, u, xmsgs[9], ci->name);
+ } else {
+ notice_lang(s_ChanServ, u, xmsgs[10], deleted, ci->name);
+ }
+ } else {
+ na = findnick(nick);
+ if (!na) {
+ notice_lang(s_ChanServ, u, NICK_X_NOT_REGISTERED, nick);
+ return MOD_CONT;
+ }
+ nc = na->nc;
+
+ for (i = 0; i < ci->accesscount; i++)
+ if (ci->access[i].nc == nc && ci->access[i].level == xlev)
+ break;
+
+ if (i == ci->accesscount) {
+ notice_lang(s_ChanServ, u, xmsgs[6], nick, chan);
+ return MOD_CONT;
+ }
+
+ access = &ci->access[i];
+ if (!is_servadmin && ulev <= access->level) {
+ notice_lang(s_ChanServ, u, PERMISSION_DENIED);
+ } else {
+ notice_lang(s_ChanServ, u, xmsgs[8], access->nc->display,
+ ci->name);
+ access->nc = NULL;
+ access->in_use = 0;
+ }
+ }
+ } else if (stricmp(cmd, "LIST") == 0) {
+ int sent_header = 0;
+
+ ulev = get_access(u, ci);
+
+ if (!is_servadmin && ulev < ACCESS_AOP) {
+ notice_lang(s_ChanServ, u, ACCESS_DENIED);
+ return MOD_CONT;
+ }
+
+ if (ci->accesscount == 0) {
+ notice_lang(s_ChanServ, u, xmsgs[11], ci->name);
+ return MOD_CONT;
+ }
+
+ if (nick && strspn(nick, "1234567890,-") == strlen(nick)) {
+ process_numlist(nick, NULL, xop_list_callback, u, ci,
+ &sent_header, xlev, xmsgs[12]);
+ } else {
+ for (i = 0; i < ci->accesscount; i++) {
+ if (nick && ci->access[i].nc
+ && !match_wild_nocase(nick, ci->access[i].nc->display))
+ continue;
+ xop_list(u, i, ci, &sent_header, xlev, xmsgs[12]);
+ }
+ }
+ if (!sent_header)
+ notice_lang(s_ChanServ, u, xmsgs[7], chan);
+ } else if (stricmp(cmd, "CLEAR") == 0) {
+ if (readonly) {
+ notice_lang(s_ChanServ, u, CHAN_ACCESS_DISABLED);
+ return MOD_CONT;
+ }
+
+ if (ci->accesscount == 0) {
+ notice_lang(s_ChanServ, u, CHAN_ACCESS_LIST_EMPTY, chan);
+ return MOD_CONT;
+ }
+
+ if (!is_servadmin && !is_founder(u, ci)) {
+ notice_lang(s_ChanServ, u, PERMISSION_DENIED);
+ return MOD_CONT;
+ }
+
+ for (i = 0; i < ci->accesscount; i++) {
+ if (ci->access[i].in_use && ci->access[i].level == xlev) {
+ ci->access[i].nc = NULL;
+ ci->access[i].in_use = 0;
+ }
+ }
+
+ notice_lang(s_ChanServ, u, xmsgs[13]);
+ } else {
+ syntax_error(s_ChanServ, u, xname, xmsgs[0]);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_aop(User * u)
+{
+ return do_xop(u, "AOP", ACCESS_AOP, xop_msgs[0]);
+}
+
+/*************************************************************************/
+
+#ifdef HAS_HALFOP
+
+static int do_hop(User * u)
+{
+ return do_xop(u, "HOP", ACCESS_HOP, xop_msgs[3]);
+}
+
+#endif
+
+/*************************************************************************/
+
+static int do_sop(User * u)
+{
+ return do_xop(u, "SOP", ACCESS_SOP, xop_msgs[1]);
+}
+
+/*************************************************************************/
+
+static int do_vop(User * u)
+{
+ return do_xop(u, "VOP", ACCESS_VOP, xop_msgs[2]);
+}
+
+/*************************************************************************/
+
+/* `last' is set to the last index this routine was called with
+ * `perm' is incremented whenever a permission-denied error occurs
+ */
+
+static int access_del(User * u, ChanAccess * access, int *perm, int uacc)
+{
+ if (!access->in_use)
+ return 0;
+ if (!is_services_admin(u) && uacc <= access->level) {
+ (*perm)++;
+ return 0;
+ }
+ access->nc = NULL;
+ access->in_use = 0;
+ return 1;
+}
+
+static int access_del_callback(User * u, int num, va_list args)
+{
+ ChannelInfo *ci = va_arg(args, ChannelInfo *);
+ int *last = va_arg(args, int *);
+ int *perm = va_arg(args, int *);
+ int uacc = va_arg(args, int);
+ if (num < 1 || num > ci->accesscount)
+ return 0;
+ *last = num;
+ return access_del(u, &ci->access[num - 1], perm, uacc);
+}
+
+
+static int access_list(User * u, int index, ChannelInfo * ci,
+ int *sent_header)
+{
+ ChanAccess *access = &ci->access[index];
+ char *xop;
+
+ if (!access->in_use)
+ return 0;
+
+ if (!*sent_header) {
+ notice_lang(s_ChanServ, u, CHAN_ACCESS_LIST_HEADER, ci->name);
+ *sent_header = 1;
+ }
+
+ if (ci->flags & CI_XOP) {
+ xop = get_xop_level(access->level);
+ notice_lang(s_ChanServ, u, CHAN_ACCESS_LIST_XOP_FORMAT, index + 1,
+ xop, access->nc->display);
+ } else {
+ notice_lang(s_ChanServ, u, CHAN_ACCESS_LIST_AXS_FORMAT, index + 1,
+ access->level, access->nc->display);
+ }
+ return 1;
+}
+
+static int access_list_callback(User * u, int num, va_list args)
+{
+ ChannelInfo *ci = va_arg(args, ChannelInfo *);
+ int *sent_header = va_arg(args, int *);
+ if (num < 1 || num > ci->accesscount)
+ return 0;
+ return access_list(u, num - 1, ci, sent_header);
+}
+
+
+static int do_access(User * u)
+{
+ char *chan = strtok(NULL, " ");
+ char *cmd = strtok(NULL, " ");
+ char *nick = strtok(NULL, " ");
+ char *s = strtok(NULL, " ");
+
+ ChannelInfo *ci;
+ NickAlias *na;
+ NickCore *nc;
+ ChanAccess *access;
+
+ int i;
+ short level = 0, ulev;
+ int is_list = (cmd && stricmp(cmd, "LIST") == 0);
+ int is_servadmin = is_services_admin(u);
+
+ /* 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 (!cmd || ((is_list || !stricmp(cmd, "CLEAR")) ? 0 :
+ (stricmp(cmd, "DEL") == 0) ? (!nick || s) : !s)) {
+ syntax_error(s_ChanServ, u, "ACCESS", CHAN_ACCESS_SYNTAX);
+ } else if (!(ci = cs_findchan(chan))) {
+ notice_lang(s_ChanServ, u, CHAN_X_NOT_REGISTERED, chan);
+ } else if (ci->flags & CI_VERBOTEN) {
+ notice_lang(s_ChanServ, u, CHAN_X_FORBIDDEN, chan);
+ /* We still allow LIST in xOP mode, but not others */
+ } else if ((ci->flags & CI_XOP) && !is_list) {
+ notice_lang(s_ChanServ, u, CHAN_ACCESS_XOP, s_ChanServ);
+ } else if (((is_list && !check_access(u, ci, CA_ACCESS_LIST))
+ || (!is_list && !check_access(u, ci, CA_ACCESS_CHANGE)))
+ && !is_servadmin) {
+ notice_lang(s_ChanServ, u, ACCESS_DENIED);
+ } else if (stricmp(cmd, "ADD") == 0) {
+ if (readonly) {
+ notice_lang(s_ChanServ, u, CHAN_ACCESS_DISABLED);
+ return MOD_CONT;
+ }
+
+ level = atoi(s);
+ ulev = get_access(u, ci);
+
+ if (!is_servadmin && level >= ulev) {
+ notice_lang(s_ChanServ, u, PERMISSION_DENIED);
+ return MOD_CONT;
+ }
+
+ if (level == 0) {
+ notice_lang(s_ChanServ, u, CHAN_ACCESS_LEVEL_NONZERO);
+ return MOD_CONT;
+ } else if (level <= ACCESS_INVALID || level >= ACCESS_FOUNDER) {
+ notice_lang(s_ChanServ, u, CHAN_ACCESS_LEVEL_RANGE,
+ ACCESS_INVALID + 1, ACCESS_FOUNDER - 1);
+ return MOD_CONT;
+ }
+
+ na = findnick(nick);
+ if (!na) {
+ notice_lang(s_ChanServ, u, CHAN_ACCESS_NICKS_ONLY);
+ return MOD_CONT;
+ }
+ if (na->status & NS_VERBOTEN) {
+ notice_lang(s_ChanServ, u, NICK_X_FORBIDDEN, nick);
+ return MOD_CONT;
+ }
+
+ nc = na->nc;
+ for (access = ci->access, i = 0; i < ci->accesscount;
+ access++, i++) {
+ if (access->nc == nc) {
+ /* Don't allow lowering from a level >= ulev */
+ if (!is_servadmin && access->level >= ulev) {
+ notice_lang(s_ChanServ, u, PERMISSION_DENIED);
+ return MOD_CONT;
+ }
+ if (access->level == level) {
+ notice_lang(s_ChanServ, u, CHAN_ACCESS_LEVEL_UNCHANGED,
+ access->nc->display, chan, level);
+ return MOD_CONT;
+ }
+ access->level = level;
+ alog("%s: %s!%s@%s (level %d) set access level %d to %s (group %s) on channel %s", s_ChanServ, u->nick, u->username, GetHost(u), ulev, access->level, na->nick, nc->display, ci->name);
+ notice_lang(s_ChanServ, u, CHAN_ACCESS_LEVEL_CHANGED,
+ access->nc->display, chan, level);
+ return MOD_CONT;
+ }
+ }
+
+ for (i = 0; i < ci->accesscount; i++) {
+ if (!ci->access[i].in_use)
+ break;
+ }
+ if (i == ci->accesscount) {
+ if (i < CSAccessMax) {
+ ci->accesscount++;
+ ci->access =
+ srealloc(ci->access,
+ sizeof(ChanAccess) * ci->accesscount);
+ } else {
+ notice_lang(s_ChanServ, u, CHAN_ACCESS_REACHED_LIMIT,
+ CSAccessMax);
+ return MOD_CONT;
+ }
+ }
+
+ access = &ci->access[i];
+ access->nc = nc;
+ access->in_use = 1;
+ access->level = level;
+ access->last_seen = 0;
+
+ alog("%s: %s!%s@%s (level %d) set access level %d to %s (group %s) on channel %s", s_ChanServ, u->nick, u->username, GetHost(u), ulev, access->level, na->nick, nc->display, ci->name);
+ notice_lang(s_ChanServ, u, CHAN_ACCESS_ADDED, nc->display,
+ ci->name, access->level);
+ } else if (stricmp(cmd, "DEL") == 0) {
+
+ if (readonly) {
+ notice_lang(s_ChanServ, u, CHAN_ACCESS_DISABLED);
+ return MOD_CONT;
+ }
+
+ if (ci->accesscount == 0) {
+ notice_lang(s_ChanServ, u, CHAN_ACCESS_LIST_EMPTY, chan);
+ return MOD_CONT;
+ }
+
+ /* Special case: is it a number/list? Only do search if it isn't. */
+ if (isdigit(*nick) && strspn(nick, "1234567890,-") == strlen(nick)) {
+ int count, deleted, last = -1, perm = 0;
+ deleted = process_numlist(nick, &count, access_del_callback, u,
+ ci, &last, &perm, get_access(u, ci));
+ if (!deleted) {
+ if (perm) {
+ notice_lang(s_ChanServ, u, PERMISSION_DENIED);
+ } else if (count == 1) {
+ notice_lang(s_ChanServ, u, CHAN_ACCESS_NO_SUCH_ENTRY,
+ last, ci->name);
+ } else {
+ notice_lang(s_ChanServ, u, CHAN_ACCESS_NO_MATCH,
+ ci->name);
+ }
+ } else if (deleted == 1) {
+ notice_lang(s_ChanServ, u, CHAN_ACCESS_DELETED_ONE,
+ ci->name);
+ } else {
+ notice_lang(s_ChanServ, u, CHAN_ACCESS_DELETED_SEVERAL,
+ deleted, ci->name);
+ }
+ } else {
+ na = findnick(nick);
+ if (!na) {
+ notice_lang(s_ChanServ, u, NICK_X_NOT_REGISTERED, nick);
+ return MOD_CONT;
+ }
+ nc = na->nc;
+ for (i = 0; i < ci->accesscount; i++) {
+ if (ci->access[i].nc == nc)
+ break;
+ }
+ if (i == ci->accesscount) {
+ notice_lang(s_ChanServ, u, CHAN_ACCESS_NOT_FOUND, nick,
+ chan);
+ return MOD_CONT;
+ }
+ access = &ci->access[i];
+ if (!is_servadmin && get_access(u, ci) <= access->level) {
+ notice_lang(s_ChanServ, u, PERMISSION_DENIED);
+ } else {
+ notice_lang(s_ChanServ, u, CHAN_ACCESS_DELETED,
+ access->nc->display, ci->name);
+ alog("%s: %s!%s@%s (level %d) deleted access of %s (group %s) on %s", s_ChanServ, u->nick, u->username, GetHost(u), get_access(u, ci), na->nick, access->nc->display, chan);
+ access->nc = NULL;
+ access->in_use = 0;
+ }
+ }
+ } else if (stricmp(cmd, "LIST") == 0) {
+ int sent_header = 0;
+
+ if (ci->accesscount == 0) {
+ notice_lang(s_ChanServ, u, CHAN_ACCESS_LIST_EMPTY, chan);
+ return MOD_CONT;
+ }
+ if (nick && strspn(nick, "1234567890,-") == strlen(nick)) {
+ process_numlist(nick, NULL, access_list_callback, u, ci,
+ &sent_header);
+ } else {
+ for (i = 0; i < ci->accesscount; i++) {
+ if (nick && ci->access[i].nc
+ && !match_wild_nocase(nick, ci->access[i].nc->display))
+ continue;
+ access_list(u, i, ci, &sent_header);
+ }
+ }
+ if (!sent_header) {
+ notice_lang(s_ChanServ, u, CHAN_ACCESS_NO_MATCH, chan);
+ } else {
+ notice_lang(s_ChanServ, u, CHAN_ACCESS_LIST_FOOTER, ci->name);
+ }
+ } else if (stricmp(cmd, "CLEAR") == 0) {
+
+ if (readonly) {
+ notice_lang(s_ChanServ, u, CHAN_ACCESS_DISABLED);
+ return MOD_CONT;
+ }
+
+ if (!is_servadmin && !is_founder(u, ci)) {
+ notice_lang(s_ChanServ, u, PERMISSION_DENIED);
+ return MOD_CONT;
+ }
+
+ free(ci->access);
+ ci->access = NULL;
+ ci->accesscount = 0;
+
+ notice_lang(s_ChanServ, u, CHAN_ACCESS_CLEAR);
+ alog("%s: %s!%s@%s (level %d) cleared access list on %s",
+ s_ChanServ, u->nick, u->username, GetHost(u), get_access(u,
+ ci),
+ chan);
+
+ } else {
+ syntax_error(s_ChanServ, u, "ACCESS", CHAN_ACCESS_SYNTAX);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+/* Is the mask stuck? */
+
+AutoKick *is_stuck(ChannelInfo * ci, char *mask)
+{
+ int i;
+ AutoKick *akick;
+
+ for (akick = ci->akick, i = 0; i < ci->akickcount; akick++, i++) {
+ if (!(akick->flags & AK_USED) || (akick->flags & AK_ISNICK)
+ || !(akick->flags & AK_STUCK))
+ continue;
+ /* Example: mask = *!*@*.org and akick->u.mask = *!*@*.epona.org */
+ if (match_wild_nocase(mask, akick->u.mask))
+ return akick;
+#ifdef IRC_DREAMFORGE
+ /* Example: mask = *!*@irc.epona.org and akick->u.mask = *!*@*.epona.org */
+ if (match_wild_nocase(akick->u.mask, mask))
+ return akick;
+#endif
+ }
+
+ return NULL;
+}
+
+/* Ban the stuck mask in a safe manner. */
+
+void stick_mask(ChannelInfo * ci, AutoKick * akick)
+{
+ int i;
+ char *av[2];
+
+ for (i = 0; i < ci->c->bancount; i++) {
+ /* If akick is already covered by a wider ban.
+ Example: c->bans[i] = *!*@*.org and akick->u.mask = *!*@*.epona.org */
+ if (match_wild_nocase(ci->c->bans[i], akick->u.mask))
+ return;
+
+#ifdef IRC_DREAMFORGE
+ /* If akick is wider than a ban already in place.
+ Example: c->bans[i] = *!*@irc.epona.org and akick->u.mask = *!*@*.epona.org */
+ if (match_wild_nocase(akick->u.mask, ci->c->bans[i]))
+ return;
+#endif
+ }
+
+ /* Falling there means set the ban */
+
+ av[0] = sstrdup("+b");
+ av[1] = akick->u.mask;
+ send_cmd(whosends(ci), "MODE %s +b %s", ci->c->name, akick->u.mask);
+ chan_set_modes(s_ChanServ, ci->c, 2, av, 1);
+ free(av[0]);
+}
+
+/* Ban the stuck mask in a safe manner. */
+
+void stick_all(ChannelInfo * ci)
+{
+ int i;
+ char *av[2];
+ AutoKick *akick;
+
+ for (akick = ci->akick, i = 0; i < ci->akickcount; akick++, i++) {
+ if (!(akick->flags & AK_USED) || (akick->flags & AK_ISNICK)
+ || !(akick->flags & AK_STUCK))
+ continue;
+
+ av[0] = sstrdup("+b");
+ av[1] = akick->u.mask;
+ send_cmd(whosends(ci), "MODE %s +b %s", ci->c->name,
+ akick->u.mask);
+ chan_set_modes(s_ChanServ, ci->c, 2, av, 1);
+ free(av[0]);
+ }
+}
+
+/* `last' is set to the last index this routine was called with */
+static int akick_del(User * u, AutoKick * akick)
+{
+ if (!(akick->flags & AK_USED))
+ return 0;
+ if (akick->flags & AK_ISNICK) {
+ akick->u.nc = NULL;
+ } else {
+ free(akick->u.mask);
+ akick->u.mask = NULL;
+ }
+ if (akick->reason) {
+ free(akick->reason);
+ akick->reason = NULL;
+ }
+ if (akick->creator) {
+ free(akick->creator);
+ akick->creator = NULL;
+ }
+ akick->addtime = 0;
+ akick->flags = 0;
+ return 1;
+}
+
+static int akick_del_callback(User * u, int num, va_list args)
+{
+ ChannelInfo *ci = va_arg(args, ChannelInfo *);
+ int *last = va_arg(args, int *);
+ if (num < 1 || num > ci->akickcount)
+ return 0;
+ *last = num;
+ return akick_del(u, &ci->akick[num - 1]);
+}
+
+
+static int akick_list(User * u, int index, ChannelInfo * ci,
+ int *sent_header)
+{
+ AutoKick *akick = &ci->akick[index];
+
+ if (!(akick->flags & AK_USED))
+ return 0;
+ if (!*sent_header) {
+ notice_lang(s_ChanServ, u, CHAN_AKICK_LIST_HEADER, ci->name);
+ *sent_header = 1;
+ }
+
+ notice_lang(s_ChanServ, u, CHAN_AKICK_LIST_FORMAT, index + 1,
+ ((akick->flags & AK_ISNICK) ? akick->u.nc->
+ display : akick->u.mask),
+ (akick->reason ? akick->
+ reason : getstring(u->na, NO_REASON)));
+ return 1;
+}
+
+static int akick_list_callback(User * u, int num, va_list args)
+{
+ ChannelInfo *ci = va_arg(args, ChannelInfo *);
+ int *sent_header = va_arg(args, int *);
+ if (num < 1 || num > ci->akickcount)
+ return 0;
+ return akick_list(u, num - 1, ci, sent_header);
+}
+
+static int akick_view(User * u, int index, ChannelInfo * ci,
+ int *sent_header)
+{
+ AutoKick *akick = &ci->akick[index];
+ char timebuf[64];
+ struct tm tm;
+
+ if (!(akick->flags & AK_USED))
+ return 0;
+ if (!*sent_header) {
+ notice_lang(s_ChanServ, u, CHAN_AKICK_LIST_HEADER, ci->name);
+ *sent_header = 1;
+ }
+
+ if (akick->addtime) {
+ tm = *localtime(&akick->addtime);
+ strftime_lang(timebuf, sizeof(timebuf), u,
+ STRFTIME_SHORT_DATE_FORMAT, &tm);
+ } else {
+ snprintf(timebuf, sizeof(timebuf), getstring(u->na, UNKNOWN));
+ }
+
+ notice_lang(s_ChanServ, u,
+ ((akick->
+ flags & AK_STUCK) ? CHAN_AKICK_VIEW_FORMAT_STUCK :
+ CHAN_AKICK_VIEW_FORMAT), index + 1,
+ ((akick->flags & AK_ISNICK) ? akick->u.nc->
+ display : akick->u.mask),
+ akick->creator ? akick->creator : getstring(u->na,
+ UNKNOWN),
+ timebuf,
+ (akick->reason ? akick->
+ reason : getstring(u->na, NO_REASON)));
+ return 1;
+}
+
+static int akick_view_callback(User * u, int num, va_list args)
+{
+ ChannelInfo *ci = va_arg(args, ChannelInfo *);
+ int *sent_header = va_arg(args, int *);
+ if (num < 1 || num > ci->akickcount)
+ return 0;
+ return akick_view(u, num - 1, ci, sent_header);
+}
+
+static int do_akick(User * u)
+{
+ char *chan = strtok(NULL, " ");
+ char *cmd = strtok(NULL, " ");
+ char *mask = strtok(NULL, " ");
+ char *reason = strtok(NULL, "");
+ ChannelInfo *ci;
+ AutoKick *akick;
+ int i;
+
+ if (!cmd || (!mask && (!stricmp(cmd, "ADD") || !stricmp(cmd, "STICK")
+ || !stricmp(cmd, "UNSTICK")
+ || !stricmp(cmd, "DEL")))) {
+
+ syntax_error(s_ChanServ, u, "AKICK", CHAN_AKICK_SYNTAX);
+ } else if (!(ci = cs_findchan(chan))) {
+ notice_lang(s_ChanServ, u, CHAN_X_NOT_REGISTERED, chan);
+ } else if (ci->flags & CI_VERBOTEN) {
+ notice_lang(s_ChanServ, u, CHAN_X_FORBIDDEN, chan);
+ } else if (!check_access(u, ci, CA_AKICK) && !is_services_admin(u)) {
+ notice_lang(s_ChanServ, u, ACCESS_DENIED);
+ } else if (stricmp(cmd, "ADD") == 0) {
+
+ NickAlias *na = findnick(mask);
+ NickCore *nc = NULL;
+ char *nick, *user, *host;
+
+ if (readonly) {
+ notice_lang(s_ChanServ, u, CHAN_AKICK_DISABLED);
+ return MOD_CONT;
+ }
+
+ if (!na) {
+ split_usermask(mask, &nick, &user, &host);
+ mask =
+ scalloc(strlen(nick) + strlen(user) + strlen(host) + 3, 1);
+ sprintf(mask, "%s!%s@%s", nick, user, host);
+ free(nick);
+ free(user);
+ free(host);
+ } else {
+ if (na->status & NS_VERBOTEN) {
+ notice_lang(s_ChanServ, u, NICK_X_FORBIDDEN, mask);
+ return MOD_CONT;
+ }
+ nc = na->nc;
+ }
+
+ /* Check excepts BEFORE we get this far */
+#if defined (IRC_ULTIMATE) || defined(IRC_ULTIMATE3) || defined(IRC_UNREAL) || defined(IRC_VIAGRA) || defined(IRC_HYBRID)
+ if (is_excepted_mask(ci, mask) == 1) {
+ notice_lang(s_ChanServ, u, CHAN_EXCEPTED, mask, chan);
+ return MOD_CONT;
+ }
+#endif
+
+ for (akick = ci->akick, i = 0; i < ci->akickcount; akick++, i++) {
+ if (!(akick->flags & AK_USED))
+ continue;
+ if ((akick->flags & AK_ISNICK) ? akick->u.nc == nc
+ : stricmp(akick->u.mask, mask) == 0) {
+ notice_lang(s_ChanServ, u, CHAN_AKICK_ALREADY_EXISTS,
+ (akick->flags & AK_ISNICK) ? akick->u.nc->
+ display : akick->u.mask, chan);
+ return MOD_CONT;
+ }
+ }
+
+ for (i = 0; i < ci->akickcount; i++) {
+ if (!(ci->akick[i].flags & AK_USED))
+ break;
+ }
+ if (i == ci->akickcount) {
+ if (ci->akickcount >= CSAutokickMax) {
+ notice_lang(s_ChanServ, u, CHAN_AKICK_REACHED_LIMIT,
+ CSAutokickMax);
+ return MOD_CONT;
+ }
+ ci->akickcount++;
+ ci->akick =
+ srealloc(ci->akick, sizeof(AutoKick) * ci->akickcount);
+ }
+ akick = &ci->akick[i];
+ akick->flags = AK_USED;
+ if (nc) {
+ akick->flags |= AK_ISNICK;
+ akick->u.nc = nc;
+ } else {
+ akick->u.mask = mask;
+ }
+ akick->creator = sstrdup(u->nick);
+ akick->addtime = time(NULL);
+ if (reason) {
+ if (strlen(reason) > 200)
+ reason[200] = '\0';
+ akick->reason = sstrdup(reason);
+ } else {
+ akick->reason = NULL;
+ }
+ notice_lang(s_ChanServ, u, CHAN_AKICK_ADDED, mask, chan);
+
+ } else if (stricmp(cmd, "STICK") == 0) {
+ NickAlias *na;
+ NickCore *nc;
+
+ if (readonly) {
+ notice_lang(s_ChanServ, u, CHAN_AKICK_DISABLED);
+ return MOD_CONT;
+ }
+
+ if (ci->akickcount == 0) {
+ notice_lang(s_ChanServ, u, CHAN_AKICK_LIST_EMPTY, ci->name);
+ return MOD_CONT;
+ }
+
+ na = findnick(mask);
+ nc = (na ? na->nc : NULL);
+
+ for (akick = ci->akick, i = 0; i < ci->akickcount; akick++, i++) {
+ if (!(akick->flags & AK_USED) || (akick->flags & AK_ISNICK))
+ continue;
+ if (!stricmp(akick->u.mask, mask))
+ break;
+ }
+
+ if (i == ci->akickcount) {
+ notice_lang(s_ChanServ, u, CHAN_AKICK_NOT_FOUND, mask,
+ ci->name);
+ return MOD_CONT;
+ }
+
+ akick->flags |= AK_STUCK;
+ notice_lang(s_ChanServ, u, CHAN_AKICK_STUCK, akick->u.mask,
+ ci->name);
+
+ if (ci->c)
+ stick_mask(ci, akick);
+ } else if (stricmp(cmd, "UNSTICK") == 0) {
+ NickAlias *na;
+ NickCore *nc;
+
+ if (readonly) {
+ notice_lang(s_ChanServ, u, CHAN_AKICK_DISABLED);
+ return MOD_CONT;
+ }
+
+ if (ci->akickcount == 0) {
+ notice_lang(s_ChanServ, u, CHAN_AKICK_LIST_EMPTY, ci->name);
+ return MOD_CONT;
+ }
+
+ na = findnick(mask);
+ nc = (na ? na->nc : NULL);
+
+ for (akick = ci->akick, i = 0; i < ci->akickcount; akick++, i++) {
+ if (!(akick->flags & AK_USED) || (akick->flags & AK_ISNICK))
+ continue;
+ if (!stricmp(akick->u.mask, mask))
+ break;
+ }
+
+ if (i == ci->akickcount) {
+ notice_lang(s_ChanServ, u, CHAN_AKICK_NOT_FOUND, mask,
+ ci->name);
+ return MOD_CONT;
+ }
+
+ akick->flags &= ~AK_STUCK;
+ notice_lang(s_ChanServ, u, CHAN_AKICK_UNSTUCK, akick->u.mask,
+ ci->name);
+
+ } else if (stricmp(cmd, "DEL") == 0) {
+
+ if (readonly) {
+ notice_lang(s_ChanServ, u, CHAN_AKICK_DISABLED);
+ return MOD_CONT;
+ }
+
+ if (ci->akickcount == 0) {
+ notice_lang(s_ChanServ, u, CHAN_AKICK_LIST_EMPTY, chan);
+ return MOD_CONT;
+ }
+
+ /* Special case: is it a number/list? Only do search if it isn't. */
+ if (isdigit(*mask) && strspn(mask, "1234567890,-") == strlen(mask)) {
+ int count, deleted, last = -1;
+ deleted = process_numlist(mask, &count, akick_del_callback, u,
+ ci, &last);
+ if (!deleted) {
+ if (count == 1) {
+ notice_lang(s_ChanServ, u, CHAN_AKICK_NO_SUCH_ENTRY,
+ last, ci->name);
+ } else {
+ notice_lang(s_ChanServ, u, CHAN_AKICK_NO_MATCH,
+ ci->name);
+ }
+ } else if (deleted == 1) {
+ notice_lang(s_ChanServ, u, CHAN_AKICK_DELETED_ONE,
+ ci->name);
+ } else {
+ notice_lang(s_ChanServ, u, CHAN_AKICK_DELETED_SEVERAL,
+ deleted, ci->name);
+ }
+ } else {
+ NickAlias *na = findnick(mask);
+ NickCore *nc = (na ? na->nc : NULL);
+
+ for (akick = ci->akick, i = 0; i < ci->akickcount;
+ akick++, i++) {
+ if (!(akick->flags & AK_USED))
+ continue;
+ if (((akick->flags & AK_ISNICK) && akick->u.nc == nc)
+ || (!(akick->flags & AK_ISNICK)
+ && stricmp(akick->u.mask, mask) == 0))
+ break;
+ }
+ if (i == ci->akickcount) {
+ notice_lang(s_ChanServ, u, CHAN_AKICK_NOT_FOUND, mask,
+ chan);
+ return MOD_CONT;
+ }
+ notice_lang(s_ChanServ, u, CHAN_AKICK_DELETED, mask, chan);
+ akick_del(u, akick);
+ }
+
+ } else if (stricmp(cmd, "LIST") == 0) {
+ int sent_header = 0;
+
+ if (ci->akickcount == 0) {
+ notice_lang(s_ChanServ, u, CHAN_AKICK_LIST_EMPTY, chan);
+ return MOD_CONT;
+ }
+ if (mask && isdigit(*mask) &&
+ strspn(mask, "1234567890,-") == strlen(mask)) {
+ process_numlist(mask, NULL, akick_list_callback, u, ci,
+ &sent_header);
+ } else {
+ for (akick = ci->akick, i = 0; i < ci->akickcount;
+ akick++, i++) {
+ if (!(akick->flags & AK_USED))
+ continue;
+ if (mask) {
+ if (!(akick->flags & AK_ISNICK)
+ && !match_wild_nocase(mask, akick->u.mask))
+ continue;
+ if ((akick->flags & AK_ISNICK)
+ && !match_wild_nocase(mask, akick->u.nc->display))
+ continue;
+ }
+ akick_list(u, i, ci, &sent_header);
+ }
+ }
+ if (!sent_header)
+ notice_lang(s_ChanServ, u, CHAN_AKICK_NO_MATCH, chan);
+
+ } else if (stricmp(cmd, "VIEW") == 0) {
+ int sent_header = 0;
+
+ if (ci->akickcount == 0) {
+ notice_lang(s_ChanServ, u, CHAN_AKICK_LIST_EMPTY, chan);
+ return MOD_CONT;
+ }
+ if (mask && isdigit(*mask) &&
+ strspn(mask, "1234567890,-") == strlen(mask)) {
+ process_numlist(mask, NULL, akick_view_callback, u, ci,
+ &sent_header);
+ } else {
+ for (akick = ci->akick, i = 0; i < ci->akickcount;
+ akick++, i++) {
+ if (!(akick->flags & AK_USED))
+ continue;
+ if (mask) {
+ if (!(akick->flags & AK_ISNICK)
+ && !match_wild_nocase(mask, akick->u.mask))
+ continue;
+ if ((akick->flags & AK_ISNICK)
+ && !match_wild_nocase(mask, akick->u.nc->display))
+ continue;
+ }
+ akick_view(u, i, ci, &sent_header);
+ }
+ }
+ if (!sent_header)
+ notice_lang(s_ChanServ, u, CHAN_AKICK_NO_MATCH, chan);
+
+ } else if (stricmp(cmd, "ENFORCE") == 0) {
+ Channel *c = findchan(ci->name);
+ struct c_userlist *cu = NULL;
+ struct c_userlist *next;
+ char *argv[3];
+ int count = 0;
+
+ if (!c) {
+ notice_lang(s_ChanServ, u, CHAN_X_NOT_IN_USE, ci->name);
+ return MOD_CONT;
+ }
+
+ cu = c->users;
+
+ while (cu) {
+ next = cu->next;
+ if (check_kick(cu->user, c->name)) {
+ argv[0] = sstrdup(c->name);
+ argv[1] = sstrdup(cu->user->nick);
+ argv[2] = sstrdup(CSAutokickReason);
+
+ do_kick(s_ChanServ, 3, argv);
+
+ free(argv[2]);
+ free(argv[1]);
+ free(argv[0]);
+
+ count++;
+ }
+ cu = next;
+ }
+
+ notice_lang(s_ChanServ, u, CHAN_AKICK_ENFORCE_DONE, chan, count);
+
+ } else if (stricmp(cmd, "CLEAR") == 0) {
+
+ if (readonly) {
+ notice_lang(s_ChanServ, u, CHAN_AKICK_DISABLED);
+ return MOD_CONT;
+ }
+
+ for (akick = ci->akick, i = 0; i < ci->akickcount; akick++, i++) {
+ if (!(akick->flags & AK_USED))
+ continue;
+ akick_del(u, akick);
+ }
+
+ free(ci->akick);
+ ci->akick = NULL;
+ ci->akickcount = 0;
+
+ notice_lang(s_ChanServ, u, CHAN_AKICK_CLEAR);
+
+ } else {
+ syntax_error(s_ChanServ, u, "AKICK", CHAN_AKICK_SYNTAX);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_levels(User * u)
+{
+ char *chan = strtok(NULL, " ");
+ char *cmd = strtok(NULL, " ");
+ char *what = strtok(NULL, " ");
+ char *s = strtok(NULL, " ");
+ char *error;
+
+ ChannelInfo *ci;
+ short level;
+ int i;
+
+ /* If SET, we want two extra parameters; if DIS[ABLE] or FOUNDER, we want only
+ * one; else, we want none.
+ */
+ if (!cmd
+ || ((stricmp(cmd, "SET") == 0) ? !s
+ : ((strnicmp(cmd, "DIS", 3) == 0)) ? (!what || s) : !!what)) {
+ syntax_error(s_ChanServ, u, "LEVELS", CHAN_LEVELS_SYNTAX);
+ } else if (!(ci = cs_findchan(chan))) {
+ notice_lang(s_ChanServ, u, CHAN_X_NOT_REGISTERED, chan);
+ } else if (ci->flags & CI_VERBOTEN) {
+ notice_lang(s_ChanServ, u, CHAN_X_FORBIDDEN, chan);
+ } else if (ci->flags & CI_XOP) {
+ notice_lang(s_ChanServ, u, CHAN_LEVELS_XOP);
+ } else if (!is_founder(u, ci) && !is_services_admin(u)) {
+ notice_lang(s_ChanServ, u, ACCESS_DENIED);
+ } else if (stricmp(cmd, "SET") == 0) {
+ level = strtol(s, &error, 10);
+
+ if (*error != '\0') {
+ syntax_error(s_ChanServ, u, "LEVELS", CHAN_LEVELS_SYNTAX);
+ return MOD_CONT;
+ }
+
+ if (level <= ACCESS_INVALID || level >= ACCESS_FOUNDER) {
+ notice_lang(s_ChanServ, u, CHAN_LEVELS_RANGE,
+ ACCESS_INVALID + 1, ACCESS_FOUNDER - 1);
+ return MOD_CONT;
+ }
+
+ for (i = 0; levelinfo[i].what >= 0; i++) {
+ if (stricmp(levelinfo[i].name, what) == 0) {
+ ci->levels[levelinfo[i].what] = level;
+
+ alog("%s: %s!%s@%s set level %s on channel %s to %d",
+ s_ChanServ, u->nick, u->username, GetHost(u),
+ levelinfo[i].name, ci->name, level);
+ notice_lang(s_ChanServ, u, CHAN_LEVELS_CHANGED,
+ levelinfo[i].name, chan, level);
+ return MOD_CONT;
+ }
+ }
+
+ notice_lang(s_ChanServ, u, CHAN_LEVELS_UNKNOWN, what, s_ChanServ);
+
+ } else if (stricmp(cmd, "DIS") == 0 || stricmp(cmd, "DISABLE") == 0) {
+ for (i = 0; levelinfo[i].what >= 0; i++) {
+ if (stricmp(levelinfo[i].name, what) == 0) {
+ ci->levels[levelinfo[i].what] = ACCESS_INVALID;
+
+ alog("%s: %s!%s@%s disabled level %s on channel %s",
+ s_ChanServ, u->nick, u->username, GetHost(u),
+ levelinfo[i].name, ci->name);
+ notice_lang(s_ChanServ, u, CHAN_LEVELS_DISABLED,
+ levelinfo[i].name, chan);
+ return MOD_CONT;
+ }
+ }
+
+ notice_lang(s_ChanServ, u, CHAN_LEVELS_UNKNOWN, what, s_ChanServ);
+ } else if (stricmp(cmd, "LIST") == 0) {
+ int i;
+
+ notice_lang(s_ChanServ, u, CHAN_LEVELS_LIST_HEADER, chan);
+
+ if (!levelinfo_maxwidth) {
+ for (i = 0; levelinfo[i].what >= 0; i++) {
+ int len = strlen(levelinfo[i].name);
+ if (len > levelinfo_maxwidth)
+ levelinfo_maxwidth = len;
+ }
+ }
+
+ for (i = 0; levelinfo[i].what >= 0; i++) {
+ int j = ci->levels[levelinfo[i].what];
+
+ if (j == ACCESS_INVALID) {
+ j = levelinfo[i].what;
+
+ if (j == CA_AUTOOP || j == CA_AUTODEOP || j == CA_AUTOVOICE
+ || j == CA_NOJOIN) {
+ notice_lang(s_ChanServ, u, CHAN_LEVELS_LIST_DISABLED,
+ levelinfo_maxwidth, levelinfo[i].name);
+ } else {
+ notice_lang(s_ChanServ, u, CHAN_LEVELS_LIST_DISABLED,
+ levelinfo_maxwidth, levelinfo[i].name);
+ }
+ } else if (j == ACCESS_FOUNDER) {
+ notice_lang(s_ChanServ, u, CHAN_LEVELS_LIST_FOUNDER,
+ levelinfo_maxwidth, levelinfo[i].name);
+ } else {
+ notice_lang(s_ChanServ, u, CHAN_LEVELS_LIST_NORMAL,
+ levelinfo_maxwidth, levelinfo[i].name, j);
+ }
+ }
+
+ } else if (stricmp(cmd, "RESET") == 0) {
+ reset_levels(ci);
+
+ alog("%s: %s!%s@%s reset levels definitions on channel %s",
+ s_ChanServ, u->nick, u->username, GetHost(u), ci->name);
+ notice_lang(s_ChanServ, u, CHAN_LEVELS_RESET, chan);
+ } else {
+ syntax_error(s_ChanServ, u, "LEVELS", CHAN_LEVELS_SYNTAX);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+/* SADMINS and users, who have identified for a channel, can now cause it's
+ * Enstry Message and Successor to be displayed by supplying the ALL parameter.
+ * Syntax: INFO channel [ALL]
+ * -TheShadow (29 Mar 1999)
+ */
+
+static int do_info(User * u)
+{
+ char *chan = strtok(NULL, " ");
+ char *param = strtok(NULL, " ");
+ ChannelInfo *ci;
+ char buf[BUFSIZE], *end;
+ struct tm *tm;
+ int need_comma = 0;
+ const char *commastr = getstring(u->na, COMMA_SPACE);
+ int is_servadmin = is_services_admin(u);
+ int show_all = 0;
+
+ if (!chan) {
+ syntax_error(s_ChanServ, u, "INFO", CHAN_INFO_SYNTAX);
+ } else if (!(ci = cs_findchan(chan))) {
+ notice_lang(s_ChanServ, u, CHAN_X_NOT_REGISTERED, chan);
+ } else if (ci->flags & CI_VERBOTEN) {
+ if (is_oper(u) && ci->forbidby)
+ notice_lang(s_ChanServ, u, CHAN_X_FORBIDDEN_OPER, chan,
+ ci->forbidby,
+ (ci->forbidreason ? ci->
+ forbidreason : getstring(u->na, NO_REASON)));
+ else
+ notice_lang(s_ChanServ, u, CHAN_X_FORBIDDEN, chan);
+ } else if (!ci->founder) {
+ /* Paranoia... this shouldn't be able to happen */
+ delchan(ci);
+ notice_lang(s_ChanServ, u, CHAN_X_NOT_REGISTERED, chan);
+ } else {
+
+ /* Should we show all fields? Only for sadmins and identified users */
+ if (param && stricmp(param, "ALL") == 0 &&
+ (check_access(u, ci, CA_INFO) || is_servadmin))
+ show_all = 1;
+
+ notice_lang(s_ChanServ, u, CHAN_INFO_HEADER, chan);
+ notice_lang(s_ChanServ, u, CHAN_INFO_NO_FOUNDER,
+ ci->founder->display);
+
+ if (show_all && ci->successor)
+ notice_lang(s_ChanServ, u, CHAN_INFO_NO_SUCCESSOR,
+ ci->successor->display);
+
+ notice_lang(s_ChanServ, u, CHAN_INFO_DESCRIPTION, ci->desc);
+ tm = localtime(&ci->time_registered);
+ strftime_lang(buf, sizeof(buf), u, STRFTIME_DATE_TIME_FORMAT, tm);
+ notice_lang(s_ChanServ, u, CHAN_INFO_TIME_REGGED, buf);
+ tm = localtime(&ci->last_used);
+ strftime_lang(buf, sizeof(buf), u, STRFTIME_DATE_TIME_FORMAT, tm);
+ notice_lang(s_ChanServ, u, CHAN_INFO_LAST_USED, buf);
+ if (ci->last_topic && (show_all || (!(ci->mlock_on & CMODE_s)
+ && (!ci->c
+ || !(ci->c->
+ mode & CMODE_s))))) {
+ notice_lang(s_ChanServ, u, CHAN_INFO_LAST_TOPIC,
+ ci->last_topic);
+ notice_lang(s_ChanServ, u, CHAN_INFO_TOPIC_SET_BY,
+ ci->last_topic_setter);
+ }
+
+ if (ci->entry_message && show_all)
+ notice_lang(s_ChanServ, u, CHAN_INFO_ENTRYMSG,
+ ci->entry_message);
+ if (ci->url)
+ notice_lang(s_ChanServ, u, CHAN_INFO_URL, ci->url);
+ if (ci->email)
+ notice_lang(s_ChanServ, u, CHAN_INFO_EMAIL, ci->email);
+
+ if (show_all) {
+ notice_lang(s_ChanServ, u, CHAN_INFO_BANTYPE, ci->bantype);
+
+ end = buf;
+ *end = 0;
+ if (ci->flags & CI_KEEPTOPIC) {
+ end += snprintf(end, sizeof(buf) - (end - buf), "%s",
+ getstring(u->na, CHAN_INFO_OPT_KEEPTOPIC));
+ need_comma = 1;
+ }
+ if (ci->flags & CI_OPNOTICE) {
+ end += snprintf(end, sizeof(buf) - (end - buf), "%s%s",
+ need_comma ? commastr : "",
+ getstring(u->na, CHAN_INFO_OPT_OPNOTICE));
+ need_comma = 1;
+ }
+ if (ci->flags & CI_PEACE) {
+ end += snprintf(end, sizeof(buf) - (end - buf), "%s%s",
+ need_comma ? commastr : "",
+ getstring(u->na, CHAN_INFO_OPT_PEACE));
+ need_comma = 1;
+ }
+ if (ci->flags & CI_PRIVATE) {
+ end += snprintf(end, sizeof(buf) - (end - buf), "%s%s",
+ need_comma ? commastr : "",
+ getstring(u->na, CHAN_INFO_OPT_PRIVATE));
+ need_comma = 1;
+ }
+ if (ci->flags & CI_RESTRICTED) {
+ end += snprintf(end, sizeof(buf) - (end - buf), "%s%s",
+ need_comma ? commastr : "",
+ getstring(u->na,
+ CHAN_INFO_OPT_RESTRICTED));
+ need_comma = 1;
+ }
+ if (ci->flags & CI_SECURE) {
+ end += snprintf(end, sizeof(buf) - (end - buf), "%s%s",
+ need_comma ? commastr : "",
+ getstring(u->na, CHAN_INFO_OPT_SECURE));
+ need_comma = 1;
+ }
+ if (ci->flags & CI_SECUREOPS) {
+ end += snprintf(end, sizeof(buf) - (end - buf), "%s%s",
+ need_comma ? commastr : "",
+ getstring(u->na, CHAN_INFO_OPT_SECUREOPS));
+ need_comma = 1;
+ }
+ if (ci->flags & CI_SECUREFOUNDER) {
+ end += snprintf(end, sizeof(buf) - (end - buf), "%s%s",
+ need_comma ? commastr : "",
+ getstring(u->na,
+ CHAN_INFO_OPT_SECUREFOUNDER));
+ need_comma = 1;
+ }
+ if ((ci->flags & CI_SIGNKICK)
+ || (ci->flags & CI_SIGNKICK_LEVEL)) {
+ end +=
+ snprintf(end, sizeof(buf) - (end - buf), "%s%s",
+ need_comma ? commastr : "", getstring(u->na,
+ CHAN_INFO_OPT_SIGNKICK));
+ need_comma = 1;
+ }
+ if (ci->flags & CI_TOPICLOCK) {
+ end += snprintf(end, sizeof(buf) - (end - buf), "%s%s",
+ need_comma ? commastr : "",
+ getstring(u->na, CHAN_INFO_OPT_TOPICLOCK));
+ need_comma = 1;
+ }
+ if (ci->flags & CI_XOP) {
+ end += snprintf(end, sizeof(buf) - (end - buf), "%s%s",
+ need_comma ? commastr : "",
+ getstring(u->na, CHAN_INFO_OPT_XOP));
+ need_comma = 1;
+ }
+ notice_lang(s_ChanServ, u, CHAN_INFO_OPTIONS,
+ *buf ? buf : getstring(u->na, CHAN_INFO_OPT_NONE));
+ notice_lang(s_ChanServ, u, CHAN_INFO_MODE_LOCK,
+ get_mlock_modes(ci, 1));
+
+ }
+
+ if ((ci->flags & CI_NO_EXPIRE) && show_all)
+ notice_lang(s_ChanServ, u, CHAN_INFO_NO_EXPIRE);
+ if (ci->flags & CI_SUSPENDED) {
+ notice_lang(s_ChanServ, u, CHAN_X_SUSPENDED, ci->forbidby,
+ (ci->forbidreason ? ci->
+ forbidreason : getstring(u->na, NO_REASON)));
+ }
+
+ if (!show_all && (check_access(u, ci, CA_INFO) || is_servadmin))
+ notice_lang(s_ChanServ, u, NICK_INFO_FOR_MORE, s_ChanServ,
+ ci->name);
+
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_list(User * u)
+{
+ char *pattern = strtok(NULL, " ");
+ ChannelInfo *ci;
+ int nchans, i;
+ char buf[BUFSIZE];
+ int is_servadmin = is_services_admin(u);
+ int count = 0, from = 0, to = 0;
+ char *tmp = NULL;
+ char *s = NULL;
+ char *keyword;
+ int32 matchflags = 0;
+
+
+ if (CSListOpersOnly && (!u || !is_oper(u))) {
+ notice_lang(s_ChanServ, u, PERMISSION_DENIED);
+ return MOD_CONT;
+ }
+
+ if (!pattern) {
+ syntax_error(s_ChanServ, u, "LIST",
+ is_servadmin ? CHAN_LIST_SERVADMIN_SYNTAX :
+ CHAN_LIST_SYNTAX);
+ } else {
+
+ if (pattern) {
+ if (pattern[0] == '#') {
+ tmp = myStrGetOnlyToken((pattern + 1), '-', 0); /* Read FROM out */
+ if (!tmp) {
+ return MOD_CONT;
+ }
+ for (s = tmp; *s; s++) {
+ if (!isdigit(*s)) {
+ return MOD_CONT;
+ }
+ }
+ from = atoi(tmp);
+ tmp = myStrGetTokenRemainder(pattern, '-', 1); /* Read TO out */
+ if (!tmp) {
+ return MOD_CONT;
+ }
+ for (s = tmp; *s; s++) {
+ if (!isdigit(*s)) {
+ return MOD_CONT;
+ }
+ }
+ to = atoi(tmp);
+ pattern = sstrdup("*");
+ }
+ }
+
+ nchans = 0;
+
+ while (is_servadmin && (keyword = strtok(NULL, " "))) {
+ if (stricmp(keyword, "FORBIDDEN") == 0)
+ matchflags |= CI_VERBOTEN;
+ if (stricmp(keyword, "SUSPENDED") == 0)
+ matchflags |= CI_SUSPENDED;
+ if (stricmp(keyword, "NOEXPIRE") == 0)
+ matchflags |= CI_NO_EXPIRE;
+ }
+
+ notice_lang(s_ChanServ, u, CHAN_LIST_HEADER, pattern);
+ for (i = 0; i < 256; i++) {
+ for (ci = chanlists[i]; ci; ci = ci->next) {
+ if (!is_servadmin && ((ci->flags & CI_PRIVATE)
+ || (ci->flags & CI_VERBOTEN)))
+ continue;
+ if ((matchflags != 0) && !(ci->flags & matchflags))
+ continue;
+
+ if (stricmp(pattern, ci->name) == 0
+ || match_wild_nocase(pattern, ci->name)) {
+ if ((((count + 1 >= from) && (count + 1 <= to))
+ || ((from == 0) && (to == 0)))
+ && (++nchans <= CSListMax)) {
+ char noexpire_char = ' ';
+ if (is_servadmin && (ci->flags & CI_NO_EXPIRE))
+ noexpire_char = '!';
+
+ if (ci->flags & CI_VERBOTEN) {
+ snprintf(buf, sizeof(buf),
+ "%-20s [Forbidden]", ci->name);
+ } else if (ci->flags & CI_SUSPENDED) {
+ snprintf(buf, sizeof(buf),
+ "%-20s [Suspended]", ci->name);
+ } else {
+ snprintf(buf, sizeof(buf), "%-20s %s",
+ ci->name, ci->desc ? ci->desc : "");
+ }
+
+ notice_user(s_ChanServ, u, " %c%s",
+ noexpire_char, buf);
+ }
+ count++;
+ }
+ }
+ }
+ notice_lang(s_ChanServ, u, CHAN_LIST_END,
+ nchans > CSListMax ? CSListMax : nchans, nchans);
+ }
+ return MOD_CONT;
+
+}
+
+/*************************************************************************/
+
+static int do_invite(User * u)
+{
+ char *chan = strtok(NULL, " ");
+ Channel *c;
+ ChannelInfo *ci;
+
+ if (!chan) {
+ syntax_error(s_ChanServ, u, "INVITE", CHAN_INVITE_SYNTAX);
+ } else if (!(c = findchan(chan))) {
+ notice_lang(s_ChanServ, u, CHAN_X_NOT_IN_USE, chan);
+ } else if (!(ci = c->ci)) {
+ notice_lang(s_ChanServ, u, CHAN_X_NOT_REGISTERED, chan);
+ } else if (ci->flags & CI_VERBOTEN) {
+ notice_lang(s_ChanServ, u, CHAN_X_FORBIDDEN, chan);
+ } else if (!u || !check_access(u, ci, CA_INVITE)) {
+ notice_lang(s_ChanServ, u, PERMISSION_DENIED);
+ } else {
+ send_cmd(whosends(ci), "INVITE %s %s", u->nick, chan);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+/* do_util: not a command, but does the job of other */
+
+static int do_util(User * u, CSModeUtil * util)
+{
+ char *av[2];
+ char *chan = strtok(NULL, " ");
+ char *nick = strtok(NULL, " ");
+
+ Channel *c;
+ ChannelInfo *ci;
+ User *u2;
+
+ int is_same;
+
+ if (!chan) {
+ struct u_chanlist *uc;
+
+ av[0] = util->mode;
+ av[1] = u->nick;
+
+ /* Sets the mode to the user on every channels he is on. */
+
+ for (uc = u->chans; uc; uc = uc->next) {
+ if ((ci = uc->chan->ci) && !(ci->flags & CI_VERBOTEN)
+ && check_access(u, ci, util->levelself)) {
+ send_cmd(whosends(ci), "MODE %s %s %s", uc->chan->name,
+ util->mode, u->nick);
+ chan_set_modes(s_ChanServ, uc->chan, 2, av, 1);
+
+ if (util->notice && ci->flags & util->notice)
+ notice(whosends(ci), chan,
+ "%s command used for %s by %s", util->name,
+ u->nick, u->nick);
+ }
+ }
+
+ return MOD_CONT;
+ } else if (!nick) {
+ nick = u->nick;
+ }
+
+ is_same = (nick == u->nick) ? 1 : (stricmp(nick, u->nick) == 0);
+
+ if (!(c = findchan(chan))) {
+ notice_lang(s_ChanServ, u, CHAN_X_NOT_IN_USE, chan);
+ } else if (!(ci = c->ci)) {
+ notice_lang(s_ChanServ, u, CHAN_X_NOT_REGISTERED, c->name);
+ } else if (ci->flags & CI_VERBOTEN) {
+ notice_lang(s_ChanServ, u, CHAN_X_FORBIDDEN, ci->name);
+ } else if (is_same ? !(u2 = u) : !(u2 = finduser(nick))) {
+ notice_lang(s_ChanServ, u, NICK_X_NOT_IN_USE, nick);
+ } else if (!is_on_chan(c, u2)) {
+ notice_lang(s_ChanServ, u, NICK_X_NOT_ON_CHAN, u2->nick, c->name);
+ } else if (is_same ? !check_access(u, ci, util->levelself) :
+ !check_access(u, ci, util->level)) {
+ notice_lang(s_ChanServ, u, ACCESS_DENIED);
+ } else if (*util->mode == '-' && !is_same && (ci->flags & CI_PEACE)
+ && (get_access(u2, ci) >= get_access(u, ci))) {
+ notice_lang(s_ChanServ, u, PERMISSION_DENIED);
+#if defined (IRC_ULTIMATE) || defined (IRC_ULTIMATE3)
+ } else if (*util->mode == '-' && is_protected(u2)) {
+ notice_lang(s_ChanServ, u, PERMISSION_DENIED);
+#endif
+ } else {
+ send_cmd(whosends(ci), "MODE %s %s %s", c->name, util->mode,
+ u2->nick);
+
+ av[0] = util->mode;
+ av[1] = u2->nick;
+ chan_set_modes(s_ChanServ, c, 2, av, 1);
+
+ if (util->notice && ci->flags & util->notice)
+ notice(whosends(ci), c->name, "%s command used for %s by %s",
+ util->name, u2->nick, u->nick);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_op(User * u)
+{
+ return do_util(u, &csmodeutils[MUT_OP]);
+}
+
+/*************************************************************************/
+
+static int do_deop(User * u)
+{
+ return do_util(u, &csmodeutils[MUT_DEOP]);
+}
+
+/*************************************************************************/
+
+static int do_voice(User * u)
+{
+ return do_util(u, &csmodeutils[MUT_VOICE]);
+}
+
+/*************************************************************************/
+
+static int do_devoice(User * u)
+{
+ return do_util(u, &csmodeutils[MUT_DEVOICE]);
+}
+
+/*************************************************************************/
+
+#ifdef HAS_HALFOP
+
+static int do_halfop(User * u)
+{
+ return do_util(u, &csmodeutils[MUT_HALFOP]);
+}
+
+/*************************************************************************/
+
+static int do_dehalfop(User * u)
+{
+ return do_util(u, &csmodeutils[MUT_DEHALFOP]);
+}
+
+#endif
+
+/*************************************************************************/
+
+#if defined(IRC_UNREAL) || defined(IRC_ULTIMATE3) || defined(IRC_VIAGRA)
+
+static int do_protect(User * u)
+{
+ return do_util(u, &csmodeutils[MUT_PROTECT]);
+}
+
+/*************************************************************************/
+
+static int do_deprotect(User * u)
+{
+ return do_util(u, &csmodeutils[MUT_DEPROTECT]);
+}
+
+/*************************************************************************/
+#endif
+
+#if defined(IRC_UNREAL) || defined(IRC_VIAGRA)
+
+static int do_owner(User * u)
+{
+ char *av[2];
+ char *chan = strtok(NULL, " ");
+
+ Channel *c;
+ ChannelInfo *ci;
+
+ if (!chan) {
+ struct u_chanlist *uc;
+
+ av[0] = sstrdup("+q");
+ av[1] = u->nick;
+
+ /* Sets the mode to the user on every channels he is on. */
+
+ for (uc = u->chans; uc; uc = uc->next) {
+ if ((ci = uc->chan->ci) && !(ci->flags & CI_VERBOTEN)
+ && is_founder(u, ci)) {
+ send_cmd(whosends(ci), "MODE %s %s %s", uc->chan->name,
+ av[0], u->nick);
+ chan_set_modes(s_ChanServ, uc->chan, 2, av, 1);
+ }
+ }
+
+ free(av[0]);
+ return MOD_CONT;
+ }
+
+ if (!(c = findchan(chan))) {
+ notice_lang(s_ChanServ, u, CHAN_X_NOT_IN_USE, chan);
+ } else if (!(ci = c->ci)) {
+ notice_lang(s_ChanServ, u, CHAN_X_NOT_REGISTERED, c->name);
+ } else if (ci->flags & CI_VERBOTEN) {
+ notice_lang(s_ChanServ, u, CHAN_X_FORBIDDEN, ci->name);
+ } else if (!is_on_chan(c, u)) {
+ notice_lang(s_ChanServ, u, NICK_X_NOT_ON_CHAN, u->nick, c->name);
+ } else if (!is_founder(u, ci)) {
+ notice_lang(s_ChanServ, u, ACCESS_DENIED);
+ } else {
+ send_cmd(whosends(ci), "MODE %s +q %s", c->name, u->nick);
+
+ av[0] = sstrdup("+q");
+ av[1] = u->nick;
+ chan_set_modes(s_ChanServ, c, 2, av, 1);
+ free(av[0]);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_deowner(User * u)
+{
+ char *av[2];
+ char *chan = strtok(NULL, " ");
+
+ Channel *c;
+ ChannelInfo *ci;
+
+ if (!chan) {
+ struct u_chanlist *uc;
+
+ av[0] = sstrdup("-q");
+ av[1] = u->nick;
+
+ /* Sets the mode to the user on every channels he is on. */
+
+ for (uc = u->chans; uc; uc = uc->next) {
+ if ((ci = uc->chan->ci) && !(ci->flags & CI_VERBOTEN)
+ && is_founder(u, ci)) {
+ send_cmd(whosends(ci), "MODE %s %s %s", uc->chan->name,
+ av[0], u->nick);
+ chan_set_modes(s_ChanServ, uc->chan, 2, av, 1);
+ }
+ }
+
+ free(av[0]);
+ return MOD_CONT;
+ }
+
+ if (!(c = findchan(chan))) {
+ notice_lang(s_ChanServ, u, CHAN_X_NOT_IN_USE, chan);
+ } else if (!(ci = c->ci)) {
+ notice_lang(s_ChanServ, u, CHAN_X_NOT_REGISTERED, c->name);
+ } else if (ci->flags & CI_VERBOTEN) {
+ notice_lang(s_ChanServ, u, CHAN_X_FORBIDDEN, ci->name);
+ } else if (!is_on_chan(c, u)) {
+ notice_lang(s_ChanServ, u, NICK_X_NOT_ON_CHAN, u->nick, c->name);
+ } else if (!is_founder(u, ci)) {
+ notice_lang(s_ChanServ, u, ACCESS_DENIED);
+ } else {
+ send_cmd(whosends(ci), "MODE %s -q %s", c->name, u->nick);
+
+ av[0] = sstrdup("-q");
+ av[1] = u->nick;
+ chan_set_modes(s_ChanServ, c, 2, av, 1);
+ free(av[0]);
+ }
+ return MOD_CONT;
+}
+
+#endif
+
+/*************************************************************************/
+
+static int do_cs_kick(User * u)
+{
+ char *chan = strtok(NULL, " ");
+ char *params = strtok(NULL, " ");
+ char *reason = strtok(NULL, "");
+
+ Channel *c;
+ ChannelInfo *ci;
+ User *u2;
+
+ int is_same;
+
+ if (!reason) {
+ reason = "Requested";
+ } else {
+ if (strlen(reason) > 200)
+ reason[200] = '\0';
+ }
+
+ if (!chan) {
+ struct u_chanlist *uc, *next;
+
+ /* Kicks the user on every channels he is on. */
+
+ for (uc = u->chans; uc; uc = next) {
+ next = uc->next;
+ if ((ci = uc->chan->ci) && !(ci->flags & CI_VERBOTEN)
+ && check_access(u, ci, CA_KICKME)) {
+ char *av[3];
+
+ if ((ci->flags & CI_SIGNKICK)
+ || ((ci->flags & CI_SIGNKICK_LEVEL)
+ && !check_access(u, ci, CA_SIGNKICK)))
+ send_cmd(whosends(ci), "KICK %s %s :%s (%s)", ci->name,
+ u->nick, reason, u->nick);
+ else
+ send_cmd(whosends(ci), "KICK %s %s :%s", ci->name,
+ u->nick, reason);
+ av[0] = ci->name;
+ av[1] = u->nick;
+ av[2] = reason;
+ do_kick(s_ChanServ, 3, av);
+ }
+ }
+
+ return MOD_CONT;
+ } else if (!params) {
+ params = u->nick;
+ }
+
+ is_same = (params == u->nick) ? 1 : (stricmp(params, u->nick) == 0);
+
+ if (!(c = findchan(chan))) {
+ notice_lang(s_ChanServ, u, CHAN_X_NOT_IN_USE, chan);
+ } else if (!(ci = c->ci)) {
+ notice_lang(s_ChanServ, u, CHAN_X_NOT_REGISTERED, chan);
+ } else if (ci->flags & CI_VERBOTEN) {
+ notice_lang(s_ChanServ, u, CHAN_X_FORBIDDEN, chan);
+ } else if (is_same ? !(u2 = u) : !(u2 = finduser(params))) {
+ notice_lang(s_ChanServ, u, NICK_X_NOT_IN_USE, params);
+ } else if (!is_on_chan(c, u2)) {
+ notice_lang(s_ChanServ, u, NICK_X_NOT_ON_CHAN, u2->nick, c->name);
+ } else if (!is_same ? !check_access(u, ci, CA_KICK) :
+ !check_access(u, ci, CA_KICKME)) {
+ notice_lang(s_ChanServ, u, ACCESS_DENIED);
+ } else if (!is_same && (ci->flags & CI_PEACE)
+ && (get_access(u2, ci) >= get_access(u, ci))) {
+ notice_lang(s_ChanServ, u, PERMISSION_DENIED);
+#if defined (IRC_ULTIMATE) || defined (IRC_ULTIMATE3)
+ } else if (is_protected(u2)) {
+ notice_lang(s_ChanServ, u, PERMISSION_DENIED);
+#endif
+ } else {
+ char *av[3];
+
+ if ((ci->flags & CI_SIGNKICK)
+ || ((ci->flags & CI_SIGNKICK_LEVEL)
+ && !check_access(u, ci, CA_SIGNKICK)))
+ send_cmd(whosends(ci), "KICK %s %s :%s (%s)", ci->name, params,
+ reason, u->nick);
+ else
+ send_cmd(whosends(ci), "KICK %s %s :%s", ci->name, params,
+ reason);
+ av[0] = ci->name;
+ av[1] = params;
+ av[2] = reason;
+ do_kick(s_ChanServ, 3, av);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_ban(User * u)
+{
+ char *chan = strtok(NULL, " ");
+ char *params = strtok(NULL, " ");
+ char *reason = strtok(NULL, "");
+
+ Channel *c;
+ ChannelInfo *ci;
+ User *u2;
+
+ int is_same;
+
+ if (!reason) {
+ reason = "Requested";
+ } else {
+ if (strlen(reason) > 200)
+ reason[200] = '\0';
+ }
+
+ if (!chan) {
+ struct u_chanlist *uc, *next;
+
+ /* Bans the user on every channels he is on. */
+
+ for (uc = u->chans; uc; uc = next) {
+ next = uc->next;
+ if ((ci = uc->chan->ci) && !(ci->flags & CI_VERBOTEN)
+ && check_access(u, ci, CA_BANME)) {
+ char *av[3];
+ char mask[BUFSIZE];
+
+#if defined (IRC_ULTIMATE) || defined(IRC_ULTIMATE3) || defined(IRC_UNREAL) || defined(IRC_VIAGRA) || defined(IRC_HYBRID)
+ /*
+ * Dont ban/kick the user on channels where he is excepted
+ * to prevent services <-> server wars.
+ */
+ if (is_excepted(ci, u))
+ notice_lang(s_ChanServ, u, CHAN_EXCEPTED,
+ u->nick, ci->name);
+ continue;
+#endif
+ av[0] = sstrdup("+b");
+ get_idealban(ci, u, mask, sizeof(mask));
+ av[1] = mask;
+ send_cmd(whosends(ci), "MODE %s +b %s", uc->chan->name,
+ av[1]);
+ chan_set_modes(s_ChanServ, uc->chan, 2, av, 1);
+ free(av[0]);
+
+ if ((ci->flags & CI_SIGNKICK)
+ || ((ci->flags & CI_SIGNKICK_LEVEL)
+ && !check_access(u, ci, CA_SIGNKICK)))
+ send_cmd(whosends(ci), "KICK %s %s :%s (%s)", ci->name,
+ u->nick, reason, u->nick);
+ else
+ send_cmd(whosends(ci), "KICK %s %s :%s", ci->name,
+ u->nick, reason);
+ av[0] = ci->name;
+ av[1] = u->nick;
+ av[2] = reason;
+ do_kick(s_ChanServ, 3, av);
+ }
+ }
+
+ return MOD_CONT;
+ } else if (!params) {
+ params = u->nick;
+ }
+
+ is_same = (params == u->nick) ? 1 : (stricmp(params, u->nick) == 0);
+
+ if (!(c = findchan(chan))) {
+ notice_lang(s_ChanServ, u, CHAN_X_NOT_IN_USE, chan);
+ } else if (!(ci = c->ci)) {
+ notice_lang(s_ChanServ, u, CHAN_X_NOT_REGISTERED, chan);
+ } else if (ci->flags & CI_VERBOTEN) {
+ notice_lang(s_ChanServ, u, CHAN_X_FORBIDDEN, chan);
+ } else if (is_same ? !(u2 = u) : !(u2 = finduser(params))) {
+ notice_lang(s_ChanServ, u, NICK_X_NOT_IN_USE, params);
+ } else if (!is_same ? !check_access(u, ci, CA_BAN) :
+ !check_access(u, ci, CA_BANME)) {
+ notice_lang(s_ChanServ, u, ACCESS_DENIED);
+ } else if (!is_same && (ci->flags & CI_PEACE)
+ && (get_access(u2, ci) >= get_access(u, ci))) {
+ notice_lang(s_ChanServ, u, PERMISSION_DENIED);
+#if defined (IRC_ULTIMATE) || defined(IRC_ULTIMATE3) || defined(IRC_UNREAL) || defined(IRC_VIAGRA) || defined(IRC_HYBRID)
+ /*
+ * Dont ban/kick the user on channels where he is excepted
+ * to prevent services <-> server wars.
+ */
+ } else if (is_excepted(ci, u2)) {
+ notice_lang(s_ChanServ, u, CHAN_EXCEPTED, u2->nick, ci->name);
+#endif
+ } else {
+ char *av[3];
+ char mask[BUFSIZE];
+
+ av[0] = sstrdup("+b");
+ get_idealban(ci, u2, mask, sizeof(mask));
+ av[1] = mask;
+ send_cmd(whosends(ci), "MODE %s +b %s", c->name, av[1]);
+ chan_set_modes(s_ChanServ, c, 2, av, 1);
+ free(av[0]);
+
+ /* We still allow host banning while not allowing to kick */
+ if (!is_on_chan(c, u2))
+ return MOD_CONT;
+
+ if ((ci->flags & CI_SIGNKICK)
+ || ((ci->flags & CI_SIGNKICK_LEVEL)
+ && !check_access(u, ci, CA_SIGNKICK)))
+ send_cmd(whosends(ci), "KICK %s %s :%s (%s)", ci->name, params,
+ reason, u->nick);
+ else
+ send_cmd(whosends(ci), "KICK %s %s :%s", ci->name, params,
+ reason);
+ av[0] = ci->name;
+ av[1] = params;
+ av[2] = reason;
+ do_kick(s_ChanServ, 3, av);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_cs_topic(User * u)
+{
+ char *chan = strtok(NULL, " ");
+ char *topic = strtok(NULL, "");
+
+ Channel *c;
+ ChannelInfo *ci;
+
+ if (!chan) {
+ syntax_error(s_ChanServ, u, "TOPIC", CHAN_TOPIC_SYNTAX);
+ } else if (!(c = findchan(chan))) {
+ notice_lang(s_ChanServ, u, CHAN_X_NOT_IN_USE, chan);
+ } else if (!(ci = c->ci)) {
+ notice_lang(s_ChanServ, u, CHAN_X_NOT_REGISTERED, c->name);
+ } else if (ci->flags & CI_VERBOTEN) {
+ notice_lang(s_ChanServ, u, CHAN_X_FORBIDDEN, ci->name);
+ } else if (!is_services_admin(u) && !check_access(u, ci, CA_TOPIC)) {
+ notice_lang(s_ChanServ, u, PERMISSION_DENIED);
+ } else {
+ if (ci->last_topic)
+ free(ci->last_topic);
+ ci->last_topic = topic ? sstrdup(topic) : NULL;
+ strscpy(ci->last_topic_setter, u->nick, NICKMAX);
+ ci->last_topic_time = time(NULL);
+
+ if (c->topic)
+ free(c->topic);
+ c->topic = topic ? sstrdup(topic) : NULL;
+ strscpy(c->topic_setter, u->nick, NICKMAX);
+#if defined(IRC_DREAMFORGE) && !defined(IRC_ULTIMATE) && !defined(IRC_UNREAL)
+ c->topic_time = c->topic_time - 1;
+#else
+ c->topic_time = ci->last_topic_time;
+#endif
+
+ if (is_services_admin(u))
+ alog("%s: %s!%s@%s changed topic of %s as services admin.",
+ s_ChanServ, u->nick, u->username, u->host, c->name);
+#ifdef IRC_HYBRID
+ if (whosends(ci) == s_ChanServ) {
+ send_cmd(NULL, "SJOIN %ld %s + :%s", time(NULL), c->name,
+ s_ChanServ);
+ send_cmd(NULL, "MODE %s +o %s", c->name, s_ChanServ);
+ }
+ send_cmd(whosends(ci), "TOPIC %s :%s", c->name,
+ c->topic ? c->topic : "");
+ if (whosends(ci) == s_ChanServ) {
+ send_cmd(s_ChanServ, "PART %s", c->name);
+ }
+#else
+ send_cmd(whosends(ci), "TOPIC %s %s %lu :%s", c->name, u->nick,
+ c->topic_time, topic ? topic : "");
+#endif
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_unban(User * u)
+{
+ char *chan = strtok(NULL, " ");
+ Channel *c;
+ ChannelInfo *ci;
+
+ if (!chan) {
+ syntax_error(s_ChanServ, u, "UNBAN", CHAN_UNBAN_SYNTAX);
+ } else if (!(c = findchan(chan))) {
+ notice_lang(s_ChanServ, u, CHAN_X_NOT_IN_USE, chan);
+ } else if (!(ci = c->ci)) {
+ notice_lang(s_ChanServ, u, CHAN_X_NOT_REGISTERED, chan);
+ } else if (ci->flags & CI_VERBOTEN) {
+ notice_lang(s_ChanServ, u, CHAN_X_FORBIDDEN, chan);
+ } else if (!check_access(u, ci, CA_UNBAN)) {
+ notice_lang(s_ChanServ, u, PERMISSION_DENIED);
+ } else {
+#ifndef IRC_BAHAMUT
+ int i;
+ char *av[3];
+ /* Save original ban info */
+ int count = c->bancount;
+ char **bans = scalloc(sizeof(char *) * count, 1);
+ memcpy(bans, c->bans, sizeof(char *) * count);
+
+ av[0] = chan;
+ av[1] = sstrdup("-b");
+ for (i = 0; i < count; i++) {
+ if (match_usermask(bans[i], u)) {
+ send_cmd(whosends(ci), "MODE %s -b %s", chan, bans[i]);
+ av[2] = sstrdup(bans[i]);
+ do_cmode(s_ChanServ, 3, av);
+ free(av[2]);
+ }
+ }
+ free(av[1]);
+ free(bans);
+#else
+ send_cmd(ServerName, "SVSMODE %s -b %s", chan, u->nick);
+#endif
+ notice_lang(s_ChanServ, u, CHAN_UNBANNED, chan);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_clear(User * u)
+{
+ char *chan = strtok(NULL, " ");
+ char *what = strtok(NULL, " ");
+ Channel *c;
+ ChannelInfo *ci;
+
+ if (!what) {
+ syntax_error(s_ChanServ, u, "CLEAR", CHAN_CLEAR_SYNTAX);
+ } else if (!(c = findchan(chan))) {
+ notice_lang(s_ChanServ, u, CHAN_X_NOT_IN_USE, chan);
+ } else if (!(ci = c->ci)) {
+ notice_lang(s_ChanServ, u, CHAN_X_NOT_REGISTERED, chan);
+ } else if (ci->flags & CI_VERBOTEN) {
+ notice_lang(s_ChanServ, u, CHAN_X_FORBIDDEN, chan);
+ } else if (!u || !check_access(u, ci, CA_CLEAR)) {
+ notice_lang(s_ChanServ, u, PERMISSION_DENIED);
+ } else if (stricmp(what, "bans") == 0) {
+ char *av[3];
+ int i;
+
+ /* Save original ban info */
+ int count = c->bancount;
+ char **bans = scalloc(sizeof(char *) * count, 1);
+ for (i = 0; i < count; i++)
+ bans[i] = sstrdup(c->bans[i]);
+
+ for (i = 0; i < count; i++) {
+ av[0] = sstrdup(chan);
+ av[1] = sstrdup("-b");
+ av[2] = bans[i];
+ send_cmd(whosends(ci), "MODE %s %s :%s", av[0], av[1], av[2]);
+ do_cmode(s_ChanServ, 3, av);
+ free(av[2]);
+ free(av[1]);
+ free(av[0]);
+ }
+ notice_lang(s_ChanServ, u, CHAN_CLEARED_BANS, chan);
+ free(bans);
+#if defined (IRC_ULTIMATE) || defined (IRC_UNREAL) || defined (IRC_ULTIMATE3) || defined(IRC_HYBRID)
+ } else if (stricmp(what, "excepts") == 0) {
+ char *av[3];
+ int i;
+
+ /* Save original except info */
+ int count = c->exceptcount;
+ char **excepts = scalloc(sizeof(char *) * count, 1);
+ for (i = 0; i < count; i++)
+ excepts[i] = sstrdup(c->excepts[i]);
+
+ for (i = 0; i < count; i++) {
+ av[0] = sstrdup(chan);
+ av[1] = sstrdup("-e");
+ av[2] = excepts[i];
+ send_cmd(whosends(ci), "MODE %s %s :%s", av[0], av[1], av[2]);
+ do_cmode(s_ChanServ, 3, av);
+ free(av[2]);
+ free(av[1]);
+ free(av[0]);
+ }
+ notice_lang(s_ChanServ, u, CHAN_CLEARED_EXCEPTS, chan);
+ free(excepts);
+#endif
+ } else if (stricmp(what, "modes") == 0) {
+ char buf[BUFSIZE], *end = buf;
+ char *argv[2];
+
+ if (c->mode) {
+ /* Clear modes */
+ send_cmd(s_ChanServ, "MODE %s %s %s", c->name, MODESTOREMOVE,
+ c->key ? c->key : "");
+ argv[0] = sstrdup(MODESTOREMOVE);
+ argv[1] = c->key ? c->key : NULL;
+ chan_set_modes(s_OperServ, c, c->key ? 2 : 1, argv, 0);
+ free(argv[0]);
+ check_modes(c);
+ }
+
+ /* TODO: decide if the above implementation is better than this one. */
+
+ if (0) {
+ CBModeInfo *cbmi = cbmodeinfos;
+ CBMode *cbm;
+
+ do {
+ if (c->mode & cbmi->flag)
+ *end++ = cbmi->mode;
+ } while ((++cbmi)->flag != 0);
+
+ cbmi = cbmodeinfos;
+
+ do {
+ if (cbmi->getvalue && (c->mode & cbmi->flag)
+ && !(cbmi->flags & CBM_MINUS_NO_ARG)) {
+ char *value = cbmi->getvalue(c);
+
+ if (value) {
+ *end++ = ' ';
+ while (*value)
+ *end++ = *value++;
+
+ /* Free the value */
+ cbm = &cbmodes[(int) cbmi->mode];
+ cbm->setvalue(c, NULL);
+ }
+ }
+ } while ((++cbmi)->flag != 0);
+
+ *end = 0;
+
+ send_cmd(whosends(ci), "MODE %s -%s", c->name, buf);
+ c->mode = 0;
+ check_modes(c);
+ }
+ notice_lang(s_ChanServ, u, CHAN_CLEARED_MODES, chan);
+ } else if (stricmp(what, "ops") == 0) {
+ char *av[3];
+ struct c_userlist *cu, *next;
+
+ for (cu = c->users; cu; cu = next) {
+ next = cu->next;
+ if (!chan_has_user_status(c, cu->user, CUS_OP))
+ continue;
+ av[0] = sstrdup(chan);
+ av[1] = sstrdup("-o");
+ av[2] = sstrdup(cu->user->nick);
+ send_cmd(whosends(ci), "MODE %s %s :%s", av[0], av[1], av[2]);
+ do_cmode(s_ChanServ, 3, av);
+ free(av[2]);
+ free(av[1]);
+ free(av[0]);
+ }
+ notice_lang(s_ChanServ, u, CHAN_CLEARED_OPS, chan);
+ } else if (stricmp(what, "voices") == 0) {
+ char *av[3];
+ struct c_userlist *cu, *next;
+
+ for (cu = c->users; cu; cu = next) {
+ next = cu->next;
+ if (!chan_has_user_status(c, cu->user, CUS_VOICE))
+ continue;
+ av[0] = sstrdup(chan);
+ av[1] = sstrdup("-v");
+ av[2] = sstrdup(cu->user->nick);
+ send_cmd(whosends(ci), "MODE %s %s :%s", av[0], av[1], av[2]);
+ do_cmode(s_ChanServ, 3, av);
+ free(av[2]);
+ free(av[1]);
+ free(av[0]);
+ }
+ notice_lang(s_ChanServ, u, CHAN_CLEARED_VOICES, chan);
+ } else if (stricmp(what, "users") == 0) {
+ char *av[3];
+ struct c_userlist *cu, *next;
+ char buf[256];
+
+ snprintf(buf, sizeof(buf), "CLEAR USERS command from %s", u->nick);
+
+ for (cu = c->users; cu; cu = next) {
+ next = cu->next;
+ av[0] = sstrdup(chan);
+ av[1] = sstrdup(cu->user->nick);
+ av[2] = sstrdup(buf);
+ send_cmd(whosends(ci), "KICK %s %s :%s", av[0], av[1], av[2]);
+ do_kick(s_ChanServ, 3, av);
+ free(av[2]);
+ free(av[1]);
+ free(av[0]);
+ }
+ notice_lang(s_ChanServ, u, CHAN_CLEARED_USERS, chan);
+ } else {
+ syntax_error(s_ChanServ, u, "CLEAR", CHAN_CLEAR_SYNTAX);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_getkey(User * u)
+{
+ char *chan = strtok(NULL, " ");
+ ChannelInfo *ci;
+
+ if (chan && (ci = cs_findchan(chan)) && !(ci->flags & CI_VERBOTEN)
+ && check_access(u, ci, CA_GETKEY)) {
+ notice_user(s_ChanServ, u, "KEY %s %s", ci->name,
+ (ci->
+ c ? (ci->c->key ? ci->c->key : "NO KEY") : "NO KEY"));
+ } else {
+ notice_user(s_ChanServ, u, "KEY %s ERROR", chan);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_getpass(User * u)
+{
+#ifndef USE_ENCRYPTION
+ char *chan = strtok(NULL, " ");
+ ChannelInfo *ci;
+#endif
+
+ /* Assumes that permission checking has already been done. */
+#ifdef USE_ENCRYPTION
+ notice_lang(s_ChanServ, u, CHAN_GETPASS_UNAVAILABLE);
+#else
+ if (!chan) {
+ syntax_error(s_ChanServ, u, "GETPASS", CHAN_GETPASS_SYNTAX);
+ } else if (!(ci = cs_findchan(chan))) {
+ notice_lang(s_ChanServ, u, CHAN_X_NOT_REGISTERED, chan);
+ } else if (ci->flags & CI_VERBOTEN) {
+ notice_lang(s_ChanServ, u, CHAN_X_FORBIDDEN, chan);
+ } else if (CSRestrictGetPass && !is_services_root(u)) {
+ notice_lang(s_ChanServ, u, PERMISSION_DENIED);
+ } else {
+ alog("%s: %s!%s@%s used GETPASS on %s",
+ s_ChanServ, u->nick, u->username, GetHost(u), ci->name);
+ if (WallGetpass) {
+ wallops(s_ChanServ, "\2%s\2 used GETPASS on channel \2%s\2",
+ u->nick, chan);
+ }
+ notice_lang(s_ChanServ, u, CHAN_GETPASS_PASSWORD_IS,
+ chan, ci->founderpass);
+ }
+#endif
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_sendpass(User * u)
+{
+#ifndef USE_ENCRYPTION
+ char *chan = strtok(NULL, " ");
+ ChannelInfo *ci;
+ NickCore *founder;
+#endif
+
+#ifdef USE_ENCRYPTION
+ notice_lang(s_ChanServ, u, CHAN_SENDPASS_UNAVAILABLE);
+#else
+ if (!chan) {
+ syntax_error(s_ChanServ, u, "SENDPASS", CHAN_SENDPASS_SYNTAX);
+ } else if (RestrictMail && !is_oper(u)) {
+ notice_lang(s_ChanServ, u, PERMISSION_DENIED);
+ } else if (!(ci = cs_findchan(chan)) || !(founder = ci->founder)) {
+ notice_lang(s_ChanServ, u, CHAN_X_NOT_REGISTERED, chan);
+ } else if (ci->flags & CI_VERBOTEN) {
+ notice_lang(s_ChanServ, u, CHAN_X_FORBIDDEN, chan);
+ } else {
+ char buf[BUFSIZE];
+ MailInfo *mail;
+
+ snprintf(buf, sizeof(buf),
+ getstring2(founder, CHAN_SENDPASS_SUBJECT), ci->name);
+ mail = MailBegin(u, founder, buf, s_ChanServ);
+ if (!mail)
+ return MOD_CONT;
+
+ fprintf(mail->pipe, getstring2(founder, CHAN_SENDPASS_HEAD));
+ fprintf(mail->pipe, "\n\n");
+ fprintf(mail->pipe, getstring2(founder, CHAN_SENDPASS_LINE_1),
+ ci->name);
+ fprintf(mail->pipe, "\n\n");
+ fprintf(mail->pipe, getstring2(founder, CHAN_SENDPASS_LINE_2),
+ ci->founderpass);
+ fprintf(mail->pipe, "\n\n");
+ fprintf(mail->pipe, getstring2(founder, CHAN_SENDPASS_LINE_3));
+ fprintf(mail->pipe, "\n\n");
+ fprintf(mail->pipe, getstring2(founder, CHAN_SENDPASS_LINE_4));
+ fprintf(mail->pipe, "\n\n");
+ fprintf(mail->pipe, getstring2(founder, CHAN_SENDPASS_LINE_5),
+ NetworkName);
+ fprintf(mail->pipe, "\n.\n");
+
+ MailEnd(mail);
+
+ alog("%s: %s!%s@%s used SENDPASS on %s", s_ChanServ, u->nick,
+ u->username, GetHost(u), chan);
+ notice_lang(s_ChanServ, u, CHAN_SENDPASS_OK, chan);
+ }
+#endif
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_forbid(User * u)
+{
+ ChannelInfo *ci;
+ char *chan = strtok(NULL, " ");
+ char *reason = strtok(NULL, "");
+
+ Channel *c;
+
+ /* Assumes that permission checking has already been done. */
+ if (!chan || (ForceForbidReason && !reason)) {
+ syntax_error(s_ChanServ, u, "FORBID",
+ (ForceForbidReason ? CHAN_FORBID_SYNTAX_REASON :
+ CHAN_FORBID_SYNTAX));
+ return MOD_CONT;
+ }
+ if (readonly)
+ notice_lang(s_ChanServ, u, READ_ONLY_MODE);
+ if ((ci = cs_findchan(chan)) != NULL)
+ delchan(ci);
+ ci = makechan(chan);
+ if (ci) {
+ ci->flags |= CI_VERBOTEN;
+ ci->forbidby = sstrdup(u->nick);
+ if (reason)
+ ci->forbidreason = sstrdup(reason);
+
+ if ((c = findchan(ci->name))) {
+ struct c_userlist *cu, *next;
+ char *av[3];
+
+ for (cu = c->users; cu; cu = next) {
+ next = cu->next;
+
+ if (is_oper(cu->user))
+ continue;
+
+ av[0] = c->name;
+ av[1] = cu->user->nick;
+ av[2] = reason ? reason : "CHAN_FORBID_REASON";
+ send_cmd(s_ChanServ, "KICK %s %s :%s", av[0], av[1],
+ av[2]);
+ do_kick(s_ChanServ, 3, av);
+ }
+ }
+
+ if (WallForbid)
+ wallops(s_ChanServ, "\2%s\2 used FORBID on channel \2%s\2",
+ u->nick, ci->name);
+
+ alog("%s: %s set FORBID for channel %s", s_ChanServ, u->nick,
+ ci->name);
+ notice_lang(s_ChanServ, u, CHAN_FORBID_SUCCEEDED, chan);
+ } else {
+ alog("%s: Valid FORBID for %s by %s failed", s_ChanServ, ci->name,
+ u->nick);
+ notice_lang(s_ChanServ, u, CHAN_FORBID_FAILED, chan);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_suspend(User * u)
+{
+ ChannelInfo *ci;
+ char *chan = strtok(NULL, " ");
+ char *reason = strtok(NULL, "");
+
+ Channel *c;
+
+ /* Assumes that permission checking has already been done. */
+ if (!chan || (ForceForbidReason && !reason)) {
+ syntax_error(s_ChanServ, u, "SUSPEND",
+ (ForceForbidReason ? CHAN_SUSPEND_SYNTAX_REASON :
+ CHAN_SUSPEND_SYNTAX));
+ return MOD_CONT;
+ }
+ if (readonly)
+ notice_lang(s_ChanServ, u, READ_ONLY_MODE);
+ if ((ci = cs_findchan(chan)) == NULL)
+ ci = makechan(chan);
+ if (ci) {
+ ci->flags |= CI_SUSPENDED;
+ ci->forbidby = sstrdup(u->nick);
+ if (reason)
+ ci->forbidreason = sstrdup(reason);
+
+ if ((c = findchan(ci->name))) {
+ struct c_userlist *cu, *next;
+ char *av[3];
+
+ for (cu = c->users; cu; cu = next) {
+ next = cu->next;
+
+ if (is_oper(cu->user))
+ continue;
+
+ av[0] = c->name;
+ av[1] = cu->user->nick;
+ av[2] = reason ? reason : "CHAN_SUSPEND_REASON";
+ send_cmd(s_ChanServ, "KICK %s %s :%s", av[0], av[1],
+ av[2]);
+ do_kick(s_ChanServ, 3, av);
+ }
+ }
+
+ if (WallForbid)
+ wallops(s_ChanServ, "\2%s\2 used SUSPEND on channel \2%s\2",
+ u->nick, ci->name);
+
+ alog("%s: %s set SUSPEND for channel %s", s_ChanServ, u->nick,
+ ci->name);
+ notice_lang(s_ChanServ, u, CHAN_SUSPEND_SUCCEEDED, chan);
+ } else {
+ alog("%s: Valid SUSPEND for %s by %s failed", s_ChanServ, ci->name,
+ u->nick);
+ notice_lang(s_ChanServ, u, CHAN_SUSPEND_FAILED, chan);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_unsuspend(User * u)
+{
+ ChannelInfo *ci;
+ char *chan = strtok(NULL, " ");
+
+ /* Assumes that permission checking has already been done. */
+ if (!chan) {
+ syntax_error(s_ChanServ, u, "UNSUSPEND", CHAN_UNSUSPEND_SYNTAX);
+ return MOD_CONT;
+ }
+ if (chan[0] != '#') {
+ notice_lang(s_ChanServ, u, CHAN_UNSUSPEND_ERROR);
+ return MOD_CONT;
+ }
+ if (readonly)
+ notice_lang(s_ChanServ, u, READ_ONLY_MODE);
+
+ ci = cs_findchan(chan);
+
+ if (ci) {
+ ci->flags &= ~CI_SUSPENDED;
+ ci->forbidreason = NULL;
+ ci->forbidby = NULL;
+
+ if (WallForbid)
+ wallops(s_ChanServ, "\2%s\2 used UNSUSPEND on channel \2%s\2",
+ u->nick, ci->name);
+
+ alog("%s: %s set UNSUSPEND for channel %s", s_ChanServ, u->nick,
+ ci->name);
+ notice_lang(s_ChanServ, u, CHAN_UNSUSPEND_SUCCEEDED, chan);
+ } else {
+ alog("%s: Valid UNSUSPEND for %s by %s failed", s_ChanServ,
+ chan, u->nick);
+ notice_lang(s_ChanServ, u, CHAN_UNSUSPEND_FAILED, chan);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/
+
+static int do_status(User * u)
+{
+ ChannelInfo *ci;
+ User *u2;
+ char *nick, *chan;
+
+ chan = strtok(NULL, " ");
+ nick = strtok(NULL, " ");
+ if (!nick || strtok(NULL, " ")) {
+ notice_user(s_ChanServ, u, "STATUS ERROR Syntax error");
+ return MOD_CONT;
+ }
+ if (!(ci = cs_findchan(chan))) {
+ char *temp = chan;
+ chan = nick;
+ nick = temp;
+ ci = cs_findchan(chan);
+ }
+ if (!ci) {
+ notice_user(s_ChanServ, u,
+ "STATUS ERROR Channel %s not registered", chan);
+ } else if (ci->flags & CI_VERBOTEN) {
+ notice_user(s_ChanServ, u, "STATUS ERROR Channel %s forbidden",
+ chan);
+ return MOD_CONT;
+ } else if ((u2 = finduser(nick)) != NULL) {
+ notice_user(s_ChanServ, u, "STATUS %s %s %d", chan, nick,
+ get_access(u2, ci));
+ } else { /* !u2 */
+ notice_user(s_ChanServ, u, "STATUS ERROR Nick %s not online",
+ nick);
+ }
+ return MOD_CONT;
+}
+
+/*************************************************************************/