source: code/cmd.c@ e80f9fc

Last change on this file since e80f9fc was c102bbf, checked in by Florian Obser <florian@…>, 11 years ago

Make pidgin-icb /who and /msg work for real.

pidgin-icb seems to treat "." as the current group, so return the who
output for the group the icb session is connected to.
Turns out the previous commit doesn't fix anything - the tests were
wrong, so revert that.

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