1 /*
2  * Copyright (c) 2007 Jilles Tjoelker, et al.
3  * Rights to this code are as documented in doc/LICENSE.
4  *
5  * ChanServ ACCESS command
6  *
7  * $Id$
8  */
9 
10 #include "atheme-compat.h"
11 #include "template.h"
12 
13 DECLARE_MODULE_V1
14 (
15 	"contrib/cs_access_alias", FALSE, _modinit, _moddeinit,
16 	"$Id$",
17 	"freenode <http://www.freenode.net>"
18 );
19 
20 static void cs_cmd_access(sourceinfo_t *si, int parc, char *parv[]);
21 
22 command_t cs_access = { "ACCESS", "Manipulates channel access lists.",
23                          AC_NONE, 4, cs_cmd_access, { .path = "contrib/access" } };
24 
_modinit(module_t * m)25 void _modinit(module_t *m)
26 {
27 	service_named_bind_command("chanserv", &cs_access);
28 }
29 
_moddeinit(module_unload_intent_t intent)30 void _moddeinit(module_unload_intent_t intent)
31 {
32 	service_named_unbind_command("chanserv", &cs_access);
33 }
34 
compat_cmd(sourceinfo_t * si,const char * cmdname,char * channel,char * arg1,char * arg2,char * arg3)35 static void compat_cmd(sourceinfo_t *si, const char *cmdname, char *channel, char *arg1, char *arg2, char *arg3)
36 {
37 	int newparc;
38 	char *newparv[5];
39 	command_t *cmd;
40 
41 	newparv[0] = channel;
42 	newparv[1] = arg1;
43 	newparv[2] = arg2;
44 	newparv[3] = arg3;
45 	newparv[4] = NULL;
46 	/* this assumes arg3!=NULL implies arg2!=NULL implies arg1!=NULL */
47 	newparc = 1 + (arg1 != NULL) + (arg2 != NULL) + (arg3 != NULL);
48 	cmd = command_find(si->service->commands, cmdname);
49 	if (cmd != NULL)
50 		command_exec(si->service, si, cmd, newparc, newparv);
51 	else
52 		command_fail(si, fault_unimplemented, _("Command \2%s\2 not loaded?"), cmdname);
53 }
54 
55 typedef struct {
56 	const char *res;
57 	unsigned int level;
58 } template_iter_t;
59 
global_template_search(const char * key,void * data,void * privdata)60 static int global_template_search(const char *key, void *data, void *privdata)
61 {
62 	template_iter_t *iter = privdata;
63 	default_template_t *def_t = data;
64 
65 	if (def_t->flags == iter->level)
66 		iter->res = key;
67 
68 	return 0;
69 }
70 
get_template_name(mychan_t * mc,unsigned int level)71 static const char *get_template_name(mychan_t *mc, unsigned int level)
72 {
73 	metadata_t *md;
74 	const char *p, *q, *r;
75 	char *s;
76 	char ss[40];
77 	static char flagname[400];
78 	template_iter_t iter;
79 
80 	md = metadata_find(mc, "private:templates");
81 	if (md != NULL)
82 	{
83 		p = md->value;
84 		while (p != NULL)
85 		{
86 			while (*p == ' ')
87 				p++;
88 			q = strchr(p, '=');
89 			if (q == NULL)
90 				break;
91 			r = strchr(q, ' ');
92 			if (r != NULL && r < q)
93 				break;
94 			mowgli_strlcpy(ss, q, sizeof ss);
95 			if (r != NULL && r - q < (int)(sizeof ss - 1))
96 			{
97 				ss[r - q] = '\0';
98 			}
99 			if (level == flags_to_bitmask(ss, 0))
100 			{
101 				mowgli_strlcpy(flagname, p, sizeof flagname);
102 				s = strchr(flagname, '=');
103 				if (s != NULL)
104 					*s = '\0';
105 				return flagname;
106 			}
107 			p = r;
108 		}
109 	}
110 
111 	iter.res = NULL;
112 	iter.level = level;
113 	mowgli_patricia_foreach(global_template_dict, global_template_search, &iter);
114 
115 	return iter.res;
116 }
117 
access_list(sourceinfo_t * si,mychan_t * mc,int parc,char * parv[])118 static void access_list(sourceinfo_t *si, mychan_t *mc, int parc, char *parv[])
119 {
120 	mowgli_node_t *n;
121 	chanacs_t *ca;
122 	const char *str1, *str2;
123 	int i = 1;
124 	bool operoverride = false;
125 
126 	/* Copied from modules/chanserv/flags.c */
127 	/* Note: This overrides the normal need of +A access unless private */
128 	if (use_channel_private && mc->flags & MC_PRIVATE &&
129 			!chanacs_source_has_flag(mc, si, CA_ACLVIEW))
130 	{
131 		if (has_priv(si, PRIV_CHAN_AUSPEX))
132 			operoverride = true;
133 		else
134 		{
135 			command_fail(si, fault_noprivs, _("You are not authorized to perform this operation."));
136 			return;
137 		}
138 	}
139 
140 	command_success_nodata(si, _("Entry Nickname/Host          Flags"));
141 	command_success_nodata(si, "----- ---------------------- -----");
142 
143 	MOWGLI_ITER_FOREACH(n, mc->chanacs.head)
144 	{
145 		ca = n->data;
146 		/* Change: don't show akicks */
147 		if (ca->level == CA_AKICK)
148 			continue;
149 		str1 = get_template_name(mc, ca->level);
150 		str2 = ca->tmodified ? time_ago(ca->tmodified) : "?";
151 		if (str1 != NULL)
152 			command_success_nodata(si, _("%-5d %-22s %s (%s) [modified %s ago]"), i, ca->entity ? ca->entity->name : ca->host, bitmask_to_flags(ca->level), str1,
153 				str2);
154 		else
155 			command_success_nodata(si, _("%-5d %-22s %s [modified %s ago]"), i, ca->entity ? ca->entity->name : ca->host, bitmask_to_flags(ca->level),
156 				str2);
157 		i++;
158 	}
159 
160 	command_success_nodata(si, "----- ---------------------- -----");
161 	command_success_nodata(si, _("End of \2%s\2 FLAGS listing."), mc->name);
162 
163 	if (operoverride)
164 		logcommand(si, CMDLOG_ADMIN, "%s ACCESS LIST (oper override)", mc->name);
165 	else
166 		logcommand(si, CMDLOG_GET, "%s ACCESS LIST", mc->name);
167 }
168 
cs_cmd_access(sourceinfo_t * si,int parc,char * parv[])169 static void cs_cmd_access(sourceinfo_t *si, int parc, char *parv[])
170 {
171 	char *chan, *cmd;
172 	mychan_t *mc;
173 	char killit[] = "-*";
174 	char deftemplate[] = "OP";
175 	char defaccess[] = "=votirA";
176 
177 	if (parc < 2)
178 	{
179 		command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "ACCESS");
180 		command_fail(si, fault_needmoreparams, _("Syntax: ACCESS <#channel> ADD|DEL|LIST [nick] [level]"));
181 		return;
182 	}
183 	if (parv[0][0] == '#')
184 		chan = parv[0], cmd = parv[1];
185 	else if (parv[1][0] == '#')
186 		cmd = parv[0], chan = parv[1];
187 	else
188 	{
189 		command_fail(si, fault_badparams, STR_INVALID_PARAMS, "ACCESS");
190 		command_fail(si, fault_badparams, _("Syntax: ACCESS <#channel> ADD|DEL|LIST [nick] [level]"));
191 		return;
192 	}
193 
194 	mc = mychan_find(chan);
195 	if (mc == NULL)
196 	{
197 		command_fail(si, fault_nosuch_target, _("\2%s\2 is not registered."), chan);
198 		return;
199 	}
200 
201 	if (!strcasecmp(cmd, "LIST"))
202 		access_list(si, mc, parc - 2, parv + 2);
203 	else if (parc < 3)
204 	{
205 		command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "ACCESS");
206 		command_fail(si, fault_needmoreparams, _("Syntax: ACCESS <#channel> ADD|DEL <nick> [level]"));
207 		return;
208 	}
209 	else if (!strcasecmp(cmd, "ADD"))
210 		compat_cmd(si, "FLAGS", chan, parv[2], parc > 3 ? parv[3] : (get_template_flags(mc, deftemplate) ? deftemplate : defaccess), NULL);
211 	else if (!strcasecmp(cmd, "DEL"))
212 		compat_cmd(si, "FLAGS", chan, parv[2], killit, NULL);
213 	else
214 		command_fail(si, fault_badparams, _("Invalid command. Use \2/%s%s help\2 for a command listing."), (ircd->uses_rcommand == FALSE) ? "msg " : "", si->service->disp);
215 }
216