source: code/cmd.c@ 940d2b5

Last change on this file since 940d2b5 was 7ff6405, checked in by Mike Belopuhov <mike@…>, 10 years ago

Better error messages for invalid groups

  • Property mode set to 100644
File size: 10.1 KB
Line 
1/*
2 * Copyright (c) 2009, 2010, 2013 Mike Belopuhov
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include <sys/types.h>
18#include <sys/queue.h>
19#include <netdb.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23#include <syslog.h>
24#include <unistd.h>
25#include <vis.h>
26#include <event.h>
27
28#include "icb.h"
29#include "icbd.h"
30
31extern int creategroups;
32
33void icb_cmd_help(struct icb_session *, char *);
34void icb_cmd_beep(struct icb_session *, char *);
35void icb_cmd_boot(struct icb_session *, char *);
36void icb_cmd_change(struct icb_session *, char *);
37void icb_cmd_name(struct icb_session *, char *);
38void icb_cmd_nobeep(struct icb_session *, char *);
39void icb_cmd_personal(struct icb_session *, char *);
40void icb_cmd_pass(struct icb_session *, char *);
41void icb_cmd_topic(struct icb_session *, char *);
42void icb_cmd_who(struct icb_session *, char *);
43
44void *
45icb_cmd_lookup(char *cmd)
46{
47 struct {
48 const char *cmd;
49 void (*handler)(struct icb_session *, char *);
50 } cmdtab[] = {
51 { "?", icb_cmd_help },
52 { "beep", icb_cmd_beep },
53 { "boot", icb_cmd_boot },
54 { "g", icb_cmd_change },
55 { "m", icb_cmd_personal },
56 { "msg", icb_cmd_personal },
57 { "name", icb_cmd_name },
58 { "nobeep", icb_cmd_nobeep },
59 { "pass", icb_cmd_pass },
60 { "topic", icb_cmd_topic },
61 { "w", icb_cmd_who },
62 { NULL, NULL }
63 };
64 int i;
65
66 for (i = 0; cmdtab[i].cmd != NULL; i++)
67 if (strcasecmp(cmdtab[i].cmd, cmd) == 0)
68 return (cmdtab[i].handler);
69 return (NULL);
70}
71
72void
73icb_cmd_help(struct icb_session *is, char *arg __attribute__((unused)))
74{
75 icb_status(is, STATUS_HELP, "Server supports following commands:");
76 icb_status(is, STATUS_HELP, "beep boot g m name nobeep pass topic");
77}
78
79void
80icb_cmd_beep(struct icb_session *is, char *arg)
81{
82 struct icb_group *ig = is->group;
83 struct icb_session *s;
84 char whom[ICB_MAXNICKLEN];
85
86 if (strlen(arg) == 0) {
87 icb_error(is, "Invalid user");
88 return;
89 }
90
91 icb_vis(whom, arg, ICB_MAXNICKLEN, VIS_SP);
92
93 LIST_FOREACH(s, &ig->sess, entry) {
94 if (strcmp(s->nick, whom) == 0)
95 break;
96 }
97 if (s == NULL) {
98 icb_status(is, STATUS_NOTIFY, "%s is not signed on", whom);
99 return;
100 }
101
102 if (ISSETF(s->flags, ICB_SF_NOBEEP | ICB_SF_NOBEEP2)) {
103 icb_error(is, "User has nobeep enabled");
104 if (ISSETF(s->flags, ICB_SF_NOBEEP2))
105 icb_status(s, STATUS_NOBEEP,
106 "%s attempted to beep you", is->nick);
107 return;
108 }
109
110 icb_sendfmt(s, "%c%s", ICB_M_BEEP, is->nick);
111}
112
113void
114icb_cmd_boot(struct icb_session *is, char *arg)
115{
116 struct icb_group *ig;
117 struct icb_session *s;
118 char whom[ICB_MAXNICKLEN];
119
120 /* to boot or not to boot, that is the question */
121 ig = is->group;
122 if (!icb_ismod(ig, is)) {
123 icb_status(is, STATUS_NOTIFY, "Sorry, booting is a privilege "
124 "you don't possess");
125 return;
126 }
127
128 if (strlen(arg) == 0) {
129 icb_error(is, "Invalid user");
130 return;
131 }
132
133 icb_vis(whom, arg, ICB_MAXNICKLEN, VIS_SP);
134
135 /* who would be a target then? */
136 LIST_FOREACH(s, &ig->sess, entry) {
137 if (strcmp(s->nick, whom) == 0)
138 break;
139 }
140 if (s == NULL) {
141 icb_status(is, STATUS_NOTIFY, "No such user");
142 return;
143 }
144 if (s == is) {
145 icb_error(is, "Just quit, would you?");
146 return;
147 }
148
149 /* okay, here we go, but first, be polite and notify a user */
150 icb_status(s, STATUS_BOOT, "%s booted you", is->nick);
151 icb_status_group(s->group, s, STATUS_BOOT, "%s was booted", s->nick);
152 icbd_drop(s, "booted");
153}
154
155void
156icb_cmd_change(struct icb_session *is, char *arg)
157{
158 struct icb_group *ig;
159 struct icb_session *s;
160 char group[ICB_MAXGRPLEN];
161 int changing = 0;
162
163 if (strlen(arg) == 0) {
164 icb_error(is, "Invalid group name");
165 return;
166 }
167
168 icb_vis(group, arg, ICB_MAXGRPLEN, VIS_SP);
169
170 LIST_FOREACH(ig, &groups, entry) {
171 if (strcmp(ig->name, group) == 0)
172 break;
173 }
174 if (ig == NULL) {
175 if (!creategroups) {
176 icb_error(is, "Can't create new groups");
177 return;
178 } else {
179 if ((ig = icb_addgroup(is, group)) == NULL) {
180 icb_error(is, "Can't create group %s", group);
181 return;
182 }
183 icbd_log(NULL, LOG_DEBUG, "%s created group %s",
184 is->nick, group);
185 }
186 }
187
188 /* see if we're changing to the same group */
189 if (is->group && is->group == ig) {
190 icb_error(is, "You are already in that group");
191 return;
192 }
193
194 LIST_FOREACH(s, &ig->sess, entry) {
195 if (strcmp(s->nick, is->nick) == 0) {
196 icb_error(is, "Nick is already in use");
197 return;
198 }
199 }
200
201 if (is->group) {
202 changing = 1;
203 if (icb_ismod(is->group, is))
204 (void)icb_pass(is->group, is, NULL);
205 LIST_REMOVE(is, entry);
206 icb_status_group(is->group, NULL, STATUS_DEPART,
207 "%s (%s@%s) just left", is->nick, is->client, is->host);
208 }
209
210 is->group = ig;
211 LIST_INSERT_HEAD(&ig->sess, is, entry);
212
213 /* notify group */
214 icb_status_group(ig, is, changing ? STATUS_ARRIVE : STATUS_SIGNON,
215 "%s (%s@%s) entered group", is->nick, is->client, is->host);
216
217 /* acknowledge successful join */
218 icb_status(is, STATUS_STATUS, "You are now in group %s%s", ig->name,
219 icb_ismod(ig, is) ? " as moderator" : "");
220
221 /* send user a topic name */
222 if (strlen(ig->topic) > 0)
223 icb_status(is, STATUS_TOPIC, "The topic is: %s", ig->topic);
224}
225
226void
227icb_cmd_name(struct icb_session *is, char *arg)
228{
229 struct icb_group *ig = is->group;
230 struct icb_session *s;
231 char nick[ICB_MAXNICKLEN];
232
233 if (strlen(arg) == 0) {
234 icb_status(is, STATUS_NAME, "Your nickname is %s",
235 is->nick);
236 return;
237 }
238 if (strcasecmp(arg, "admin") == 0) {
239 icb_error(is, "Wuff wuff!");
240 return;
241 }
242 /* sanitize user input */
243 if (strlen(arg) > ICB_MAXNICKLEN)
244 arg[ICB_MAXNICKLEN - 1] = '\0';
245 icb_vis(nick, arg, ICB_MAXNICKLEN, VIS_SP);
246 LIST_FOREACH(s, &ig->sess, entry) {
247 if (strcmp(s->nick, nick) == 0) {
248 icb_error(is, "Nick is already in use");
249 return;
250 }
251 }
252 icb_status_group(ig, NULL, STATUS_NAME,
253 "%s changed nickname to %s", is->nick, nick);
254 strlcpy(is->nick, nick, sizeof is->nick);
255}
256
257void
258icb_cmd_nobeep(struct icb_session *is, char *arg)
259{
260 if (strlen(arg) == 0) {
261 /* fail if we have verbose turned on */
262 if (ISSETF(is->flags, ICB_SF_NOBEEP2)) {
263 icb_error(is, "Can't toggle your nobeep status");
264 return;
265 }
266 /* otherwise toggle the status */
267 if (ISSETF(is->flags, ICB_SF_NOBEEP))
268 CLRF(is->flags, ICB_SF_NOBEEP);
269 else
270 SETF(is->flags, ICB_SF_NOBEEP);
271 icb_status(is, STATUS_NOBEEP, "No-Beep %s",
272 ISSETF(is->flags, ICB_SF_NOBEEP) ? "on" : "off");
273 return;
274 }
275
276 if (strcmp(arg, "on") == 0) {
277 SETF(is->flags, ICB_SF_NOBEEP);
278 CLRF(is->flags, ICB_SF_NOBEEP2); /* can't be on and verbose */
279 icb_status(is, STATUS_NOBEEP, "No-Beep on");
280 } else if (strcmp(arg, "verbose") == 0) {
281 SETF(is->flags, ICB_SF_NOBEEP2);
282 CLRF(is->flags, ICB_SF_NOBEEP); /* can't be on and verbose */
283 icb_status(is, STATUS_NOBEEP, "No-Beep on (verbose)");
284 } else if (strcmp(arg, "off") == 0) {
285 CLRF(is->flags, ICB_SF_NOBEEP | ICB_SF_NOBEEP2);
286 icb_status(is, STATUS_NOBEEP, "No-Beep off");
287 } else
288 icb_error(is, "Invalid nobeep mode");
289}
290
291void
292icb_cmd_personal(struct icb_session *is, char *arg)
293{
294 char *p;
295
296 if ((p = strchr(arg, ' ')) == 0) {
297 icb_error(is, "Empty message");
298 return;
299 }
300 *p = '\0';
301 icb_privmsg(is, arg, ++p);
302}
303
304void
305icb_cmd_pass(struct icb_session *is, char *arg)
306{
307 struct icb_group *ig = is->group;
308 struct icb_session *s;
309 char whom[ICB_MAXNICKLEN];
310
311 if (!ig->mod) { /* if there is no mod, try grabbing it */
312 if (icb_modpermit(is, 0) && icb_pass(ig, ig->mod, is) < 0)
313 icb_error(is, "Acquiring group moderation failed.");
314 } else if (icb_ismod(ig, is)) {
315 if (strlen(arg) == 0) {
316 /* no argument: relinquish moderator */
317 (void)icb_pass(ig, ig->mod, NULL);
318 return;
319 }
320 icb_vis(whom, arg, ICB_MAXNICKLEN, VIS_SP);
321 LIST_FOREACH(s, &ig->sess, entry) {
322 if (strcmp(s->nick, whom) == 0)
323 break;
324 }
325 if (s == NULL) {
326 icb_status(is, STATUS_NOTIFY, "No such user");
327 return;
328 }
329 if (icb_modpermit(s, 0) && icb_pass(ig, ig->mod, s) < 0)
330 icb_error(s, "Acquiring group moderation failed.");
331 } else {
332 /*
333 * if group is moderated and we're not the moderator,
334 * but modtab is enabled, then check the permission
335 * and pass moderation if successful.
336 */
337 if (icb_modpermit(is, 1) && icb_pass(ig, ig->mod, is) < 0)
338 icb_error(is, "Acquiring group moderation failed.");
339 }
340}
341
342void
343icb_cmd_topic(struct icb_session *is, char *arg)
344{
345 struct icb_group *ig = is->group;
346 char topic[ICB_MAXTOPICLEN];
347
348 if (strlen(arg) == 0) { /* querying the topic */
349 if (strlen(ig->topic) > 0)
350 icb_status(is, STATUS_TOPIC, "The topic is: %s",
351 ig->topic);
352 else
353 icb_status(is, STATUS_TOPIC, "The topic is not set.");
354 } else { /* setting the topic */
355 if (!icb_ismod(ig, is)) {
356 icb_status(is, STATUS_NOTIFY, "Setting the topic is "
357 "only for moderators.");
358 return;
359 }
360 icb_vis(topic, arg, ICB_MAXTOPICLEN, 0);
361 strlcpy(ig->topic, topic, sizeof ig->topic);
362 icb_status_group(ig, NULL, STATUS_TOPIC,
363 "%s changed the topic to \"%s\"", is->nick, ig->topic);
364 }
365}
366
367void
368icb_cmd_who(struct icb_session *is, char *arg)
369{
370 struct icb_group *ig;
371 char group[ICB_MAXGRPLEN];
372
373 while (strlen(arg) && arg[0] == '-') { /* ignore options, for now */
374 /* ircII "set SHOW_CHANNEL_NAMES ON" uses /w -s */
375 while(arg[0] != ' ' && arg[0] != 0)
376 arg++;
377 if(arg[0] == ' ')
378 arg++;
379 }
380
381 if (strlen(arg) == 0)
382 return icb_who(is, NULL);
383
384 /* pidgin-icb treats '.' as the current group */
385 if (strlen(arg) == 1 && arg[0] == '.') {
386 icb_who(is, is->group);
387 return;
388 }
389
390 icb_vis(group, arg, ICB_MAXGRPLEN, VIS_SP);
391 LIST_FOREACH(ig, &groups, entry) {
392 if (strcmp(ig->name, group) == 0)
393 break;
394 }
395 if (ig == NULL) {
396 icb_error(is, "The group %s doesn't exist.", group);
397 return;
398 }
399 icb_who(is, ig);
400}
Note: See TracBrowser for help on using the repository browser.