source: irc_commands.c @ 4bc66ae

Last change on this file since 4bc66ae was 060d066, checked in by Wilmer van der Gaast <wilmer@…>, at 2011-02-01T13:05:58Z

More password paranoia: Allow omitting the identify/register password as
well (and enter it using /OPER instead).

This is a gross hack and indeed still not solid: In irssi one can still
use /RAWLOG SAVE to find the OPER line sent to BitlBee (and of course not
everyone uses SSL to talk to remote BitlBee servers). This only works
within 10-30 minutes after entering the password though.

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