source: irc_commands.c @ 7821ee8

Last change on this file since 7821ee8 was 7821ee8, checked in by jgeboski <jgeboski@…>, at 2015-01-29T19:24:17Z

irc_commands: implemented KICK support

With similar commands being supported, such as INVITE, the KICK command
should be supported as well. The key motivation behind supporting KICK
is having for having a way to remove users from group chats. As of now,
there is no way for a bitlbee user to remove a user from a group chat.
With no current KICK implementation, it made using this command a prime
candidate for the UI side of this implementation. In addition, the KICK
command has been supported in the control channel as well. This is to
keep the INVITE/KICK pair consistent.

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