1 /*
2  * Copyright (c) 2005 William Pitcock, et al.
3  * Rights to this code are as documented in doc/LICENSE.
4  *
5  * This file contains code for the ChanServ CLEAR USERS function.
6  *
7  */
8 
9 #include "atheme.h"
10 
11 DECLARE_MODULE_V1
12 (
13 	"chanserv/clear_users", false, _modinit, _moddeinit,
14 	PACKAGE_STRING,
15 	VENDOR_STRING
16 );
17 
18 static void cs_cmd_clear_users(sourceinfo_t *si, int parc, char *parv[]);
19 
20 command_t cs_clear_users = { "USERS", N_("Kicks all users from a channel."),
21 	AC_NONE, 2, cs_cmd_clear_users, { .path = "cservice/clear_users" } };
22 
23 mowgli_patricia_t **cs_clear_cmds;
24 
_modinit(module_t * m)25 void _modinit(module_t *m)
26 {
27 	MODULE_TRY_REQUEST_SYMBOL(m, cs_clear_cmds, "chanserv/clear", "cs_clear_cmds");
28 
29 	command_add(&cs_clear_users, *cs_clear_cmds);
30 }
31 
_moddeinit(module_unload_intent_t intent)32 void _moddeinit(module_unload_intent_t intent)
33 {
34 	command_delete(&cs_clear_users, *cs_clear_cmds);
35 }
36 
cs_cmd_clear_users(sourceinfo_t * si,int parc,char * parv[])37 static void cs_cmd_clear_users(sourceinfo_t *si, int parc, char *parv[])
38 {
39 	char fullreason[200];
40 	channel_t *c;
41 	char *channel = parv[0];
42 	mychan_t *mc = mychan_find(channel);
43 	chanuser_t *cu;
44 	mowgli_node_t *n, *tn;
45 	int oldlimit;
46 	unsigned int nmembers;
47 
48 	if (parc >= 2)
49 		snprintf(fullreason, sizeof fullreason, "CLEAR USERS used by %s: %s", get_source_name(si), parv[1]);
50 	else
51 		snprintf(fullreason, sizeof fullreason, "CLEAR USERS used by %s", get_source_name(si));
52 
53 	if (!mc)
54 	{
55 		command_fail(si, fault_nosuch_target, _("Channel \2%s\2 is not registered."), channel);
56 		return;
57 	}
58 
59 	if (!(c = channel_find(channel)))
60 	{
61 		command_fail(si, fault_nosuch_target, _("\2%s\2 is currently empty."), channel);
62 		return;
63 	}
64 
65 	if (!chanacs_source_has_flag(mc, si, CA_RECOVER))
66 	{
67 		command_fail(si, fault_noprivs, _("You are not authorized to perform this operation."));
68 		return;
69 	}
70 
71 	if (metadata_find(mc, "private:close:closer"))
72 	{
73 		command_fail(si, fault_noprivs, _("\2%s\2 is closed."), channel);
74 		return;
75 	}
76 
77 	command_add_flood(si, MOWGLI_LIST_LENGTH(&c->members) > 3 ? FLOOD_HEAVY : FLOOD_MODERATE);
78 
79 	/* stop a race condition where users can rejoin */
80 	oldlimit = c->limit;
81 	if (oldlimit != 1)
82 		modestack_mode_limit(chansvs.nick, c, MTYPE_ADD, 1);
83 	modestack_flush_channel(c);
84 
85 	MOWGLI_ITER_FOREACH_SAFE(n, tn, c->members.head)
86 	{
87 		cu = n->data;
88 
89 		/* don't kick the user who requested the masskick */
90 		if (cu->user == si->su || is_internal_client(cu->user))
91 			continue;
92 
93 		nmembers = MOWGLI_LIST_LENGTH(&c->members);
94 		try_kick(chansvs.me->me, c, cu->user, fullreason);
95 		/* If there are only two users remaining before the kick,
96 		 * it is possible that the last user is chanserv which will
97 		 * part if leave_chans is enabled. If it is a permanent
98 		 * channel this will leave an empty channel, otherwise the
99 		 * channel will have been destroyed. In either case, it is
100 		 * not safe to continue.
101 		 *
102 		 * Kicking the last user is safe, tn will be NULL and
103 		 * MOWGLI_ITER_FOREACH_SAFE will stop without touching any part
104 		 * of the channel structure.
105 		 */
106 		if (nmembers == 2 && ((c = channel_find(channel)) == NULL ||
107 					MOWGLI_LIST_LENGTH(&c->members) == 0))
108 			break;
109 	}
110 
111 	/* the channel may be empty now, so our pointer may be bogus! */
112 	c = channel_find(channel);
113 	if (c != NULL)
114 	{
115 		if ((mc->flags & MC_GUARD) && !config_options.leave_chans
116 				&& c != NULL &&
117 				(si->su == NULL || !chanuser_find(c, si->su)))
118 		{
119 			/* Always cycle it if the requester is not on channel
120 			 * -- jilles */
121 			part(channel, chansvs.nick);
122 		}
123 		/* could be permanent channel, blah */
124 		c = channel_find(channel);
125 		if (c != NULL)
126 		{
127 			if (oldlimit == 0)
128 				modestack_mode_limit(chansvs.nick, c, MTYPE_DEL, 0);
129 			else if (oldlimit != 1)
130 				modestack_mode_limit(chansvs.nick, c, MTYPE_ADD, oldlimit);
131 		}
132 	}
133 
134 	logcommand(si, CMDLOG_DO, "CLEAR:USERS: \2%s\2", mc->name);
135 
136 	command_success_nodata(si, _("Cleared users from \2%s\2."), channel);
137 }
138 
139 /* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
140  * vim:ts=8
141  * vim:sw=8
142  * vim:noexpandtab
143  */
144