source: irc_commands.c @ 872e017

Last change on this file since 872e017 was 355e2ad, checked in by dequis <dx@…>, at 2015-10-01T01:01:48Z

Move CAP commands to irc_cap.c; use enum for flags

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