source: irc_commands.c @ 0ef1c92

Last change on this file since 0ef1c92 was 0ef1c92, checked in by dequis <dx@…>, at 2015-09-11T02:31:10Z

Initial implementation of ircv3 capability negotiation

Mostly no-op for now. Puts registration on hold, supports the basic
commands, and NAKs everything

  • Property mode set to 100644
File size: 22.4 KB
Line 
1/********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
4  * Copyright 2002-2012 Wilmer van der Gaast and others                *
5  \********************************************************************/
6
7/* IRC commands                                                         */
8
9/*
10  This program is free software; you can redistribute it and/or modify
11  it under the terms of the GNU General Public License as published by
12  the Free Software Foundation; either version 2 of the License, or
13  (at your option) any later version.
14
15  This program is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  GNU General Public License for more details.
19
20  You should have received a copy of the GNU General Public License with
21  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
22  if not, write to the Free Software Foundation, Inc., 51 Franklin St.,
23  Fifth Floor, Boston, MA  02110-1301  USA
24*/
25
26#define BITLBEE_CORE
27#include "bitlbee.h"
28#include "help.h"
29#include "ipc.h"
30
31static void irc_cmd_cap(irc_t *irc, char **cmd)
32{
33        if (!(irc->status & USTATUS_LOGGED_IN)) {
34                /* Put registration on hold until CAP END */
35                irc->status |= USTATUS_CAP_PENDING;
36        }
37
38        if (g_strcasecmp(cmd[1], "LS") == 0) {
39                /* gboolean irc302 = (g_strcmp0(cmd[2], "302") == 0); */
40                irc_send_cap(irc, "LS", "");
41
42        } else if (g_strcasecmp(cmd[1], "LIST") == 0) {
43                irc_send_cap(irc, "LIST", "");
44
45        } else if (g_strcasecmp(cmd[1], "REQ") == 0) {
46                irc_send_cap(irc, "NAK", cmd[2] ? : "");
47
48        } else if (g_strcasecmp(cmd[1], "END") == 0) {
49                irc->status &= ~USTATUS_CAP_PENDING;
50                irc_check_login(irc);
51
52        } else {
53                irc_send_num(irc, 410, "%s :Invalid CAP command", cmd[1]);
54        }
55
56}
57
58static void irc_cmd_pass(irc_t *irc, char **cmd)
59{
60        if (irc->status & USTATUS_LOGGED_IN) {
61                char *send_cmd[] = { "identify", cmd[1], NULL };
62
63                /* We're already logged in, this client seems to send the PASS
64                   command last. (Possibly it won't send it at all if it turns
65                   out we don't require it, which will break this feature.)
66                   Try to identify using the given password. */
67                root_command(irc, send_cmd);
68                return;
69        }
70        /* Handling in pre-logged-in state, first see if this server is
71           password-protected: */
72        else if (global.conf->auth_pass &&
73                 (strncmp(global.conf->auth_pass, "md5:", 4) == 0 ?
74                  md5_verify_password(cmd[1], global.conf->auth_pass + 4) == 0 :
75                  strcmp(cmd[1], global.conf->auth_pass) == 0)) {
76                irc->status |= USTATUS_AUTHORIZED;
77                irc_check_login(irc);
78        } else if (global.conf->auth_pass) {
79                irc_send_num(irc, 464, ":Incorrect password");
80        } else {
81                /* Remember the password and try to identify after USER/NICK. */
82                irc_setpass(irc, cmd[1]);
83                irc_check_login(irc);
84        }
85}
86
87static void irc_cmd_user(irc_t *irc, char **cmd)
88{
89        irc->user->user = g_strdup(cmd[1]);
90        irc->user->fullname = g_strdup(cmd[4]);
91
92        irc_check_login(irc);
93}
94
95static void irc_cmd_nick(irc_t *irc, char **cmd)
96{
97        irc_user_t *iu;
98
99        if ((iu = irc_user_by_name(irc, cmd[1])) && iu != irc->user) {
100                irc_send_num(irc, 433, "%s :This nick is already in use", cmd[1]);
101        } else if (!nick_ok(NULL, cmd[1])) {
102                /* [SH] Invalid characters. */
103                irc_send_num(irc, 432, "%s :This nick contains invalid characters", cmd[1]);
104        } else if (irc->status & USTATUS_LOGGED_IN) {
105                /* WATCH OUT: iu from the first if reused here to check if the
106                   new nickname is the same (other than case, possibly). If it
107                   is, no need to reset identify-status. */
108                if ((irc->status & USTATUS_IDENTIFIED) && iu != irc->user) {
109                        irc_setpass(irc, NULL);
110                        irc->status &= ~USTATUS_IDENTIFIED;
111                        irc_umode_set(irc, "-R", 1);
112                        irc_rootmsg(irc, "Changing nicks resets your identify status. "
113                                    "Re-identify or register a new account if you want "
114                                    "your configuration to be saved. See \x02help "
115                                    "nick_changes\x02.");
116                }
117
118                if (strcmp(cmd[1], irc->user->nick) != 0) {
119                        irc_user_set_nick(irc->user, cmd[1]);
120                }
121        } else {
122                g_free(irc->user->nick);
123                irc->user->nick = g_strdup(cmd[1]);
124
125                irc_check_login(irc);
126        }
127}
128
129static void irc_cmd_quit(irc_t *irc, char **cmd)
130{
131        if (cmd[1] && *cmd[1]) {
132                irc_abort(irc, 0, "Quit: %s", cmd[1]);
133        } else {
134                irc_abort(irc, 0, "Leaving...");
135        }
136}
137
138static void irc_cmd_ping(irc_t *irc, char **cmd)
139{
140        irc_write(irc, ":%s PONG %s :%s", irc->root->host,
141                  irc->root->host, cmd[1] ? cmd[1] : irc->root->host);
142}
143
144static void irc_cmd_pong(irc_t *irc, char **cmd)
145{
146        /* We could check the value we get back from the user, but in
147           fact we don't care, we're just happy s/he's still alive. */
148        irc->last_pong = gettime();
149        irc->pinging = 0;
150}
151
152static void irc_cmd_join(irc_t *irc, char **cmd)
153{
154        char *comma, *s = cmd[1];
155
156        while (s) {
157                irc_channel_t *ic;
158
159                if ((comma = strchr(s, ','))) {
160                        *comma = '\0';
161                }
162
163                if ((ic = irc_channel_by_name(irc, s)) == NULL &&
164                    (ic = irc_channel_new(irc, s))) {
165                        if (strcmp(set_getstr(&ic->set, "type"), "control") != 0) {
166                                /* Autoconfiguration is for control channels only ATM. */
167                        } else if (bee_group_by_name(ic->irc->b, ic->name + 1, FALSE)) {
168                                set_setstr(&ic->set, "group", ic->name + 1);
169                                set_setstr(&ic->set, "fill_by", "group");
170                        } else if (set_setstr(&ic->set, "protocol", ic->name + 1)) {
171                                set_setstr(&ic->set, "fill_by", "protocol");
172                        } else if (set_setstr(&ic->set, "account", ic->name + 1)) {
173                                set_setstr(&ic->set, "fill_by", "account");
174                        } else {
175                                /* The set commands above will run this already,
176                                   but if we didn't hit any, we have to fill the
177                                   channel with the default population. */
178                                bee_irc_channel_update(ic->irc, ic, NULL);
179                        }
180                } else if (ic == NULL) {
181                        irc_send_num(irc, 479, "%s :Invalid channel name", s);
182                        goto next;
183                }
184
185                if (ic->flags & IRC_CHANNEL_JOINED) {
186                        /* Dude, you're already there...
187                           RFC doesn't have any reply for that though? */
188                        goto next;
189                }
190
191                if (ic->f->join && !ic->f->join(ic)) {
192                        /* The story is: FALSE either means the handler
193                           showed an error message, or is doing some work
194                           before the join should be confirmed. (In the
195                           latter case, the caller should take care of that
196                           confirmation.) TRUE means all's good, let the
197                           user join the channel right away. */
198                        goto next;
199                }
200
201                irc_channel_add_user(ic, irc->user);
202
203next:
204                if (comma) {
205                        s = comma + 1;
206                        *comma = ',';
207                } else {
208                        break;
209                }
210        }
211}
212
213static void irc_cmd_names(irc_t *irc, char **cmd)
214{
215        irc_channel_t *ic;
216
217        if (cmd[1] && (ic = irc_channel_by_name(irc, cmd[1]))) {
218                irc_send_names(ic);
219        }
220        /* With no args, we should show /names of all chans. Make the code
221           below work well if necessary.
222        else
223        {
224                GSList *l;
225
226                for( l = irc->channels; l; l = l->next )
227                        irc_send_names( l->data );
228        }
229        */
230}
231
232static void irc_cmd_part(irc_t *irc, char **cmd)
233{
234        irc_channel_t *ic;
235
236        if ((ic = irc_channel_by_name(irc, cmd[1])) == NULL) {
237                irc_send_num(irc, 403, "%s :No such channel", cmd[1]);
238        } else if (irc_channel_del_user(ic, irc->user, IRC_CDU_PART, cmd[2])) {
239                if (ic->f->part) {
240                        ic->f->part(ic, NULL);
241                }
242        } else {
243                irc_send_num(irc, 442, "%s :You're not on that channel", cmd[1]);
244        }
245}
246
247static void irc_cmd_whois(irc_t *irc, char **cmd)
248{
249        char *nick = cmd[1];
250        irc_user_t *iu = irc_user_by_name(irc, nick);
251
252        if (iu) {
253                irc_send_whois(iu);
254        } else {
255                irc_send_num(irc, 401, "%s :Nick does not exist", nick);
256        }
257}
258
259static void irc_cmd_whowas(irc_t *irc, char **cmd)
260{
261        /* For some reason irssi tries a whowas when whois fails. We can
262           ignore this, but then the user never gets a "user not found"
263           message from irssi which is a bit annoying. So just respond
264           with not-found and irssi users will get better error messages */
265
266        irc_send_num(irc, 406, "%s :Nick does not exist", cmd[1]);
267        irc_send_num(irc, 369, "%s :End of WHOWAS", cmd[1]);
268}
269
270static void irc_cmd_motd(irc_t *irc, char **cmd)
271{
272        irc_send_motd(irc);
273}
274
275static void irc_cmd_mode(irc_t *irc, char **cmd)
276{
277        if (irc_channel_name_ok(cmd[1])) {
278                irc_channel_t *ic;
279
280                if ((ic = irc_channel_by_name(irc, cmd[1])) == NULL) {
281                        irc_send_num(irc, 403, "%s :No such channel", cmd[1]);
282                } else if (cmd[2]) {
283                        if (*cmd[2] == '+' || *cmd[2] == '-') {
284                                irc_send_num(irc, 477, "%s :Can't change channel modes", cmd[1]);
285                        } else if (*cmd[2] == 'b') {
286                                irc_send_num(irc, 368, "%s :No bans possible", cmd[1]);
287                        }
288                } else {
289                        irc_send_num(irc, 324, "%s +%s", cmd[1], ic->mode);
290                }
291        } else {
292                if (nick_cmp(NULL, cmd[1], irc->user->nick) == 0) {
293                        if (cmd[2]) {
294                                irc_umode_set(irc, cmd[2], 0);
295                        } else {
296                                irc_send_num(irc, 221, "+%s", irc->umode);
297                        }
298                } else {
299                        irc_send_num(irc, 502, ":Don't touch their modes");
300                }
301        }
302}
303
304static void irc_cmd_who(irc_t *irc, char **cmd)
305{
306        char *channel = cmd[1];
307        irc_channel_t *ic;
308        irc_user_t *iu;
309
310        if (!channel || *channel == '0' || *channel == '*' || !*channel) {
311                irc_send_who(irc, irc->users, "**");
312        } else if ((ic = irc_channel_by_name(irc, channel))) {
313                irc_send_who(irc, ic->users, channel);
314        } else if ((iu = irc_user_by_name(irc, channel))) {
315                /* Tiny hack! */
316                GSList *l = g_slist_append(NULL, iu);
317                irc_send_who(irc, l, channel);
318                g_slist_free(l);
319        } else {
320                irc_send_num(irc, 403, "%s :No such channel", channel);
321        }
322}
323
324static void irc_cmd_privmsg(irc_t *irc, char **cmd)
325{
326        irc_channel_t *ic;
327        irc_user_t *iu;
328
329        if (!cmd[2]) {
330                irc_send_num(irc, 412, ":No text to send");
331                return;
332        }
333
334        /* Don't treat CTCP actions as real CTCPs, just convert them right now. */
335        if (g_strncasecmp(cmd[2], "\001ACTION", 7) == 0) {
336                cmd[2] += 4;
337                memcpy(cmd[2], "/me", 3);
338                if (cmd[2][strlen(cmd[2]) - 1] == '\001') {
339                        cmd[2][strlen(cmd[2]) - 1] = '\0';
340                }
341        }
342
343        if (irc_channel_name_ok(cmd[1]) &&
344            (ic = irc_channel_by_name(irc, cmd[1]))) {
345                if (cmd[2][0] == '\001') {
346                        /* CTCPs to channels? Nah. Maybe later. */
347                } else if (ic->f->privmsg) {
348                        ic->f->privmsg(ic, cmd[2]);
349                }
350        } else if ((iu = irc_user_by_name(irc, cmd[1]))) {
351                if (cmd[2][0] == '\001') {
352                        char **ctcp;
353
354                        if (iu->f->ctcp == NULL) {
355                                return;
356                        }
357                        if (cmd[2][strlen(cmd[2]) - 1] == '\001') {
358                                cmd[2][strlen(cmd[2]) - 1] = '\0';
359                        }
360
361                        ctcp = split_command_parts(cmd[2] + 1, 0);
362                        iu->f->ctcp(iu, ctcp);
363                } else if (iu->f->privmsg) {
364                        iu->last_channel = NULL;
365                        iu->f->privmsg(iu, cmd[2]);
366                }
367        } else {
368                irc_send_num(irc, 401, "%s :No such nick/channel", cmd[1]);
369        }
370}
371
372static void irc_cmd_notice(irc_t *irc, char **cmd)
373{
374        irc_user_t *iu;
375
376        if (!cmd[2]) {
377                irc_send_num(irc, 412, ":No text to send");
378                return;
379        }
380
381        /* At least for now just echo. IIRC some IRC clients use self-notices
382           for lag checks, so try to support that. */
383        if (nick_cmp(NULL, cmd[1], irc->user->nick) == 0) {
384                irc_send_msg(irc->user, "NOTICE", irc->user->nick, cmd[2], NULL);
385        } else if ((iu = irc_user_by_name(irc, cmd[1]))) {
386                iu->f->privmsg(iu, cmd[2]);
387        }
388}
389
390static void irc_cmd_nickserv(irc_t *irc, char **cmd)
391{
392        /* [SH] This aliases the NickServ command to PRIVMSG root */
393        /* [TV] This aliases the NS command to PRIVMSG root as well */
394        root_command(irc, cmd + 1);
395}
396
397static void irc_cmd_oper_hack(irc_t *irc, char **cmd);
398
399static void irc_cmd_oper(irc_t *irc, char **cmd)
400{
401        /* Very non-standard evil but useful/secure hack, see below. */
402        if (irc->status & OPER_HACK_ANY) {
403                return irc_cmd_oper_hack(irc, cmd);
404        }
405
406        if (global.conf->oper_pass &&
407            (strncmp(global.conf->oper_pass, "md5:", 4) == 0 ?
408             md5_verify_password(cmd[2], global.conf->oper_pass + 4) == 0 :
409             strcmp(cmd[2], global.conf->oper_pass) == 0)) {
410                irc_umode_set(irc, "+o", 1);
411                irc_send_num(irc, 381, ":Password accepted");
412        } else {
413                irc_send_num(irc, 491, ":Incorrect password");
414        }
415}
416
417static void irc_cmd_oper_hack(irc_t *irc, char **cmd)
418{
419        char *password = g_strjoinv(" ", cmd + 2);
420
421        /* /OPER can now also be used to enter IM/identify passwords without
422           echoing. It's a hack but the extra password security is worth it. */
423        if (irc->status & OPER_HACK_ACCOUNT_PASSWORD) {
424                account_t *a;
425
426                for (a = irc->b->accounts; a; a = a->next) {
427                        if (strcmp(a->pass, PASSWORD_PENDING) == 0) {
428                                set_setstr(&a->set, "password", password);
429                                irc_rootmsg(irc, "Password added to IM account "
430                                            "%s", a->tag);
431                                /* The IRC client may expect this. 491 suggests the OPER
432                                   password was wrong, so the client won't expect a +o.
433                                   It may however repeat the password prompt. We'll see. */
434                                irc_send_num(irc, 491, ":Password added to IM account "
435                                             "%s", a->tag);
436                        }
437                }
438        } else if (irc->status & OPER_HACK_IDENTIFY) {
439                char *send_cmd[] = { "identify", password, NULL, NULL };
440                irc->status &= ~OPER_HACK_IDENTIFY;
441                if (irc->status & OPER_HACK_IDENTIFY_NOLOAD) {
442                        send_cmd[1] = "-noload";
443                        send_cmd[2] = password;
444                } else if (irc->status & OPER_HACK_IDENTIFY_FORCE) {
445                        send_cmd[1] = "-force";
446                        send_cmd[2] = password;
447                }
448                irc_send_num(irc, 491, ":Trying to identify");
449                root_command(irc, send_cmd);
450        } else if (irc->status & OPER_HACK_REGISTER) {
451                char *send_cmd[] = { "register", password, NULL };
452                irc_send_num(irc, 491, ":Trying to identify");
453                root_command(irc, send_cmd);
454        }
455
456        irc->status &= ~OPER_HACK_ANY;
457        g_free(password);
458}
459
460static void irc_cmd_invite(irc_t *irc, char **cmd)
461{
462        irc_channel_t *ic;
463        irc_user_t *iu;
464
465        if ((iu = irc_user_by_name(irc, cmd[1])) == NULL) {
466                irc_send_num(irc, 401, "%s :No such nick", cmd[1]);
467                return;
468        } else if ((ic = irc_channel_by_name(irc, cmd[2])) == NULL) {
469                irc_send_num(irc, 403, "%s :No such channel", cmd[2]);
470                return;
471        }
472
473        if (!ic->f->invite) {
474                irc_send_num(irc, 482, "%s :Can't invite people here", cmd[2]);
475        } else if (ic->f->invite(ic, iu)) {
476                irc_send_num(irc, 341, "%s %s", iu->nick, ic->name);
477        }
478}
479
480static void irc_cmd_kick(irc_t *irc, char **cmd)
481{
482        irc_channel_t *ic;
483        irc_user_t *iu;
484
485        if ((iu = irc_user_by_name(irc, cmd[2])) == NULL) {
486                irc_send_num(irc, 401, "%s :No such nick", cmd[2]);
487                return;
488        } else if ((ic = irc_channel_by_name(irc, cmd[1])) == NULL) {
489                irc_send_num(irc, 403, "%s :No such channel", cmd[1]);
490                return;
491        } else if (!ic->f->kick) {
492                irc_send_num(irc, 482, "%s :Can't kick people here", cmd[1]);
493                return;
494        }
495
496        ic->f->kick(ic, iu, cmd[3] ? cmd[3] : NULL);
497}
498
499static void irc_cmd_userhost(irc_t *irc, char **cmd)
500{
501        int i;
502
503        /* [TV] Usable USERHOST-implementation according to
504                RFC1459. Without this, mIRC shows an error
505                while connecting, and the used way of rejecting
506                breaks standards.
507        */
508
509        for (i = 1; cmd[i]; i++) {
510                irc_user_t *iu = irc_user_by_name(irc, cmd[i]);
511
512                if (iu) {
513                        irc_send_num(irc, 302, ":%s=%c%s@%s", iu->nick,
514                                     irc_user_get_away(iu) ? '-' : '+',
515                                     iu->user, iu->host);
516                }
517        }
518}
519
520static void irc_cmd_ison(irc_t *irc, char **cmd)
521{
522        char buff[IRC_MAX_LINE];
523        int lenleft, i;
524
525        buff[0] = '\0';
526
527        /* [SH] Leave room for : and \0 */
528        lenleft = IRC_MAX_LINE - 2;
529
530        for (i = 1; cmd[i]; i++) {
531                char *this, *next;
532
533                this = cmd[i];
534                while (*this) {
535                        irc_user_t *iu;
536
537                        if ((next = strchr(this, ' '))) {
538                                *next = 0;
539                        }
540
541                        if ((iu = irc_user_by_name(irc, this)) &&
542                            iu->bu && iu->bu->flags & BEE_USER_ONLINE) {
543                                lenleft -= strlen(iu->nick) + 1;
544
545                                if (lenleft < 0) {
546                                        break;
547                                }
548
549                                strcat(buff, iu->nick);
550                                strcat(buff, " ");
551                        }
552
553                        if (next) {
554                                *next = ' ';
555                                this = next + 1;
556                        } else {
557                                break;
558                        }
559                }
560
561                /* *sigh* */
562                if (lenleft < 0) {
563                        break;
564                }
565        }
566
567        if (strlen(buff) > 0) {
568                buff[strlen(buff) - 1] = '\0';
569        }
570
571        irc_send_num(irc, 303, ":%s", buff);
572}
573
574static void irc_cmd_watch(irc_t *irc, char **cmd)
575{
576        int i;
577
578        /* Obviously we could also mark a user structure as being
579           watched, but what if the WATCH command is sent right
580           after connecting? The user won't exist yet then... */
581        for (i = 1; cmd[i]; i++) {
582                char *nick;
583                irc_user_t *iu;
584
585                if (!cmd[i][0] || !cmd[i][1]) {
586                        break;
587                }
588
589                nick = g_strdup(cmd[i] + 1);
590                nick_lc(irc, nick);
591
592                iu = irc_user_by_name(irc, nick);
593
594                if (cmd[i][0] == '+') {
595                        if (!g_hash_table_lookup(irc->watches, nick)) {
596                                g_hash_table_insert(irc->watches, nick, nick);
597                        }
598
599                        if (iu && iu->bu && iu->bu->flags & BEE_USER_ONLINE) {
600                                irc_send_num(irc, 604, "%s %s %s %d :%s", iu->nick, iu->user,
601                                             iu->host, (int) time(NULL), "is online");
602                        } else {
603                                irc_send_num(irc, 605, "%s %s %s %d :%s", nick, "*", "*",
604                                             (int) time(NULL), "is offline");
605                        }
606                } else if (cmd[i][0] == '-') {
607                        gpointer okey, ovalue;
608
609                        if (g_hash_table_lookup_extended(irc->watches, nick, &okey, &ovalue)) {
610                                g_hash_table_remove(irc->watches, okey);
611                                g_free(okey);
612
613                                irc_send_num(irc, 602, "%s %s %s %d :%s", nick, "*", "*", 0, "Stopped watching");
614                        }
615                }
616        }
617}
618
619static void irc_cmd_topic(irc_t *irc, char **cmd)
620{
621        irc_channel_t *ic = irc_channel_by_name(irc, cmd[1]);
622        const char *new = cmd[2];
623
624        if (ic == NULL) {
625                irc_send_num(irc, 403, "%s :No such channel", cmd[1]);
626        } else if (new) {
627                if (ic->f->topic == NULL) {
628                        irc_send_num(irc, 482, "%s :Can't change this channel's topic", ic->name);
629                } else if (ic->f->topic(ic, new)) {
630                        irc_send_topic(ic, TRUE);
631                }
632        } else {
633                irc_send_topic(ic, FALSE);
634        }
635}
636
637static void irc_cmd_away(irc_t *irc, char **cmd)
638{
639        if (cmd[1] && *cmd[1]) {
640                char away[strlen(cmd[1]) + 1];
641                int i, j;
642
643                /* Copy away string, but skip control chars. Mainly because
644                   Jabber really doesn't like them. */
645                for (i = j = 0; cmd[1][i]; i++) {
646                        if ((unsigned char) (away[j] = cmd[1][i]) >= ' ') {
647                                j++;
648                        }
649                }
650                away[j] = '\0';
651
652                irc_send_num(irc, 306, ":You're now away: %s", away);
653                set_setstr(&irc->b->set, "away", away);
654        } else {
655                irc_send_num(irc, 305, ":Welcome back");
656                set_setstr(&irc->b->set, "away", NULL);
657        }
658}
659
660static void irc_cmd_list(irc_t *irc, char **cmd)
661{
662        GSList *l;
663
664        for (l = irc->channels; l; l = l->next) {
665                irc_channel_t *ic = l->data;
666
667                irc_send_num(irc, 322, "%s %d :%s",
668                             ic->name, g_slist_length(ic->users), ic->topic ? : "");
669        }
670        irc_send_num(irc, 323, ":%s", "End of /LIST");
671}
672
673static void irc_cmd_version(irc_t *irc, char **cmd)
674{
675        irc_send_num(irc, 351, "%s-%s. %s :%s/%s ",
676                     PACKAGE, BITLBEE_VERSION, irc->root->host, ARCH, CPU);
677}
678
679static void irc_cmd_completions(irc_t *irc, char **cmd)
680{
681        help_t *h;
682        set_t *s;
683        int i;
684
685        irc_send_msg_raw(irc->root, "NOTICE", irc->user->nick, "COMPLETIONS OK");
686
687        for (i = 0; root_commands[i].command; i++) {
688                irc_send_msg_f(irc->root, "NOTICE", irc->user->nick, "COMPLETIONS %s", root_commands[i].command);
689        }
690
691        for (h = global.help; h; h = h->next) {
692                irc_send_msg_f(irc->root, "NOTICE", irc->user->nick, "COMPLETIONS help %s", h->title);
693        }
694
695        for (s = irc->b->set; s; s = s->next) {
696                irc_send_msg_f(irc->root, "NOTICE", irc->user->nick, "COMPLETIONS set %s", s->key);
697        }
698
699        irc_send_msg_raw(irc->root, "NOTICE", irc->user->nick, "COMPLETIONS END");
700}
701
702static void irc_cmd_rehash(irc_t *irc, char **cmd)
703{
704        if (global.conf->runmode == RUNMODE_INETD) {
705                ipc_master_cmd_rehash(NULL, NULL);
706        } else {
707                ipc_to_master(cmd);
708        }
709
710        irc_send_num(irc, 382, "%s :Rehashing", global.conf_file);
711}
712
713static const command_t irc_commands[] = {
714        { "cap",         1, irc_cmd_cap,         0 },
715        { "pass",        1, irc_cmd_pass,        0 },
716        { "user",        4, irc_cmd_user,        IRC_CMD_PRE_LOGIN },
717        { "nick",        1, irc_cmd_nick,        0 },
718        { "quit",        0, irc_cmd_quit,        0 },
719        { "ping",        0, irc_cmd_ping,        0 },
720        { "pong",        0, irc_cmd_pong,        IRC_CMD_LOGGED_IN },
721        { "join",        1, irc_cmd_join,        IRC_CMD_LOGGED_IN },
722        { "names",       1, irc_cmd_names,       IRC_CMD_LOGGED_IN },
723        { "part",        1, irc_cmd_part,        IRC_CMD_LOGGED_IN },
724        { "whois",       1, irc_cmd_whois,       IRC_CMD_LOGGED_IN },
725        { "whowas",      1, irc_cmd_whowas,      IRC_CMD_LOGGED_IN },
726        { "motd",        0, irc_cmd_motd,        IRC_CMD_LOGGED_IN },
727        { "mode",        1, irc_cmd_mode,        IRC_CMD_LOGGED_IN },
728        { "who",         0, irc_cmd_who,         IRC_CMD_LOGGED_IN },
729        { "privmsg",     1, irc_cmd_privmsg,     IRC_CMD_LOGGED_IN },
730        { "notice",      1, irc_cmd_notice,      IRC_CMD_LOGGED_IN },
731        { "nickserv",    1, irc_cmd_nickserv,    IRC_CMD_LOGGED_IN },
732        { "ns",          1, irc_cmd_nickserv,    IRC_CMD_LOGGED_IN },
733        { "away",        0, irc_cmd_away,        IRC_CMD_LOGGED_IN },
734        { "version",     0, irc_cmd_version,     IRC_CMD_LOGGED_IN },
735        { "completions", 0, irc_cmd_completions, IRC_CMD_LOGGED_IN },
736        { "userhost",    1, irc_cmd_userhost,    IRC_CMD_LOGGED_IN },
737        { "ison",        1, irc_cmd_ison,        IRC_CMD_LOGGED_IN },
738        { "watch",       1, irc_cmd_watch,       IRC_CMD_LOGGED_IN },
739        { "invite",      2, irc_cmd_invite,      IRC_CMD_LOGGED_IN },
740        { "kick",        2, irc_cmd_kick,        IRC_CMD_LOGGED_IN },
741        { "topic",       1, irc_cmd_topic,       IRC_CMD_LOGGED_IN },
742        { "oper",        2, irc_cmd_oper,        IRC_CMD_LOGGED_IN },
743        { "list",        0, irc_cmd_list,        IRC_CMD_LOGGED_IN },
744        { "die",         0, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
745        { "deaf",        0, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
746        { "wallops",     1, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
747        { "wall",        1, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
748        { "rehash",      0, irc_cmd_rehash,      IRC_CMD_OPER_ONLY },
749        { "restart",     0, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
750        { "kill",        2, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
751        { NULL }
752};
753
754void irc_exec(irc_t *irc, char *cmd[])
755{
756        int i, n_arg;
757
758        if (!cmd[0]) {
759                return;
760        }
761
762        for (i = 0; irc_commands[i].command; i++) {
763                if (g_strcasecmp(irc_commands[i].command, cmd[0]) == 0) {
764                        /* There should be no typo in the next line: */
765                        for (n_arg = 0; cmd[n_arg]; n_arg++) {
766                                ;
767                        }
768                        n_arg--;
769
770                        if (irc_commands[i].flags & IRC_CMD_PRE_LOGIN && irc->status & USTATUS_LOGGED_IN) {
771                                irc_send_num(irc, 462, ":Only allowed before logging in");
772                        } else if (irc_commands[i].flags & IRC_CMD_LOGGED_IN && !(irc->status & USTATUS_LOGGED_IN)) {
773                                irc_send_num(irc, 451, ":Register first");
774                        } else if (irc_commands[i].flags & IRC_CMD_OPER_ONLY && !strchr(irc->umode, 'o')) {
775                                irc_send_num(irc, 481, ":Permission denied - You're not an IRC operator");
776                        } else if (n_arg < irc_commands[i].required_parameters) {
777                                irc_send_num(irc, 461, "%s :Need more parameters", cmd[0]);
778                        } else if (irc_commands[i].flags & IRC_CMD_TO_MASTER) {
779                                /* IPC doesn't make sense in inetd mode,
780                                    but the function will catch that. */
781                                ipc_to_master(cmd);
782                        } else {
783                                irc_commands[i].execute(irc, cmd);
784                        }
785
786                        return;
787                }
788        }
789
790        if (irc->status & USTATUS_LOGGED_IN) {
791                irc_send_num(irc, 421, "%s :Unknown command", cmd[0]);
792        }
793}
Note: See TracBrowser for help on using the repository browser.