source: irc_commands.c @ 9b69eb7

Last change on this file since 9b69eb7 was 9b69eb7, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-03-27T12:34:44Z

MOTD command added since, well, I had all the code for it already anyway.

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