Scan media entities as well, not just url entities. This should expand more
[bitlbee.git] / irc_commands.c
blob9a46f882eb8ab7f4ca016899e48425a7fc6db4a8
1 /********************************************************************\
2 * BitlBee -- An IRC to other IM-networks gateway *
3 * *
4 * Copyright 2002-2010 Wilmer van der Gaast and others *
5 \********************************************************************/
7 /* IRC commands */
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.
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.
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
26 #define BITLBEE_CORE
27 #include "bitlbee.h"
28 #include "help.h"
29 #include "ipc.h"
31 static void irc_cmd_pass( irc_t *irc, char **cmd )
33 if( irc->status & USTATUS_LOGGED_IN )
35 char *send_cmd[] = { "identify", cmd[1], NULL };
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;
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 ) )
51 irc->status |= USTATUS_AUTHORIZED;
52 irc_check_login( irc );
54 else if( global.conf->auth_pass )
56 irc_send_num( irc, 464, ":Incorrect password" );
58 else
60 /* Remember the password and try to identify after USER/NICK. */
61 irc_setpass( irc, cmd[1] );
62 irc_check_login( irc );
66 static void irc_cmd_user( irc_t *irc, char **cmd )
68 irc->user->user = g_strdup( cmd[1] );
69 irc->user->fullname = g_strdup( cmd[4] );
71 irc_check_login( irc );
74 static void irc_cmd_nick( irc_t *irc, char **cmd )
76 irc_user_t *iu;
78 if( ( iu = irc_user_by_name( irc, cmd[1] ) ) && iu != irc->user )
80 irc_send_num( irc, 433, "%s :This nick is already in use", cmd[1] );
82 else if( !nick_ok( cmd[1] ) )
84 /* [SH] Invalid characters. */
85 irc_send_num( irc, 432, "%s :This nick contains invalid characters", cmd[1] );
87 else if( irc->status & USTATUS_LOGGED_IN )
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 )
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." );
103 if( strcmp( cmd[1], irc->user->nick ) != 0 )
104 irc_user_set_nick( irc->user, cmd[1] );
106 else
108 g_free( irc->user->nick );
109 irc->user->nick = g_strdup( cmd[1] );
111 irc_check_login( irc );
115 static void irc_cmd_quit( irc_t *irc, char **cmd )
117 if( cmd[1] && *cmd[1] )
118 irc_abort( irc, 0, "Quit: %s", cmd[1] );
119 else
120 irc_abort( irc, 0, "Leaving..." );
123 static void irc_cmd_ping( irc_t *irc, char **cmd )
125 irc_write( irc, ":%s PONG %s :%s", irc->root->host,
126 irc->root->host, cmd[1]?cmd[1]:irc->root->host );
129 static void irc_cmd_pong( irc_t *irc, char **cmd )
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;
137 static void irc_cmd_join( irc_t *irc, char **cmd )
139 char *comma, *s = cmd[1];
141 while( s )
143 irc_channel_t *ic;
145 if( ( comma = strchr( s, ',' ) ) )
146 *comma = '\0';
148 if( ( ic = irc_channel_by_name( irc, s ) ) == NULL &&
149 ( ic = irc_channel_new( irc, s ) ) )
151 if( strcmp( set_getstr( &ic->set, "type" ), "control" ) != 0 )
153 /* Autoconfiguration is for control channels only ATM. */
155 else if( bee_group_by_name( ic->irc->b, ic->name + 1, FALSE ) )
157 set_setstr( &ic->set, "group", ic->name + 1 );
158 set_setstr( &ic->set, "fill_by", "group" );
160 else if( set_setstr( &ic->set, "protocol", ic->name + 1 ) )
162 set_setstr( &ic->set, "fill_by", "protocol" );
164 else if( set_setstr( &ic->set, "account", ic->name + 1 ) )
166 set_setstr( &ic->set, "fill_by", "account" );
168 else
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 );
176 else if( ic == NULL )
178 irc_send_num( irc, 479, "%s :Invalid channel name", s );
179 goto next;
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;
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;
196 irc_channel_add_user( ic, irc->user );
198 next:
199 if( comma )
201 s = comma + 1;
202 *comma = ',';
204 else
205 break;
209 static void irc_cmd_names( irc_t *irc, char **cmd )
211 irc_channel_t *ic;
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
219 GSList *l;
221 for( l = irc->channels; l; l = l->next )
222 irc_send_names( l->data );
227 static void irc_cmd_part( irc_t *irc, char **cmd )
229 irc_channel_t *ic;
231 if( ( ic = irc_channel_by_name( irc, cmd[1] ) ) == NULL )
233 irc_send_num( irc, 403, "%s :No such channel", cmd[1] );
235 else if( irc_channel_del_user( ic, irc->user, IRC_CDU_PART, cmd[2] ) )
237 if( ic->f->part )
238 ic->f->part( ic, NULL );
240 else
242 irc_send_num( irc, 442, "%s :You're not on that channel", cmd[1] );
246 static void irc_cmd_whois( irc_t *irc, char **cmd )
248 char *nick = cmd[1];
249 irc_user_t *iu = irc_user_by_name( irc, nick );
251 if( iu )
252 irc_send_whois( iu );
253 else
254 irc_send_num( irc, 401, "%s :Nick does not exist", nick );
257 static void irc_cmd_whowas( irc_t *irc, char **cmd )
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 */
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] );
268 static void irc_cmd_motd( irc_t *irc, char **cmd )
270 irc_send_motd( irc );
273 static void irc_cmd_mode( irc_t *irc, char **cmd )
275 if( irc_channel_name_ok( cmd[1] ) )
277 irc_channel_t *ic;
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] )
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] );
288 else
289 irc_send_num( irc, 324, "%s +%s", cmd[1], ic->mode );
291 else
293 if( nick_cmp( cmd[1], irc->user->nick ) == 0 )
295 if( cmd[2] )
296 irc_umode_set( irc, cmd[2], 0 );
297 else
298 irc_send_num( irc, 221, "+%s", irc->umode );
300 else
301 irc_send_num( irc, 502, ":Don't touch their modes" );
305 static void irc_cmd_who( irc_t *irc, char **cmd )
307 char *channel = cmd[1];
308 irc_channel_t *ic;
309 irc_user_t *iu;
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 ) ) )
317 /* Tiny hack! */
318 GSList *l = g_slist_append( NULL, iu );
319 irc_send_who( irc, l, channel );
320 g_slist_free( l );
322 else
323 irc_send_num( irc, 403, "%s :No such channel", channel );
326 static void irc_cmd_privmsg( irc_t *irc, char **cmd )
328 irc_channel_t *ic;
329 irc_user_t *iu;
331 if( !cmd[2] )
333 irc_send_num( irc, 412, ":No text to send" );
334 return;
337 /* Don't treat CTCP actions as real CTCPs, just convert them right now. */
338 if( g_strncasecmp( cmd[2], "\001ACTION", 7 ) == 0 )
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';
346 if( irc_channel_name_ok( cmd[1] ) &&
347 ( ic = irc_channel_by_name( irc, cmd[1] ) ) )
349 if( cmd[2][0] == '\001' )
351 /* CTCPs to channels? Nah. Maybe later. */
353 else if( ic->f->privmsg )
354 ic->f->privmsg( ic, cmd[2] );
356 else if( ( iu = irc_user_by_name( irc, cmd[1] ) ) )
358 if( cmd[2][0] == '\001' )
360 char **ctcp;
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';
367 ctcp = split_command_parts( cmd[2] + 1 );
368 iu->f->ctcp( iu, ctcp );
370 else if( iu->f->privmsg )
372 iu->last_channel = NULL;
373 iu->f->privmsg( iu, cmd[2] );
376 else
378 irc_send_num( irc, 401, "%s :No such nick/channel", cmd[1] );
382 static void irc_cmd_notice( irc_t *irc, char **cmd )
384 if( !cmd[2] )
386 irc_send_num( irc, 412, ":No text to send" );
387 return;
390 /* At least for now just echo. IIRC some IRC clients use self-notices
391 for lag checks, so try to support that. */
392 if( nick_cmp( cmd[1], irc->user->nick ) == 0 )
393 irc_send_msg( irc->user, "NOTICE", irc->user->nick, cmd[2], NULL );
396 static void irc_cmd_nickserv( irc_t *irc, char **cmd )
398 /* [SH] This aliases the NickServ command to PRIVMSG root */
399 /* [TV] This aliases the NS command to PRIVMSG root as well */
400 root_command( irc, cmd + 1 );
403 static void irc_cmd_oper_hack( irc_t *irc, char **cmd );
405 static void irc_cmd_oper( irc_t *irc, char **cmd )
407 /* Very non-standard evil but useful/secure hack, see below. */
408 if( irc->status & OPER_HACK_ANY )
409 return irc_cmd_oper_hack( irc, cmd );
411 if( global.conf->oper_pass &&
412 ( strncmp( global.conf->oper_pass, "md5:", 4 ) == 0 ?
413 md5_verify_password( cmd[2], global.conf->oper_pass + 4 ) == 0 :
414 strcmp( cmd[2], global.conf->oper_pass ) == 0 ) )
416 irc_umode_set( irc, "+o", 1 );
417 irc_send_num( irc, 381, ":Password accepted" );
419 else
421 irc_send_num( irc, 491, ":Incorrect password" );
425 static void irc_cmd_oper_hack( irc_t *irc, char **cmd )
427 char *password = g_strjoinv( " ", cmd + 2 );
429 /* /OPER can now also be used to enter IM/identify passwords without
430 echoing. It's a hack but the extra password security is worth it. */
431 if( irc->status & OPER_HACK_ACCOUNT_ADD )
433 account_t *a;
435 for( a = irc->b->accounts; a; a = a->next )
436 if( strcmp( a->pass, PASSWORD_PENDING ) == 0 )
438 set_setstr( &a->set, "password", password );
439 irc_rootmsg( irc, "Password added to IM account "
440 "%s", a->tag );
441 /* The IRC client may expect this. 491 suggests the OPER
442 password was wrong, so the client won't expect a +o.
443 It may however repeat the password prompt. We'll see. */
444 irc_send_num( irc, 491, ":Password added to IM account "
445 "%s", a->tag );
448 else if( irc->status & OPER_HACK_IDENTIFY )
450 char *send_cmd[] = { "identify", password, NULL, NULL };
451 irc->status &= ~OPER_HACK_IDENTIFY;
452 if( irc->status & OPER_HACK_IDENTIFY_NOLOAD )
454 send_cmd[1] = "-noload";
455 send_cmd[2] = password;
457 else if( irc->status & OPER_HACK_IDENTIFY_FORCE )
459 send_cmd[1] = "-force";
460 send_cmd[2] = password;
462 irc_send_num( irc, 491, ":Trying to identify" );
463 root_command( irc, send_cmd );
465 else if( irc->status & OPER_HACK_REGISTER )
467 char *send_cmd[] = { "register", password, NULL };
468 irc_send_num( irc, 491, ":Trying to identify" );
469 root_command( irc, send_cmd );
472 irc->status &= ~OPER_HACK_ANY;
473 g_free( password );
476 static void irc_cmd_invite( irc_t *irc, char **cmd )
478 irc_channel_t *ic;
479 irc_user_t *iu;
481 if( ( iu = irc_user_by_name( irc, cmd[1] ) ) == NULL )
483 irc_send_num( irc, 401, "%s :No such nick", cmd[1] );
484 return;
486 else if( ( ic = irc_channel_by_name( irc, cmd[2] ) ) == NULL )
488 irc_send_num( irc, 403, "%s :No such channel", cmd[2] );
489 return;
492 if( !ic->f->invite )
493 irc_send_num( irc, 482, "%s :Can't invite people here", cmd[2] );
494 else if( ic->f->invite( ic, iu ) )
495 irc_send_num( irc, 341, "%s %s", iu->nick, ic->name );
498 static void irc_cmd_userhost( irc_t *irc, char **cmd )
500 int i;
502 /* [TV] Usable USERHOST-implementation according to
503 RFC1459. Without this, mIRC shows an error
504 while connecting, and the used way of rejecting
505 breaks standards.
508 for( i = 1; cmd[i]; i ++ )
510 irc_user_t *iu = irc_user_by_name( irc, cmd[i] );
512 if( iu )
513 irc_send_num( irc, 302, ":%s=%c%s@%s", iu->nick,
514 irc_user_get_away( iu ) ? '-' : '+',
515 iu->user, iu->host );
519 static void irc_cmd_ison( irc_t *irc, char **cmd )
521 char buff[IRC_MAX_LINE];
522 int lenleft, i;
524 buff[0] = '\0';
526 /* [SH] Leave room for : and \0 */
527 lenleft = IRC_MAX_LINE - 2;
529 for( i = 1; cmd[i]; i ++ )
531 char *this, *next;
533 this = cmd[i];
534 while( *this )
536 irc_user_t *iu;
538 if( ( next = strchr( this, ' ' ) ) )
539 *next = 0;
541 if( ( iu = irc_user_by_name( irc, this ) ) &&
542 iu->bu && iu->bu->flags & BEE_USER_ONLINE )
544 lenleft -= strlen( iu->nick ) + 1;
546 if( lenleft < 0 )
547 break;
549 strcat( buff, iu->nick );
550 strcat( buff, " " );
553 if( next )
555 *next = ' ';
556 this = next + 1;
558 else
560 break;
564 /* *sigh* */
565 if( lenleft < 0 )
566 break;
569 if( strlen( buff ) > 0 )
570 buff[strlen(buff)-1] = '\0';
572 irc_send_num( irc, 303, ":%s", buff );
575 static void irc_cmd_watch( irc_t *irc, char **cmd )
577 int i;
579 /* Obviously we could also mark a user structure as being
580 watched, but what if the WATCH command is sent right
581 after connecting? The user won't exist yet then... */
582 for( i = 1; cmd[i]; i ++ )
584 char *nick;
585 irc_user_t *iu;
587 if( !cmd[i][0] || !cmd[i][1] )
588 break;
590 nick = g_strdup( cmd[i] + 1 );
591 nick_lc( nick );
593 iu = irc_user_by_name( irc, nick );
595 if( cmd[i][0] == '+' )
597 if( !g_hash_table_lookup( irc->watches, nick ) )
598 g_hash_table_insert( irc->watches, nick, nick );
600 if( iu && iu->bu && iu->bu->flags & BEE_USER_ONLINE )
601 irc_send_num( irc, 604, "%s %s %s %d :%s", iu->nick, iu->user,
602 iu->host, (int) time( NULL ), "is online" );
603 else
604 irc_send_num( irc, 605, "%s %s %s %d :%s", nick, "*", "*",
605 (int) time( NULL ), "is offline" );
607 else if( cmd[i][0] == '-' )
609 gpointer okey, ovalue;
611 if( g_hash_table_lookup_extended( irc->watches, nick, &okey, &ovalue ) )
613 g_hash_table_remove( irc->watches, okey );
614 g_free( okey );
616 irc_send_num( irc, 602, "%s %s %s %d :%s", nick, "*", "*", 0, "Stopped watching" );
622 static void irc_cmd_topic( irc_t *irc, char **cmd )
624 irc_channel_t *ic = irc_channel_by_name( irc, cmd[1] );
625 const char *new = cmd[2];
627 if( ic == NULL )
629 irc_send_num( irc, 403, "%s :No such channel", cmd[1] );
631 else if( new )
633 if( ic->f->topic == NULL )
634 irc_send_num( irc, 482, "%s :Can't change this channel's topic", ic->name );
635 else if( ic->f->topic( ic, new ) )
636 irc_send_topic( ic, TRUE );
638 else
640 irc_send_topic( ic, FALSE );
644 static void irc_cmd_away( irc_t *irc, char **cmd )
646 if( cmd[1] && *cmd[1] )
648 char away[strlen(cmd[1])+1];
649 int i, j;
651 /* Copy away string, but skip control chars. Mainly because
652 Jabber really doesn't like them. */
653 for( i = j = 0; cmd[1][i]; i ++ )
654 if( (unsigned char) ( away[j] = cmd[1][i] ) >= ' ' )
655 j ++;
656 away[j] = '\0';
658 irc_send_num( irc, 306, ":You're now away: %s", away );
659 set_setstr( &irc->b->set, "away", away );
661 else
663 irc_send_num( irc, 305, ":Welcome back" );
664 set_setstr( &irc->b->set, "away", NULL );
668 static void irc_cmd_list( irc_t *irc, char **cmd )
670 GSList *l;
672 for( l = irc->channels; l; l = l->next )
674 irc_channel_t *ic = l->data;
676 irc_send_num( irc, 322, "%s %d :%s",
677 ic->name, g_slist_length( ic->users ), ic->topic ? : "" );
679 irc_send_num( irc, 323, ":%s", "End of /LIST" );
682 static void irc_cmd_version( irc_t *irc, char **cmd )
684 irc_send_num( irc, 351, "%s-%s. %s :%s/%s ",
685 PACKAGE, BITLBEE_VERSION, irc->root->host, ARCH, CPU );
688 static void irc_cmd_completions( irc_t *irc, char **cmd )
690 help_t *h;
691 set_t *s;
692 int i;
694 irc_send_msg_raw( irc->root, "NOTICE", irc->user->nick, "COMPLETIONS OK" );
696 for( i = 0; root_commands[i].command; i ++ )
697 irc_send_msg_f( irc->root, "NOTICE", irc->user->nick, "COMPLETIONS %s", root_commands[i].command );
699 for( h = global.help; h; h = h->next )
700 irc_send_msg_f( irc->root, "NOTICE", irc->user->nick, "COMPLETIONS help %s", h->title );
702 for( s = irc->b->set; s; s = s->next )
703 irc_send_msg_f( irc->root, "NOTICE", irc->user->nick, "COMPLETIONS set %s", s->key );
705 irc_send_msg_raw( irc->root, "NOTICE", irc->user->nick, "COMPLETIONS END" );
708 static void irc_cmd_rehash( irc_t *irc, char **cmd )
710 if( global.conf->runmode == RUNMODE_INETD )
711 ipc_master_cmd_rehash( NULL, NULL );
712 else
713 ipc_to_master( cmd );
715 irc_send_num( irc, 382, "%s :Rehashing", global.conf_file );
718 static const command_t irc_commands[] = {
719 { "pass", 1, irc_cmd_pass, 0 },
720 { "user", 4, irc_cmd_user, IRC_CMD_PRE_LOGIN },
721 { "nick", 1, irc_cmd_nick, 0 },
722 { "quit", 0, irc_cmd_quit, 0 },
723 { "ping", 0, irc_cmd_ping, 0 },
724 { "pong", 0, irc_cmd_pong, IRC_CMD_LOGGED_IN },
725 { "join", 1, irc_cmd_join, IRC_CMD_LOGGED_IN },
726 { "names", 1, irc_cmd_names, IRC_CMD_LOGGED_IN },
727 { "part", 1, irc_cmd_part, IRC_CMD_LOGGED_IN },
728 { "whois", 1, irc_cmd_whois, IRC_CMD_LOGGED_IN },
729 { "whowas", 1, irc_cmd_whowas, IRC_CMD_LOGGED_IN },
730 { "motd", 0, irc_cmd_motd, IRC_CMD_LOGGED_IN },
731 { "mode", 1, irc_cmd_mode, IRC_CMD_LOGGED_IN },
732 { "who", 0, irc_cmd_who, IRC_CMD_LOGGED_IN },
733 { "privmsg", 1, irc_cmd_privmsg, IRC_CMD_LOGGED_IN },
734 { "notice", 1, irc_cmd_notice, IRC_CMD_LOGGED_IN },
735 { "nickserv", 1, irc_cmd_nickserv, IRC_CMD_LOGGED_IN },
736 { "ns", 1, irc_cmd_nickserv, IRC_CMD_LOGGED_IN },
737 { "away", 0, irc_cmd_away, IRC_CMD_LOGGED_IN },
738 { "version", 0, irc_cmd_version, IRC_CMD_LOGGED_IN },
739 { "completions", 0, irc_cmd_completions, IRC_CMD_LOGGED_IN },
740 { "userhost", 1, irc_cmd_userhost, IRC_CMD_LOGGED_IN },
741 { "ison", 1, irc_cmd_ison, IRC_CMD_LOGGED_IN },
742 { "watch", 1, irc_cmd_watch, IRC_CMD_LOGGED_IN },
743 { "invite", 2, irc_cmd_invite, IRC_CMD_LOGGED_IN },
744 { "topic", 1, irc_cmd_topic, IRC_CMD_LOGGED_IN },
745 { "oper", 2, irc_cmd_oper, IRC_CMD_LOGGED_IN },
746 { "list", 0, irc_cmd_list, IRC_CMD_LOGGED_IN },
747 { "die", 0, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
748 { "deaf", 0, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
749 { "wallops", 1, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
750 { "wall", 1, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
751 { "rehash", 0, irc_cmd_rehash, IRC_CMD_OPER_ONLY },
752 { "restart", 0, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
753 { "kill", 2, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
754 { NULL }
757 void irc_exec( irc_t *irc, char *cmd[] )
759 int i, n_arg;
761 if( !cmd[0] )
762 return;
764 for( i = 0; irc_commands[i].command; i++ )
765 if( g_strcasecmp( irc_commands[i].command, cmd[0] ) == 0 )
767 /* There should be no typo in the next line: */
768 for( n_arg = 0; cmd[n_arg]; n_arg ++ ); n_arg --;
770 if( irc_commands[i].flags & IRC_CMD_PRE_LOGIN && irc->status & USTATUS_LOGGED_IN )
772 irc_send_num( irc, 462, ":Only allowed before logging in" );
774 else if( irc_commands[i].flags & IRC_CMD_LOGGED_IN && !( irc->status & USTATUS_LOGGED_IN ) )
776 irc_send_num( irc, 451, ":Register first" );
778 else if( irc_commands[i].flags & IRC_CMD_OPER_ONLY && !strchr( irc->umode, 'o' ) )
780 irc_send_num( irc, 481, ":Permission denied - You're not an IRC operator" );
782 else if( n_arg < irc_commands[i].required_parameters )
784 irc_send_num( irc, 461, "%s :Need more parameters", cmd[0] );
786 else if( irc_commands[i].flags & IRC_CMD_TO_MASTER )
788 /* IPC doesn't make sense in inetd mode,
789 but the function will catch that. */
790 ipc_to_master( cmd );
792 else
794 irc_commands[i].execute( irc, cmd );
797 return;
800 if( irc->status & USTATUS_LOGGED_IN )
801 irc_send_num( irc, 421, "%s :Unknown command", cmd[0] );