1 /*
2 * atheme-services: A collection of minimalist IRC services
3 * commandtree.c: Management of services commands.
4 *
5 * Copyright (c) 2005-2010 Atheme Project (http://www.atheme.org)
6 *
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
12 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
13 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
14 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
15 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
16 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
17 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
18 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
19 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
20 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
21 * POSSIBILITY OF SUCH DAMAGE.
22 */
23
24 #include "atheme.h"
25 #include "privs.h"
26
27 static int text_to_parv(char *text, int maxparc, char **parv);
28
command_add(command_t * cmd,mowgli_patricia_t * commandtree)29 void command_add(command_t *cmd, mowgli_patricia_t *commandtree)
30 {
31 return_if_fail(cmd != NULL);
32 return_if_fail(commandtree != NULL);
33
34 mowgli_patricia_add(commandtree, cmd->name, cmd);
35 }
36
command_delete(command_t * cmd,mowgli_patricia_t * commandtree)37 void command_delete(command_t *cmd, mowgli_patricia_t *commandtree)
38 {
39 return_if_fail(cmd != NULL);
40 return_if_fail(commandtree != NULL);
41
42 mowgli_patricia_delete(commandtree, cmd->name);
43 }
44
command_find(mowgli_patricia_t * commandtree,const char * command)45 command_t *command_find(mowgli_patricia_t *commandtree, const char *command)
46 {
47 return_val_if_fail(commandtree != NULL, NULL);
48 return_val_if_fail(command != NULL, NULL);
49
50 return mowgli_patricia_retrieve(commandtree, command);
51 }
52
53 static bool permissive_mode_fallback = false;
default_command_authorize(service_t * svs,sourceinfo_t * si,command_t * c,const char * userlevel)54 static bool default_command_authorize(service_t *svs, sourceinfo_t *si, command_t *c, const char *userlevel)
55 {
56 if (!(has_priv(si, c->access) && has_priv(si, userlevel)))
57 {
58 if (!permissive_mode_fallback)
59 logaudit_denycmd(si, c, userlevel);
60 return false;
61 }
62
63 return true;
64 }
65 bool (*command_authorize)(service_t *svs, sourceinfo_t *si, command_t *c, const char *userlevel) = default_command_authorize;
66
command_verify(service_t * svs,sourceinfo_t * si,command_t * c,const char * userlevel)67 static inline bool command_verify(service_t *svs, sourceinfo_t *si, command_t *c, const char *userlevel)
68 {
69 if (command_authorize(svs, si, c, userlevel))
70 return true;
71
72 if (permissive_mode)
73 {
74 bool ret;
75
76 permissive_mode_fallback = true;
77 ret = default_command_authorize(svs, si, c, userlevel);
78 permissive_mode_fallback = false;
79
80 return ret;
81 }
82
83 return false;
84 }
85
command_exec(service_t * svs,sourceinfo_t * si,command_t * c,int parc,char * parv[])86 void command_exec(service_t *svs, sourceinfo_t *si, command_t *c, int parc, char *parv[])
87 {
88 const char *cmdaccess;
89
90 if (si->smu != NULL)
91 language_set_active(si->smu->language);
92
93 /* Make this look a bit more expected for normal users */
94 if (si->smu == NULL && c->access != NULL && !strcasecmp(c->access, AC_AUTHENTICATED))
95 {
96 command_fail(si, fault_noprivs, _("You are not logged in."));
97 language_set_active(NULL);
98 return;
99 }
100
101 cmdaccess = service_set_access(svs, c->name, c->access);
102
103 if (command_verify(svs, si, c, cmdaccess))
104 {
105 if (si->force_language != NULL)
106 language_set_active(si->force_language);
107
108 si->command = c;
109 c->cmd(si, parc, parv);
110 language_set_active(NULL);
111 return;
112 }
113
114 if (has_any_privs(si))
115 {
116 char accessbuf[BUFSIZE];
117
118 *accessbuf = '\0';
119
120 if (c->access && (!cmdaccess || strcmp(c->access, cmdaccess)))
121 {
122 mowgli_strlcat(accessbuf, c->access, BUFSIZE);
123 mowgli_strlcat(accessbuf, " ", BUFSIZE);
124 }
125
126 mowgli_strlcat(accessbuf, cmdaccess, BUFSIZE);
127
128 command_fail(si, fault_noprivs, STR_NO_PRIVILEGE, accessbuf);
129 }
130 else
131 command_fail(si, fault_noprivs, _("You are not authorized to perform this operation."));
132 /* logcommand(si, CMDLOG_ADMIN, "DENIED COMMAND: \2%s\2 used \2%s\2 \2%s\2", origin, svs->name, cmd); */
133 if (si->smu != NULL)
134 language_set_active(NULL);
135 }
136
command_exec_split(service_t * svs,sourceinfo_t * si,const char * cmd,char * text,mowgli_patricia_t * commandtree)137 void command_exec_split(service_t *svs, sourceinfo_t *si, const char *cmd, char *text, mowgli_patricia_t *commandtree)
138 {
139 int parc, i;
140 char *parv[20];
141 command_t *c;
142
143 cmd = service_resolve_alias(svs, commandtree == svs->commands ? NULL : "unknown", cmd);
144 if ((c = command_find(commandtree, cmd)))
145 {
146 parc = text_to_parv(text, c->maxparc, parv);
147 for (i = parc; i < (int)(sizeof(parv) / sizeof(parv[0])); i++)
148 parv[i] = NULL;
149 command_exec(svs, si, c, parc, parv);
150 }
151 else
152 {
153 if (si->smu != NULL)
154 language_set_active(si->smu->language);
155
156 notice(svs->nick, si->su->nick, _("Invalid command. Use \2/%s%s help\2 for a command listing."), (ircd->uses_rcommand == false) ? "msg " : "", svs->disp);
157
158 if (si->smu != NULL)
159 language_set_active(NULL);
160 }
161 }
162
163 /*
164 * command_help
165 * Iterates the command tree and lists available commands.
166 *
167 * inputs -
168 * si: The origin of the request.
169 * commandtree: The command tree being listed.
170 *
171 * outputs -
172 * A list of available commands.
173 */
command_help(sourceinfo_t * si,mowgli_patricia_t * commandtree)174 void command_help(sourceinfo_t *si, mowgli_patricia_t *commandtree)
175 {
176 mowgli_patricia_iteration_state_t state;
177 command_t *c;
178
179 if (si->service == NULL || si->service->commands == commandtree)
180 command_success_nodata(si, _("The following commands are available:"));
181 else
182 command_success_nodata(si, _("The following subcommands are available:"));
183
184 MOWGLI_PATRICIA_FOREACH(c, &state, commandtree)
185 {
186 /* show only the commands we have access to
187 * (taken from command_exec())
188 */
189 if (has_priv(si, c->access) || (c->access != NULL && !strcasecmp(c->access, AC_AUTHENTICATED) && si->smu != NULL))
190 command_success_nodata(si, "\2%-15s\2 %s", c->name, translation_get(_(c->desc)));
191 }
192 }
193
194 /* name1 name2 name3... */
string_in_list(const char * str,const char * name)195 static bool string_in_list(const char *str, const char *name)
196 {
197 char *p;
198 int l;
199
200 if (str == NULL)
201 return false;
202 l = strlen(name);
203 while (*str != '\0')
204 {
205 p = strchr(str, ' ');
206 if (p != NULL ? p - str == l && !strncasecmp(str, name, p - str) : !strcasecmp(str, name))
207 return true;
208 if (p == NULL)
209 return false;
210 str = p;
211 while (*str == ' ')
212 str++;
213 }
214 return false;
215 }
216
217 /*
218 * command_help_short
219 * Iterates over the command tree and lists available commands.
220 *
221 * inputs -
222 * mynick: The nick of the services bot sending out the notices.
223 * origin: The origin of the request.
224 * commandtree: The command tree being listed.
225 * maincmds: The commands to list verbosely.
226 *
227 * outputs -
228 * A list of available commands.
229 */
command_help_short(sourceinfo_t * si,mowgli_patricia_t * commandtree,const char * maincmds)230 void command_help_short(sourceinfo_t *si, mowgli_patricia_t *commandtree, const char *maincmds)
231 {
232 mowgli_patricia_iteration_state_t state;
233 unsigned int l, lv;
234 char buf[256], *p;
235 command_t *c;
236
237 if (si->service == NULL || si->service->commands == commandtree)
238 command_success_nodata(si, _("The following commands are available:"));
239 else
240 command_success_nodata(si, _("The following subcommands are available:"));
241
242 MOWGLI_PATRICIA_FOREACH(c, &state, commandtree)
243 {
244 /* show only the commands we have access to
245 * (taken from command_exec())
246 */
247 if (string_in_list(maincmds, c->name) && (has_priv(si, c->access) || (c->access != NULL && !strcasecmp(c->access, AC_AUTHENTICATED) && si->smu != NULL)))
248 command_success_nodata(si, "\2%-15s\2 %s", c->name, translation_get(_(c->desc)));
249 }
250
251 command_success_nodata(si, " ");
252 mowgli_strlcpy(buf, translation_get(_("\2Other commands:\2 ")), sizeof buf);
253 l = strlen(buf);
254 lv = 0;
255 for (p = buf; *p != '\0'; p++)
256 {
257 if (!(*p >= '\1' && *p < ' '))
258 lv++;
259 }
260
261 MOWGLI_PATRICIA_FOREACH(c, &state, commandtree)
262 {
263 /* show only the commands we have access to
264 * (taken from command_exec())
265 */
266 if (!string_in_list(maincmds, c->name) && (has_priv(si, c->access) || (c->access != NULL && !strcasecmp(c->access, AC_AUTHENTICATED) && si->smu != NULL)))
267 {
268 if (strlen(buf) > l)
269 mowgli_strlcat(buf, ", ", sizeof buf);
270 if (strlen(buf) > 55)
271 {
272 command_success_nodata(si, "%s", buf);
273 l = lv;
274 buf[lv] = '\0';
275 while (--lv > 0)
276 buf[lv] = ' ';
277 buf[0] = ' ';
278 lv = l;
279 }
280 mowgli_strlcat(buf, c->name, sizeof buf);
281 }
282 }
283 if (strlen(buf) > l)
284 command_success_nodata(si, "%s", buf);
285 }
286
text_to_parv(char * text,int maxparc,char ** parv)287 static int text_to_parv(char *text, int maxparc, char **parv)
288 {
289 int count = 0;
290 char *p;
291
292 if (maxparc == 0)
293 return 0;
294
295 if (!text)
296 return 0;
297
298 p = text;
299 while (count < maxparc - 1 && (parv[count] = strtok(p, " ")) != NULL)
300 count++, p = NULL;
301
302 if ((parv[count] = strtok(p, "")) != NULL)
303 {
304 p = parv[count];
305 while (*p == ' ')
306 p++;
307 parv[count] = p;
308 if (*p != '\0')
309 {
310 p += strlen(p) - 1;
311 while (*p == ' ' && p > parv[count])
312 p--;
313 p[1] = '\0';
314 count++;
315 }
316 }
317 return count;
318 }
319
320 /* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
321 * vim:ts=8
322 * vim:sw=8
323 * vim:noexpandtab
324 */
325