1 /*
2  * Copyright (c) 2005-2006 William Pitcock, et al.
3  * Rights to this code are as documented in doc/LICENSE.
4  *
5  * This file contains code for the ChanServ WHY function.
6  *
7  */
8 
9 #include "atheme.h"
10 
11 DECLARE_MODULE_V1
12 (
13 	"chanserv/why", false, _modinit, _moddeinit,
14 	PACKAGE_STRING,
15 	VENDOR_STRING
16 );
17 
18 static void cs_cmd_why(sourceinfo_t *si, int parc, char *parv[]);
19 
20 command_t cs_why = { "WHY", N_("Explains channel access logic."),
21 		     AC_NONE, 2, cs_cmd_why, { .path = "cservice/why" } };
22 
_modinit(module_t * m)23 void _modinit(module_t *m)
24 {
25 	service_named_bind_command("chanserv", &cs_why);
26 }
27 
_moddeinit(module_unload_intent_t intent)28 void _moddeinit(module_unload_intent_t intent)
29 {
30 	service_named_unbind_command("chanserv", &cs_why);
31 }
32 
cs_cmd_why(sourceinfo_t * si,int parc,char * parv[])33 static void cs_cmd_why(sourceinfo_t *si, int parc, char *parv[])
34 {
35 	const char *chan = parv[0];
36 	const char *targ = parv[1];
37 	mychan_t *mc;
38 	user_t *u;
39 	myuser_t *mu;
40 	mowgli_node_t *n;
41 	chanacs_t *ca;
42 	entity_chanacs_validation_vtable_t *vt;
43 	metadata_t *md;
44 	bool operoverride = false;
45 	int fl = 0;
46 
47 	if (!chan || (!targ && si->su == NULL))
48 	{
49 		command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "WHY");
50 		command_fail(si, fault_needmoreparams, _("Syntax: WHY <channel> [user]"));
51 		return;
52 	}
53 
54 	if (!targ)
55 		targ = si->su->nick;
56 
57 	mc = mychan_find(chan);
58 	u = user_find_named(targ);
59 
60 	if (u == NULL)
61 	{
62 		command_fail(si, fault_nosuch_target, _("\2%s\2 is not online."),
63 			targ);
64 		return;
65 	}
66 
67 	mu = u->myuser;
68 
69 	if (mc == NULL)
70 	{
71 		command_fail(si, fault_nosuch_target, _("Channel \2%s\2 is not registered."),
72 			chan);
73 		return;
74 	}
75 
76 	if (!(mc->flags & MC_PUBACL) && !chanacs_source_has_flag(mc, si, CA_ACLVIEW))
77 	{
78 		if (has_priv(si, PRIV_CHAN_AUSPEX))
79 			operoverride = true;
80 		else
81 		{
82 			command_fail(si, fault_noprivs, _("You are not authorized to perform this operation."));
83 			return;
84 		}
85 	}
86 
87 	if (metadata_find(mc, "private:close:closer"))
88 	{
89 		command_fail(si, fault_noprivs, _("\2%s\2 is closed."), chan);
90 		return;
91 	}
92 
93 	if (operoverride)
94 		logcommand(si, CMDLOG_ADMIN, "WHY: \2%s!%s@%s\2 on \2%s\2 (oper override)", u->nick, u->user, u->vhost, mc->name);
95 	else
96 		logcommand(si, CMDLOG_GET, "WHY: \2%s!%s@%s\2 on \2%s\2", u->nick, u->user, u->vhost, mc->name);
97 
98 	MOWGLI_ITER_FOREACH(n, mc->chanacs.head)
99 	{
100 		ca = (chanacs_t *)n->data;
101 
102 		if (ca->entity == NULL)
103 			continue;
104 		vt = myentity_get_chanacs_validator(ca->entity);
105 		if (ca->entity == entity(u->myuser) ||
106 				(vt->match_user != NULL ?
107 				 vt->match_user(ca, u) != NULL :
108 				 u->myuser != NULL &&
109 				 vt->match_entity(ca, entity(u->myuser)) != NULL))
110 		{
111 			fl |= ca->level;
112 			if (ca->entity == entity(u->myuser))
113 				command_success_nodata(si,
114 					"\2%s\2 has flags \2%s\2 in \2%s\2 because they are logged in as \2%s\2.",
115 					u->nick, bitmask_to_flags2(ca->level, 0), mc->name, ca->entity->name);
116 			else if (isgroup(ca->entity))
117 				command_success_nodata(si,
118 					"\2%s\2 has flags \2%s\2 in \2%s\2 because they are a member of \2%s\2.",
119 					u->nick, bitmask_to_flags2(ca->level, 0), mc->name, ca->entity->name);
120 			else
121 				command_success_nodata(si,
122 					"\2%s\2 has flags \2%s\2 in \2%s\2 because they match \2%s\2.",
123 					u->nick, bitmask_to_flags2(ca->level, 0), mc->name, ca->entity->name);
124 			if (ca->level & CA_AKICK)
125 			{
126 				md = metadata_find(ca, "reason");
127 				if (md != NULL)
128 					command_success_nodata(si, "Ban reason: %s", md->value);
129 			}
130 		}
131 	}
132 	for (n = next_matching_host_chanacs(mc, u, mc->chanacs.head); n != NULL; n = next_matching_host_chanacs(mc, u, n->next))
133 	{
134 		ca = n->data;
135 		fl |= ca->level;
136 		command_success_nodata(si,
137 				"\2%s\2 has flags \2%s\2 in \2%s\2 because they match the mask \2%s\2.",
138 				u->nick, bitmask_to_flags2(ca->level, 0), mc->name, ca->host);
139 		if (ca->level & CA_AKICK)
140 		{
141 			md = metadata_find(ca, "reason");
142 			if (md != NULL)
143 				command_success_nodata(si, "Ban reason: %s", md->value);
144 		}
145 	}
146 
147 	if (fl & (CA_AUTOOP | CA_AUTOHALFOP | CA_AUTOVOICE))
148 	{
149 		if (mc->flags & MC_NOOP)
150 			command_success_nodata(si, _("The \2%s\2 flag is set for \2%s\2, therefore no status will be given."),
151 					"NOOP",
152 					mc->name);
153 		else if (mu != NULL && mu->flags & MU_NOOP)
154 			command_success_nodata(si, _("The \2%s\2 flag is set for \2%s\2, therefore no status will be given."),
155 					"NOOP",
156 					entity(mu)->name);
157 	}
158 	if ((fl & (CA_AKICK | CA_EXEMPT)) == (CA_AKICK | CA_EXEMPT))
159 		command_success_nodata(si, _("+e exempts from +b."));
160 	else if (fl == 0)
161 		command_success_nodata(si, _("\2%s\2 has no special access to \2%s\2."),
162 				u->nick, mc->name);
163 }
164 
165 /* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
166  * vim:ts=8
167  * vim:sw=8
168  * vim:noexpandtab
169  */
170