source: irc_commands.c @ a670aeb

Last change on this file since a670aeb was 84c3a72, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-06-27T12:39:07Z

Import chatrooms configured in older BitlBee versions. Settings are currently
ignored though. Also removing the old chat.[ch] files since they're really not
important anymore.

  • Property mode set to 100644
File size: 17.5 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 "ipc.h"
29
30static void irc_cmd_pass( irc_t *irc, char **cmd )
31{
32        if( irc->status & USTATUS_LOGGED_IN )
33        {
34                char *send_cmd[] = { "identify", cmd[1], NULL };
35               
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. */
40                return root_command( irc, send_cmd );
41        }
42        /* Handling in pre-logged-in state, first see if this server is
43           password-protected: */
44        else if( global.conf->auth_pass &&
45            ( strncmp( global.conf->auth_pass, "md5:", 4 ) == 0 ?
46                md5_verify_password( cmd[1], global.conf->auth_pass + 4 ) == 0 :
47                strcmp( cmd[1], global.conf->auth_pass ) == 0 ) )
48        {
49                irc->status |= USTATUS_AUTHORIZED;
50                irc_check_login( irc );
51        }
52        else if( global.conf->auth_pass )
53        {
54                irc_send_num( irc, 464, ":Incorrect password" );
55        }
56        else
57        {
58                /* Remember the password and try to identify after USER/NICK. */
59                irc_setpass( irc, cmd[1] );
60                irc_check_login( irc );
61        }
62}
63
64static void irc_cmd_user( irc_t *irc, char **cmd )
65{
66        irc->user->user = g_strdup( cmd[1] );
67        irc->user->fullname = g_strdup( cmd[4] );
68       
69        irc_check_login( irc );
70}
71
72static void irc_cmd_nick( irc_t *irc, char **cmd )
73{
74        if( irc_user_by_name( irc, cmd[1] ) )
75        {
76                irc_send_num( irc, 433, ":This nick is already in use" );
77        }
78        else if( !nick_ok( cmd[1] ) )
79        {
80                /* [SH] Invalid characters. */
81                irc_send_num( irc, 432, ":This nick contains invalid characters" );
82        }
83        else if( irc->user->nick )
84        {
85                if( irc->status & USTATUS_IDENTIFIED )
86                {
87                        irc_setpass( irc, NULL );
88                        irc->status &= ~USTATUS_IDENTIFIED;
89                        irc_umode_set( irc, "-R", 1 );
90                        irc_usermsg( irc, "Changing nicks resets your identify status. "
91                                     "Re-identify or register a new account if you want "
92                                     "your configuration to be saved. See \x02help "
93                                     "nick_changes\x02." );
94                }
95               
96                irc_user_set_nick( irc->user, cmd[1] );
97        }
98        else
99        {
100                irc->user->nick = g_strdup( cmd[1] );
101               
102                irc_check_login( irc );
103        }
104}
105
106static void irc_cmd_quit( irc_t *irc, char **cmd )
107{
108        if( cmd[1] && *cmd[1] )
109                irc_abort( irc, 0, "Quit: %s", cmd[1] );
110        else
111                irc_abort( irc, 0, "Leaving..." );
112}
113
114static void irc_cmd_ping( irc_t *irc, char **cmd )
115{
116        irc_write( irc, ":%s PONG %s :%s", irc->root->host,
117                   irc->root->host, cmd[1]?cmd[1]:irc->root->host );
118}
119
120static void irc_cmd_pong( irc_t *irc, char **cmd )
121{
122        /* We could check the value we get back from the user, but in
123           fact we don't care, we're just happy s/he's still alive. */
124        irc->last_pong = gettime();
125        irc->pinging = 0;
126}
127
128static void irc_cmd_join( irc_t *irc, char **cmd )
129{
130        irc_channel_t *ic;
131       
132        if( ( ic = irc_channel_by_name( irc, cmd[1] ) ) == NULL )
133                ic = irc_channel_new( irc, cmd[1] );
134       
135        if( ic == NULL )
136        {
137                irc_send_num( irc, 479, "%s :Invalid channel name", cmd[1] );
138                return;
139        }
140       
141        if( ic->flags & IRC_CHANNEL_JOINED )
142                return; /* Dude, you're already there...
143                           RFC doesn't have any reply for that though? */
144       
145        if( ic->f->join && !ic->f->join( ic ) )
146                /* The story is: FALSE either means the handler showed an error
147                   message, or is doing some work before the join should be
148                   confirmed. (In the latter case, the caller should take care
149                   of that confirmation.)
150                   TRUE means all's good, let the user join the channel right away. */
151                return;
152       
153        irc_channel_add_user( ic, irc->user );
154}
155
156static void irc_cmd_names( irc_t *irc, char **cmd )
157{
158        irc_channel_t *ic;
159       
160        if( cmd[1] && ( ic = irc_channel_by_name( irc, cmd[1] ) ) )
161                irc_send_names( ic );
162        /* With no args, we should show /names of all chans. Make the code
163           below work well if necessary.
164        else
165        {
166                GSList *l;
167               
168                for( l = irc->channels; l; l = l->next )
169                        irc_send_names( l->data );
170        }
171        */
172}
173
174static void irc_cmd_part( irc_t *irc, char **cmd )
175{
176        irc_channel_t *ic;
177       
178        if( ( ic = irc_channel_by_name( irc, cmd[1] ) ) == NULL )
179        {
180                irc_send_num( irc, 403, "%s :No such channel", cmd[1] );
181        }
182        else if( irc_channel_del_user( ic, irc->user, FALSE, cmd[2] ) )
183        {
184                if( ic->f->part )
185                        ic->f->part( ic, NULL );
186        }
187        else
188        {
189                irc_send_num( irc, 442, "%s :You're not on that channel", cmd[1] );
190        }
191}
192
193static void irc_cmd_whois( irc_t *irc, char **cmd )
194{
195        char *nick = cmd[1];
196        irc_user_t *iu = irc_user_by_name( irc, nick );
197       
198        if( iu )
199                irc_send_whois( iu );
200        else
201                irc_send_num( irc, 401, "%s :Nick does not exist", nick );
202}
203
204static void irc_cmd_whowas( irc_t *irc, char **cmd )
205{
206        /* For some reason irssi tries a whowas when whois fails. We can
207           ignore this, but then the user never gets a "user not found"
208           message from irssi which is a bit annoying. So just respond
209           with not-found and irssi users will get better error messages */
210       
211        irc_send_num( irc, 406, "%s :Nick does not exist", cmd[1] );
212        irc_send_num( irc, 369, "%s :End of WHOWAS", cmd[1] );
213}
214
215static void irc_cmd_motd( irc_t *irc, char **cmd )
216{
217        irc_send_motd( irc );
218}
219
220static void irc_cmd_mode( irc_t *irc, char **cmd )
221{
222        if( irc_channel_name_ok( cmd[1] ) )
223        {
224                irc_channel_t *ic;
225               
226                if( ( ic = irc_channel_by_name( irc, cmd[1] ) ) == NULL )
227                        irc_send_num( irc, 403, "%s :No such channel", cmd[1] );
228                else if( cmd[2] )
229                {
230                        if( *cmd[2] == '+' || *cmd[2] == '-' )
231                                irc_send_num( irc, 477, "%s :Can't change channel modes", cmd[1] );
232                        else if( *cmd[2] == 'b' )
233                                irc_send_num( irc, 368, "%s :No bans possible", cmd[1] );
234                }
235                else
236                        irc_send_num( irc, 324, "%s +%s", cmd[1], ic->mode );
237        }
238        else
239        {
240                if( nick_cmp( cmd[1], irc->user->nick ) == 0 )
241                {
242                        if( cmd[2] )
243                                irc_umode_set( irc, cmd[2], 0 );
244                        else
245                                irc_send_num( irc, 221, "+%s", irc->umode );
246                }
247                else
248                        irc_send_num( irc, 502, ":Don't touch their modes" );
249        }
250}
251
252static void irc_cmd_who( irc_t *irc, char **cmd )
253{
254        char *channel = cmd[1];
255        irc_channel_t *ic;
256       
257        if( !channel || *channel == '0' || *channel == '*' || !*channel )
258                irc_send_who( irc, irc->users, "**" );
259        else if( ( ic = irc_channel_by_name( irc, channel ) ) )
260                irc_send_who( irc, ic->users, channel );
261        else
262                irc_send_num( irc, 403, "%s :No such channel", channel );
263}
264
265static void irc_cmd_privmsg( irc_t *irc, char **cmd )
266{
267        irc_channel_t *ic;
268        irc_user_t *iu;
269       
270        if( !cmd[2] ) 
271        {
272                irc_send_num( irc, 412, ":No text to send" );
273                return;
274        }
275       
276        /* Don't treat CTCP actions as real CTCPs, just convert them right now. */
277        if( g_strncasecmp( cmd[2], "\001ACTION", 7 ) == 0 )
278        {
279                cmd[2] += 4;
280                strcpy( cmd[2], "/me" );
281                if( cmd[2][strlen(cmd[2])-1] == '\001' )
282                        cmd[2][strlen(cmd[2])-1] = '\0';
283        }
284       
285        if( irc_channel_name_ok( cmd[1] ) &&
286            ( ic = irc_channel_by_name( irc, cmd[1] ) ) )
287        {
288                if( ic->f->privmsg )
289                        ic->f->privmsg( ic, cmd[2] );
290        }
291        else if( ( iu = irc_user_by_name( irc, cmd[1] ) ) )
292        {
293                if( cmd[2][0] == '\001' )
294                {
295                        char **ctcp;
296                       
297                        if( iu->f->ctcp == NULL )
298                                return;
299                        if( cmd[2][strlen(cmd[2])-1] == '\001' )
300                                cmd[2][strlen(cmd[2])-1] = '\0';
301                       
302                        ctcp = split_command_parts( cmd[2] + 1 );
303                        iu->f->ctcp( iu, ctcp );
304                }
305                else if( iu->f->privmsg )
306                {
307                        iu->last_channel = NULL;
308                        iu->f->privmsg( iu, cmd[2] );
309                }
310        }
311        else
312        {
313                irc_send_num( irc, 401, "%s :No such nick/channel", cmd[1] );
314        }
315}
316
317static void irc_cmd_nickserv( irc_t *irc, char **cmd )
318{
319        /* [SH] This aliases the NickServ command to PRIVMSG root */
320        /* [TV] This aliases the NS command to PRIVMSG root as well */
321        root_command( irc, cmd + 1 );
322}
323
324
325
326static void irc_cmd_oper( irc_t *irc, char **cmd )
327{
328        if( global.conf->oper_pass &&
329            ( strncmp( global.conf->oper_pass, "md5:", 4 ) == 0 ?
330                md5_verify_password( cmd[2], global.conf->oper_pass + 4 ) == 0 :
331                strcmp( cmd[2], global.conf->oper_pass ) == 0 ) )
332        {
333                irc_umode_set( irc, "+o", 1 );
334                irc_send_num( irc, 381, ":Password accepted" );
335        }
336        else
337        {
338                irc_send_num( irc, 432, ":Incorrect password" );
339        }
340}
341
342static void irc_cmd_invite( irc_t *irc, char **cmd )
343{
344        irc_channel_t *ic;
345        irc_user_t *iu;
346       
347        if( ( iu = irc_user_by_name( irc, cmd[1] ) ) == NULL )
348        {
349                irc_send_num( irc, 401, "%s :No such nick", cmd[1] );
350                return;
351        }
352        else if( ( ic = irc_channel_by_name( irc, cmd[2] ) ) == NULL )
353        {
354                irc_send_num( irc, 403, "%s :No such channel", cmd[2] );
355                return;
356        }
357       
358        if( !ic->f->invite )
359                irc_send_num( irc, 482, "%s :Can't invite people here", cmd[2] );
360        else if( ic->f->invite( ic, iu ) )
361                irc_send_num( irc, 341, "%s %s", iu->nick, ic->name );
362}
363
364static void irc_cmd_userhost( irc_t *irc, char **cmd )
365{
366        int i;
367       
368        /* [TV] Usable USERHOST-implementation according to
369                RFC1459. Without this, mIRC shows an error
370                while connecting, and the used way of rejecting
371                breaks standards.
372        */
373       
374        for( i = 1; cmd[i]; i ++ )
375        {
376                irc_user_t *iu = irc_user_by_name( irc, cmd[i] );
377               
378                if( iu )
379                        irc_send_num( irc, 302, ":%s=%c%s@%s", iu->nick,
380                                      irc_user_get_away( iu ) ? '-' : '+',
381                                      iu->user, iu->host );
382        }
383}
384
385static void irc_cmd_ison( irc_t *irc, char **cmd )
386{
387        char buff[IRC_MAX_LINE];
388        int lenleft, i;
389       
390        buff[0] = '\0';
391       
392        /* [SH] Leave room for : and \0 */
393        lenleft = IRC_MAX_LINE - 2;
394       
395        for( i = 1; cmd[i]; i ++ )
396        {
397                char *this, *next;
398               
399                this = cmd[i];
400                while( *this )
401                {
402                        irc_user_t *iu;
403                       
404                        if( ( next = strchr( this, ' ' ) ) )
405                                *next = 0;
406                       
407                        if( ( iu = irc_user_by_name( irc, this ) ) &&
408                            iu->bu && iu->bu->flags & BEE_USER_ONLINE )
409                        {
410                                lenleft -= strlen( iu->nick ) + 1;
411                               
412                                if( lenleft < 0 )
413                                        break;
414                               
415                                strcat( buff, iu->nick );
416                                strcat( buff, " " );
417                        }
418                       
419                        if( next )
420                        {
421                                *next = ' ';
422                                this = next + 1;
423                        }
424                        else
425                        {
426                                break;
427                        }   
428                }
429               
430                /* *sigh* */
431                if( lenleft < 0 )
432                        break;
433        }
434       
435        if( strlen( buff ) > 0 )
436                buff[strlen(buff)-1] = '\0';
437       
438        irc_send_num( irc, 303, ":%s", buff );
439}
440
441static void irc_cmd_watch( irc_t *irc, char **cmd )
442{
443        int i;
444       
445        /* Obviously we could also mark a user structure as being
446           watched, but what if the WATCH command is sent right
447           after connecting? The user won't exist yet then... */
448        for( i = 1; cmd[i]; i ++ )
449        {
450                char *nick;
451                irc_user_t *iu;
452               
453                if( !cmd[i][0] || !cmd[i][1] )
454                        break;
455               
456                nick = g_strdup( cmd[i] + 1 );
457                nick_lc( nick );
458               
459                iu = irc_user_by_name( irc, nick );
460               
461                if( cmd[i][0] == '+' )
462                {
463                        if( !g_hash_table_lookup( irc->watches, nick ) )
464                                g_hash_table_insert( irc->watches, nick, nick );
465                       
466                        if( iu && iu->bu && iu->bu->flags & BEE_USER_ONLINE )
467                                irc_send_num( irc, 604, "%s %s %s %d :%s", iu->nick, iu->user,
468                                              iu->host, (int) time( NULL ), "is online" );
469                        else
470                                irc_send_num( irc, 605, "%s %s %s %d :%s", nick, "*", "*",
471                                              (int) time( NULL ), "is offline" );
472                }
473                else if( cmd[i][0] == '-' )
474                {
475                        gpointer okey, ovalue;
476                       
477                        if( g_hash_table_lookup_extended( irc->watches, nick, &okey, &ovalue ) )
478                        {
479                                g_hash_table_remove( irc->watches, okey );
480                                g_free( okey );
481                               
482                                irc_send_num( irc, 602, "%s %s %s %d :%s", nick, "*", "*", 0, "Stopped watching" );
483                        }
484                }
485        }
486}
487
488static void irc_cmd_topic( irc_t *irc, char **cmd )
489{
490        irc_channel_t *ic = irc_channel_by_name( irc, cmd[1] );
491        const char *new = cmd[2];
492       
493        if( ic == NULL )
494        {
495                irc_send_num( irc, 403, "%s :No such channel", cmd[1] );
496        }
497        else if( new )
498        {
499                if( ic->f->topic == NULL )
500                        irc_send_num( irc, 482, "%s :Can't change this channel's topic", ic->name );
501                else if( ic->f->topic( ic, new ) )
502                        irc_send_topic( ic, TRUE );
503        }
504        else
505        {
506                irc_send_topic( ic, FALSE );
507        }
508}
509
510static void irc_cmd_away( irc_t *irc, char **cmd )
511{
512        if( cmd[1] && *cmd[1] )
513        {
514                char away[strlen(cmd[1])+1];
515                int i, j;
516               
517                /* Copy away string, but skip control chars. Mainly because
518                   Jabber really doesn't like them. */
519                for( i = j = 0; cmd[1][i]; i ++ )
520                        if( ( away[j] = cmd[1][i] ) >= ' ' )
521                                j ++;
522                away[j] = '\0';
523               
524                irc_send_num( irc, 306, ":You're now away: %s", away );
525                set_setstr( &irc->b->set, "away", away );
526        }
527        else
528        {
529                irc_send_num( irc, 305, ":Welcome back" );
530                set_setstr( &irc->b->set, "away", NULL );
531        }
532}
533
534static void irc_cmd_version( irc_t *irc, char **cmd )
535{
536        irc_send_num( irc, 351, "bitlbee-%s. %s :%s/%s ",
537                      BITLBEE_VERSION, irc->root->host, ARCH, CPU );
538}
539
540static void irc_cmd_completions( irc_t *irc, char **cmd )
541{
542        help_t *h;
543        set_t *s;
544        int i;
545       
546        irc_send_msg_raw( irc->root, "NOTICE", irc->user->nick, "COMPLETIONS OK" );
547       
548        for( i = 0; commands[i].command; i ++ )
549                irc_send_msg_f( irc->root, "NOTICE", irc->user->nick, "COMPLETIONS %s", commands[i].command );
550       
551        for( h = global.help; h; h = h->next )
552                irc_send_msg_f( irc->root, "NOTICE", irc->user->nick, "COMPLETIONS help %s", h->title );
553       
554        for( s = irc->b->set; s; s = s->next )
555                irc_send_msg_f( irc->root, "NOTICE", irc->user->nick, "COMPLETIONS set %s", s->key );
556       
557        irc_send_msg_raw( irc->root, "NOTICE", irc->user->nick, "COMPLETIONS END" );
558}
559
560static void irc_cmd_rehash( irc_t *irc, char **cmd )
561{
562        if( global.conf->runmode == RUNMODE_INETD )
563                ipc_master_cmd_rehash( NULL, NULL );
564        else
565                ipc_to_master( cmd );
566       
567        irc_send_num( irc, 382, "%s :Rehashing", global.conf_file );
568}
569
570static const command_t irc_commands[] = {
571        { "pass",        1, irc_cmd_pass,        0 },
572        { "user",        4, irc_cmd_user,        IRC_CMD_PRE_LOGIN },
573        { "nick",        1, irc_cmd_nick,        0 },
574        { "quit",        0, irc_cmd_quit,        0 },
575        { "ping",        0, irc_cmd_ping,        0 },
576        { "pong",        0, irc_cmd_pong,        IRC_CMD_LOGGED_IN },
577        { "join",        1, irc_cmd_join,        IRC_CMD_LOGGED_IN },
578        { "names",       1, irc_cmd_names,       IRC_CMD_LOGGED_IN },
579        { "part",        1, irc_cmd_part,        IRC_CMD_LOGGED_IN },
580        { "whois",       1, irc_cmd_whois,       IRC_CMD_LOGGED_IN },
581        { "whowas",      1, irc_cmd_whowas,      IRC_CMD_LOGGED_IN },
582        { "motd",        0, irc_cmd_motd,        IRC_CMD_LOGGED_IN },
583        { "mode",        1, irc_cmd_mode,        IRC_CMD_LOGGED_IN },
584        { "who",         0, irc_cmd_who,         IRC_CMD_LOGGED_IN },
585        { "privmsg",     1, irc_cmd_privmsg,     IRC_CMD_LOGGED_IN },
586        { "nickserv",    1, irc_cmd_nickserv,    IRC_CMD_LOGGED_IN },
587        { "ns",          1, irc_cmd_nickserv,    IRC_CMD_LOGGED_IN },
588        { "away",        0, irc_cmd_away,        IRC_CMD_LOGGED_IN },
589        { "version",     0, irc_cmd_version,     IRC_CMD_LOGGED_IN },
590        { "completions", 0, irc_cmd_completions, IRC_CMD_LOGGED_IN },
591        { "userhost",    1, irc_cmd_userhost,    IRC_CMD_LOGGED_IN },
592        { "ison",        1, irc_cmd_ison,        IRC_CMD_LOGGED_IN },
593        { "watch",       1, irc_cmd_watch,       IRC_CMD_LOGGED_IN },
594        { "invite",      2, irc_cmd_invite,      IRC_CMD_LOGGED_IN },
595#if 0
596        { "notice",      1, irc_cmd_privmsg,     IRC_CMD_LOGGED_IN },
597#endif
598        { "topic",       1, irc_cmd_topic,       IRC_CMD_LOGGED_IN },
599        { "oper",        2, irc_cmd_oper,        IRC_CMD_LOGGED_IN },
600        { "die",         0, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
601        { "deaf",        0, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
602        { "wallops",     1, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
603        { "wall",        1, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
604        { "rehash",      0, irc_cmd_rehash,      IRC_CMD_OPER_ONLY },
605        { "restart",     0, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
606        { "kill",        2, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
607        { NULL }
608};
609
610void irc_exec( irc_t *irc, char *cmd[] )
611{       
612        int i, n_arg;
613       
614        if( !cmd[0] )
615                return;
616       
617        for( i = 0; irc_commands[i].command; i++ )
618                if( g_strcasecmp( irc_commands[i].command, cmd[0] ) == 0 )
619                {
620                        /* There should be no typo in the next line: */
621                        for( n_arg = 0; cmd[n_arg]; n_arg ++ ); n_arg --;
622                       
623                        if( irc_commands[i].flags & IRC_CMD_PRE_LOGIN && irc->status & USTATUS_LOGGED_IN )
624                        {
625                                irc_send_num( irc, 462, ":Only allowed before logging in" );
626                        }
627                        else if( irc_commands[i].flags & IRC_CMD_LOGGED_IN && !( irc->status & USTATUS_LOGGED_IN ) )
628                        {
629                                irc_send_num( irc, 451, ":Register first" );
630                        }
631                        else if( irc_commands[i].flags & IRC_CMD_OPER_ONLY && !strchr( irc->umode, 'o' ) )
632                        {
633                                irc_send_num( irc, 481, ":Permission denied - You're not an IRC operator" );
634                        }
635                        else if( n_arg < irc_commands[i].required_parameters )
636                        {
637                                irc_send_num( irc, 461, "%s :Need more parameters", cmd[0] );
638                        }
639                        else if( irc_commands[i].flags & IRC_CMD_TO_MASTER )
640                        {
641                                /* IPC doesn't make sense in inetd mode,
642                                    but the function will catch that. */
643                                ipc_to_master( cmd );
644                        }
645                        else
646                        {
647                                irc_commands[i].execute( irc, cmd );
648                        }
649                       
650                        return;
651                }
652       
653        if( irc->status >= USTATUS_LOGGED_IN )
654                irc_send_num( irc, 421, "%s :Unknown command", cmd[0] );
655}
Note: See TracBrowser for help on using the repository browser.