source: irc_commands.c @ 6ee51a9

Last change on this file since 6ee51a9 was 18e1f3b, checked in by Wilmer van der Gaast <wilmer@…>, at 2012-05-02T07:58:22Z

Set PACKAGE to BitlBee-LIBPURPLE for the libpurple variant, because in many
ways it's not BitlBee and I'm tired of getting libpurple-related bug reports.

  • Property mode set to 100644
File size: 21.6 KB
RevLine 
[0298d11]1  /********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
[3ddb7477]4  * Copyright 2002-2010 Wilmer van der Gaast and others                *
[0298d11]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"
[674a01d]28#include "help.h"
[0431ea1]29#include "ipc.h"
[0298d11]30
[f73b969]31static void irc_cmd_pass( irc_t *irc, char **cmd )
[0298d11]32{
[a199d33]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. */
[daae10f]41                root_command( irc, send_cmd );
42                return;
[a199d33]43        }
44        /* Handling in pre-logged-in state, first see if this server is
45           password-protected: */
46        else if( global.conf->auth_pass &&
[c029350]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 ) )
[0298d11]50        {
[79e826a]51                irc->status |= USTATUS_AUTHORIZED;
[de3e100]52                irc_check_login( irc );
[0298d11]53        }
[a199d33]54        else if( global.conf->auth_pass )
[0298d11]55        {
[3ddb7477]56                irc_send_num( irc, 464, ":Incorrect password" );
[0298d11]57        }
[a199d33]58        else
59        {
60                /* Remember the password and try to identify after USER/NICK. */
[70f69ecc]61                irc_setpass( irc, cmd[1] );
[a199d33]62                irc_check_login( irc );
63        }
[0298d11]64}
65
[f73b969]66static void irc_cmd_user( irc_t *irc, char **cmd )
[0298d11]67{
[3ddb7477]68        irc->user->user = g_strdup( cmd[1] );
69        irc->user->fullname = g_strdup( cmd[4] );
[edf9657]70       
71        irc_check_login( irc );
[0298d11]72}
73
[f73b969]74static void irc_cmd_nick( irc_t *irc, char **cmd )
[0298d11]75{
[9a9b520]76        irc_user_t *iu;
77       
78        if( ( iu = irc_user_by_name( irc, cmd[1] ) ) && iu != irc->user )
[0298d11]79        {
[9a9b520]80                irc_send_num( irc, 433, "%s :This nick is already in use", cmd[1] );
[0298d11]81        }
82        else if( !nick_ok( cmd[1] ) )
83        {
84                /* [SH] Invalid characters. */
[9a9b520]85                irc_send_num( irc, 432, "%s :This nick contains invalid characters", cmd[1] );
[0298d11]86        }
[9a9b520]87        else if( irc->status & USTATUS_LOGGED_IN )
[ffa1173]88        {
[3558fea]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 )
[ffa1173]93                {
94                        irc_setpass( irc, NULL );
95                        irc->status &= ~USTATUS_IDENTIFIED;
96                        irc_umode_set( irc, "-R", 1 );
[e67e513]97                        irc_rootmsg( irc, "Changing nicks resets your identify status. "
[92cb8c4]98                                     "Re-identify or register a new account if you want "
99                                     "your configuration to be saved. See \x02help "
100                                     "nick_changes\x02." );
[ffa1173]101                }
102               
[3558fea]103                if( strcmp( cmd[1], irc->user->nick ) != 0 )
104                        irc_user_set_nick( irc->user, cmd[1] );
[ffa1173]105        }
[0298d11]106        else
107        {
[9a9b520]108                g_free( irc->user->nick );
[3ddb7477]109                irc->user->nick = g_strdup( cmd[1] );
[edf9657]110               
111                irc_check_login( irc );
[0298d11]112        }
113}
114
[f73b969]115static void irc_cmd_quit( irc_t *irc, char **cmd )
[0298d11]116{
[f73b969]117        if( cmd[1] && *cmd[1] )
118                irc_abort( irc, 0, "Quit: %s", cmd[1] );
119        else
120                irc_abort( irc, 0, "Leaving..." );
[0298d11]121}
122
[f73b969]123static void irc_cmd_ping( irc_t *irc, char **cmd )
[0298d11]124{
[ebaebfe]125        irc_write( irc, ":%s PONG %s :%s", irc->root->host,
126                   irc->root->host, cmd[1]?cmd[1]:irc->root->host );
[0298d11]127}
128
[3923003]129static void irc_cmd_pong( irc_t *irc, char **cmd )
130{
131        /* We could check the value we get back from the user, but in
132           fact we don't care, we're just happy s/he's still alive. */
133        irc->last_pong = gettime();
134        irc->pinging = 0;
135}
136
[b9e020a]137static void irc_cmd_join( irc_t *irc, char **cmd )
138{
[58646d9]139        char *comma, *s = cmd[1];
[b9e020a]140       
[58646d9]141        while( s )
[57119e8]142        {
[58646d9]143                irc_channel_t *ic;
144               
145                if( ( comma = strchr( s, ',' ) ) )
146                        *comma = '\0';
147               
[13fa2db]148                if( ( ic = irc_channel_by_name( irc, s ) ) == NULL &&
149                    ( ic = irc_channel_new( irc, s ) ) )
[324c378]150                {
151                        if( strcmp( set_getstr( &ic->set, "type" ), "control" ) != 0 )
152                        {
153                                /* Autoconfiguration is for control channels only ATM. */
154                        }
155                        else if( bee_group_by_name( ic->irc->b, ic->name + 1, FALSE ) )
156                        {
157                                set_setstr( &ic->set, "group", ic->name + 1 );
158                                set_setstr( &ic->set, "fill_by", "group" );
159                        }
160                        else if( set_setstr( &ic->set, "protocol", ic->name + 1 ) )
161                        {
162                                set_setstr( &ic->set, "fill_by", "protocol" );
163                        }
164                        else if( set_setstr( &ic->set, "account", ic->name + 1 ) )
165                        {
166                                set_setstr( &ic->set, "fill_by", "account" );
167                        }
168                        else
169                        {
[13fa2db]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. */
[324c378]173                                bee_irc_channel_update( ic->irc, ic, NULL );
174                        }
175                }
[13fa2db]176                else if( ic == NULL )
[58646d9]177                {
178                        irc_send_num( irc, 479, "%s :Invalid channel name", s );
179                        goto next;
180                }
181               
182                if( ic->flags & IRC_CHANNEL_JOINED )
183                        /* Dude, you're already there...
184                           RFC doesn't have any reply for that though? */
185                        goto next;
186               
187                if( ic->f->join && !ic->f->join( ic ) )
188                        /* The story is: FALSE either means the handler
189                           showed an error message, or is doing some work
190                           before the join should be confirmed. (In the
191                           latter case, the caller should take care of that
192                           confirmation.) TRUE means all's good, let the
193                           user join the channel right away. */
194                        goto next;
195               
196                irc_channel_add_user( ic, irc->user );
197               
198next:
199                if( comma )
200                {
201                        s = comma + 1;
202                        *comma = ',';
203                }
204                else
205                        break;
[57119e8]206        }
[b9e020a]207}
208
209static void irc_cmd_names( irc_t *irc, char **cmd )
210{
211        irc_channel_t *ic;
212       
213        if( cmd[1] && ( ic = irc_channel_by_name( irc, cmd[1] ) ) )
214                irc_send_names( ic );
215        /* With no args, we should show /names of all chans. Make the code
216           below work well if necessary.
217        else
218        {
219                GSList *l;
220               
221                for( l = irc->channels; l; l = l->next )
222                        irc_send_names( l->data );
223        }
224        */
225}
226
227static void irc_cmd_part( irc_t *irc, char **cmd )
228{
229        irc_channel_t *ic;
230       
231        if( ( ic = irc_channel_by_name( irc, cmd[1] ) ) == NULL )
232        {
233                irc_send_num( irc, 403, "%s :No such channel", cmd[1] );
234        }
[b1af3e8]235        else if( irc_channel_del_user( ic, irc->user, IRC_CDU_PART, cmd[2] ) )
[47fae0f]236        {
237                if( ic->f->part )
238                        ic->f->part( ic, NULL );
239        }
240        else
[b9e020a]241        {
242                irc_send_num( irc, 442, "%s :You're not on that channel", cmd[1] );
243        }
244}
245
[b95932e]246static void irc_cmd_whois( irc_t *irc, char **cmd )
247{
248        char *nick = cmd[1];
[280c56a]249        irc_user_t *iu = irc_user_by_name( irc, nick );
[b95932e]250       
251        if( iu )
252                irc_send_whois( iu );
253        else
254                irc_send_num( irc, 401, "%s :Nick does not exist", nick );
255}
256
257static void irc_cmd_whowas( irc_t *irc, char **cmd )
258{
259        /* For some reason irssi tries a whowas when whois fails. We can
260           ignore this, but then the user never gets a "user not found"
261           message from irssi which is a bit annoying. So just respond
262           with not-found and irssi users will get better error messages */
263       
264        irc_send_num( irc, 406, "%s :Nick does not exist", cmd[1] );
265        irc_send_num( irc, 369, "%s :End of WHOWAS", cmd[1] );
266}
267
[9b69eb7]268static void irc_cmd_motd( irc_t *irc, char **cmd )
269{
270        irc_send_motd( irc );
271}
272
[f73b969]273static void irc_cmd_mode( irc_t *irc, char **cmd )
[0298d11]274{
[b919363]275        if( irc_channel_name_ok( cmd[1] ) )
[0298d11]276        {
[b919363]277                irc_channel_t *ic;
278               
279                if( ( ic = irc_channel_by_name( irc, cmd[1] ) ) == NULL )
280                        irc_send_num( irc, 403, "%s :No such channel", cmd[1] );
281                else if( cmd[2] )
[0298d11]282                {
283                        if( *cmd[2] == '+' || *cmd[2] == '-' )
[3ddb7477]284                                irc_send_num( irc, 477, "%s :Can't change channel modes", cmd[1] );
[0298d11]285                        else if( *cmd[2] == 'b' )
[3ddb7477]286                                irc_send_num( irc, 368, "%s :No bans possible", cmd[1] );
[0298d11]287                }
288                else
[b919363]289                        irc_send_num( irc, 324, "%s +%s", cmd[1], ic->mode );
[0298d11]290        }
291        else
292        {
[b919363]293                if( nick_cmp( cmd[1], irc->user->nick ) == 0 )
[0298d11]294                {
295                        if( cmd[2] )
296                                irc_umode_set( irc, cmd[2], 0 );
[2f13222]297                        else
[3ddb7477]298                                irc_send_num( irc, 221, "+%s", irc->umode );
[0298d11]299                }
300                else
[3ddb7477]301                        irc_send_num( irc, 502, ":Don't touch their modes" );
[0298d11]302        }
303}
304
[2f53ada]305static void irc_cmd_who( irc_t *irc, char **cmd )
306{
307        char *channel = cmd[1];
308        irc_channel_t *ic;
[a72af0d]309        irc_user_t *iu;
[2f53ada]310       
311        if( !channel || *channel == '0' || *channel == '*' || !*channel )
312                irc_send_who( irc, irc->users, "**" );
313        else if( ( ic = irc_channel_by_name( irc, channel ) ) )
314                irc_send_who( irc, ic->users, channel );
[a72af0d]315        else if( ( iu = irc_user_by_name( irc, channel ) ) )
316        {
317                /* Tiny hack! */
318                GSList *l = g_slist_append( NULL, iu );
319                irc_send_who( irc, l, channel );
320                g_slist_free( l );
321        }
[2f53ada]322        else
323                irc_send_num( irc, 403, "%s :No such channel", channel );
324}
325
[280c56a]326static void irc_cmd_privmsg( irc_t *irc, char **cmd )
[b919363]327{
[280c56a]328        irc_channel_t *ic;
329        irc_user_t *iu;
330       
331        if( !cmd[2] ) 
[b919363]332        {
[280c56a]333                irc_send_num( irc, 412, ":No text to send" );
[24b8bbb]334                return;
[280c56a]335        }
[24b8bbb]336       
337        /* Don't treat CTCP actions as real CTCPs, just convert them right now. */
338        if( g_strncasecmp( cmd[2], "\001ACTION", 7 ) == 0 )
339        {
340                cmd[2] += 4;
[e1f3f94]341                memcpy( cmd[2], "/me", 3 );
[24b8bbb]342                if( cmd[2][strlen(cmd[2])-1] == '\001' )
343                        cmd[2][strlen(cmd[2])-1] = '\0';
344        }
345       
346        if( irc_channel_name_ok( cmd[1] ) &&
347            ( ic = irc_channel_by_name( irc, cmd[1] ) ) )
[280c56a]348        {
[a7dbf45]349                if( cmd[2][0] == '\001' )
350                {
351                        /* CTCPs to channels? Nah. Maybe later. */
352                }
353                else if( ic->f->privmsg )
[280c56a]354                        ic->f->privmsg( ic, cmd[2] );
355        }
356        else if( ( iu = irc_user_by_name( irc, cmd[1] ) ) )
357        {
[24b8bbb]358                if( cmd[2][0] == '\001' )
359                {
360                        char **ctcp;
361                       
362                        if( iu->f->ctcp == NULL )
363                                return;
364                        if( cmd[2][strlen(cmd[2])-1] == '\001' )
365                                cmd[2][strlen(cmd[2])-1] = '\0';
366                       
367                        ctcp = split_command_parts( cmd[2] + 1 );
368                        iu->f->ctcp( iu, ctcp );
369                }
370                else if( iu->f->privmsg )
[bce78c8]371                {
[92c8d41]372                        iu->last_channel = NULL;
[280c56a]373                        iu->f->privmsg( iu, cmd[2] );
[bce78c8]374                }
[b919363]375        }
376        else
377        {
[280c56a]378                irc_send_num( irc, 401, "%s :No such nick/channel", cmd[1] );
[b919363]379        }
[280c56a]380}
381
[d7f8500]382static void irc_cmd_notice( irc_t *irc, char **cmd )
383{
384        if( !cmd[2] ) 
385        {
386                irc_send_num( irc, 412, ":No text to send" );
387                return;
388        }
389       
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 );
394}
395
[d860a8d]396static void irc_cmd_nickserv( irc_t *irc, char **cmd )
397{
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 );
401}
402
[060d066]403static void irc_cmd_oper_hack( irc_t *irc, char **cmd );
[280c56a]404
405static void irc_cmd_oper( irc_t *irc, char **cmd )
406{
[060d066]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 );
[9564e55]410       
[280c56a]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 ) )
415        {
416                irc_umode_set( irc, "+o", 1 );
417                irc_send_num( irc, 381, ":Password accepted" );
418        }
419        else
420        {
[bedad20]421                irc_send_num( irc, 491, ":Incorrect password" );
[280c56a]422        }
423}
424
[060d066]425static void irc_cmd_oper_hack( irc_t *irc, char **cmd )
426{
427        char *password = g_strjoinv( " ", cmd + 2 );
428       
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 )
432        {
433                account_t *a;
434               
435                for( a = irc->b->accounts; a; a = a->next )
436                        if( strcmp( a->pass, PASSWORD_PENDING ) == 0 )
437                        {
438                                set_setstr( &a->set, "password", password );
[e67e513]439                                irc_rootmsg( irc, "Password added to IM account "
[17f057d]440                                             "%s", a->tag );
[060d066]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 "
[17f057d]445                                              "%s", a->tag );
[060d066]446                        }
447        }
448        else if( irc->status & OPER_HACK_IDENTIFY )
449        {
[fda194f]450                char *send_cmd[] = { "identify", password, NULL, NULL };
451                irc->status &= ~OPER_HACK_IDENTIFY;
452                if( irc->status & OPER_HACK_IDENTIFY_NOLOAD )
453                {
454                        send_cmd[1] = "-noload";
455                        send_cmd[2] = password;
456                }
457                else if( irc->status & OPER_HACK_IDENTIFY_FORCE )
458                {
459                        send_cmd[1] = "-force";
460                        send_cmd[2] = password;
461                }
[060d066]462                irc_send_num( irc, 491, ":Trying to identify" );
463                root_command( irc, send_cmd );
464        }
465        else if( irc->status & OPER_HACK_REGISTER )
466        {
467                char *send_cmd[] = { "register", password, NULL };
468                irc_send_num( irc, 491, ":Trying to identify" );
469                root_command( irc, send_cmd );
470        }
471       
472        irc->status &= ~OPER_HACK_ANY;
473        g_free( password );
474}
475
[280c56a]476static void irc_cmd_invite( irc_t *irc, char **cmd )
477{
[66b9e36a]478        irc_channel_t *ic;
479        irc_user_t *iu;
[280c56a]480       
[66b9e36a]481        if( ( iu = irc_user_by_name( irc, cmd[1] ) ) == NULL )
482        {
483                irc_send_num( irc, 401, "%s :No such nick", cmd[1] );
484                return;
485        }
486        else if( ( ic = irc_channel_by_name( irc, cmd[2] ) ) == NULL )
487        {
488                irc_send_num( irc, 403, "%s :No such channel", cmd[2] );
489                return;
490        }
[280c56a]491       
[46d215d]492        if( !ic->f->invite )
[66b9e36a]493                irc_send_num( irc, 482, "%s :Can't invite people here", cmd[2] );
[46d215d]494        else if( ic->f->invite( ic, iu ) )
495                irc_send_num( irc, 341, "%s %s", iu->nick, ic->name );
[0298d11]496}
497
[f73b969]498static void irc_cmd_userhost( irc_t *irc, char **cmd )
[0298d11]499{
500        int i;
501       
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.
506        */
507       
508        for( i = 1; cmd[i]; i ++ )
[003a12b]509        {
510                irc_user_t *iu = irc_user_by_name( irc, cmd[i] );
511               
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 );
516        }
[0298d11]517}
518
[f73b969]519static void irc_cmd_ison( irc_t *irc, char **cmd )
[0298d11]520{
[b4e4b95]521        char buff[IRC_MAX_LINE];
[0298d11]522        int lenleft, i;
523       
524        buff[0] = '\0';
525       
526        /* [SH] Leave room for : and \0 */
527        lenleft = IRC_MAX_LINE - 2;
528       
529        for( i = 1; cmd[i]; i ++ )
530        {
[42616d1]531                char *this, *next;
532               
533                this = cmd[i];
534                while( *this )
[0298d11]535                {
[003a12b]536                        irc_user_t *iu;
537                       
[42616d1]538                        if( ( next = strchr( this, ' ' ) ) )
539                                *next = 0;
[0298d11]540                       
[003a12b]541                        if( ( iu = irc_user_by_name( irc, this ) ) &&
542                            iu->bu && iu->bu->flags & BEE_USER_ONLINE )
[0298d11]543                        {
[003a12b]544                                lenleft -= strlen( iu->nick ) + 1;
[42616d1]545                               
546                                if( lenleft < 0 )
547                                        break;
548                               
[003a12b]549                                strcat( buff, iu->nick );
[42616d1]550                                strcat( buff, " " );
[0298d11]551                        }
552                       
[42616d1]553                        if( next )
554                        {
555                                *next = ' ';
556                                this = next + 1;
557                        }
558                        else
559                        {
560                                break;
561                        }   
[0298d11]562                }
[42616d1]563               
564                /* *sigh* */
565                if( lenleft < 0 )
566                        break;
[0298d11]567        }
568       
569        if( strlen( buff ) > 0 )
570                buff[strlen(buff)-1] = '\0';
571       
[3ddb7477]572        irc_send_num( irc, 303, ":%s", buff );
[0298d11]573}
574
[f73b969]575static void irc_cmd_watch( irc_t *irc, char **cmd )
[0298d11]576{
577        int i;
578       
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 ++ )
583        {
584                char *nick;
[003a12b]585                irc_user_t *iu;
[0298d11]586               
587                if( !cmd[i][0] || !cmd[i][1] )
588                        break;
589               
590                nick = g_strdup( cmd[i] + 1 );
591                nick_lc( nick );
592               
[003a12b]593                iu = irc_user_by_name( irc, nick );
[0298d11]594               
595                if( cmd[i][0] == '+' )
596                {
597                        if( !g_hash_table_lookup( irc->watches, nick ) )
598                                g_hash_table_insert( irc->watches, nick, nick );
599                       
[003a12b]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" );
[0298d11]603                        else
[003a12b]604                                irc_send_num( irc, 605, "%s %s %s %d :%s", nick, "*", "*",
605                                              (int) time( NULL ), "is offline" );
[0298d11]606                }
607                else if( cmd[i][0] == '-' )
608                {
609                        gpointer okey, ovalue;
610                       
611                        if( g_hash_table_lookup_extended( irc->watches, nick, &okey, &ovalue ) )
612                        {
613                                g_hash_table_remove( irc->watches, okey );
[e59b4f6]614                                g_free( okey );
[0298d11]615                               
[3ddb7477]616                                irc_send_num( irc, 602, "%s %s %s %d :%s", nick, "*", "*", 0, "Stopped watching" );
[0298d11]617                        }
618                }
619        }
620}
621
[f73b969]622static void irc_cmd_topic( irc_t *irc, char **cmd )
[0298d11]623{
[4469e7e]624        irc_channel_t *ic = irc_channel_by_name( irc, cmd[1] );
625        const char *new = cmd[2];
[50e1776]626       
[4469e7e]627        if( ic == NULL )
[50e1776]628        {
[4469e7e]629                irc_send_num( irc, 403, "%s :No such channel", cmd[1] );
630        }
631        else if( new )
632        {
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 );
[50e1776]637        }
[0298d11]638        else
[50e1776]639        {
[4469e7e]640                irc_send_topic( ic, FALSE );
[50e1776]641        }
[0298d11]642}
643
[f73b969]644static void irc_cmd_away( irc_t *irc, char **cmd )
[0298d11]645{
[81186cab]646        if( cmd[1] && *cmd[1] )
[0298d11]647        {
[81186cab]648                char away[strlen(cmd[1])+1];
[0298d11]649                int i, j;
650               
651                /* Copy away string, but skip control chars. Mainly because
652                   Jabber really doesn't like them. */
[81186cab]653                for( i = j = 0; cmd[1][i]; i ++ )
[14df98f]654                        if( (unsigned char) ( away[j] = cmd[1][i] ) >= ' ' )
[0298d11]655                                j ++;
[81186cab]656                away[j] = '\0';
[0298d11]657               
[81186cab]658                irc_send_num( irc, 306, ":You're now away: %s", away );
[4a9fd5f]659                set_setstr( &irc->b->set, "away", away );
[0298d11]660        }
661        else
662        {
[3ddb7477]663                irc_send_num( irc, 305, ":Welcome back" );
[4a9fd5f]664                set_setstr( &irc->b->set, "away", NULL );
[0298d11]665        }
666}
667
[23d6165]668static void irc_cmd_list( irc_t *irc, char **cmd )
669{
670        GSList *l;
671       
672        for( l = irc->channels; l; l = l->next )
673        {
674                irc_channel_t *ic = l->data;
675               
676                irc_send_num( irc, 322, "%s %d :%s",
677                              ic->name, g_slist_length( ic->users ), ic->topic ? : "" );
678        }
679        irc_send_num( irc, 323, ":%s", "End of /LIST" );
680}
681
[82898af]682static void irc_cmd_version( irc_t *irc, char **cmd )
683{
[18e1f3b]684        irc_send_num( irc, 351, "%s-%s. %s :%s/%s ",
685                      PACKAGE, BITLBEE_VERSION, irc->root->host, ARCH, CPU );
[82898af]686}
687
[f73b969]688static void irc_cmd_completions( irc_t *irc, char **cmd )
[0298d11]689{
690        help_t *h;
691        set_t *s;
692        int i;
693       
[d7d677d]694        irc_send_msg_raw( irc->root, "NOTICE", irc->user->nick, "COMPLETIONS OK" );
[0298d11]695       
[8358691]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 );
[0298d11]698       
699        for( h = global.help; h; h = h->next )
[d7d677d]700                irc_send_msg_f( irc->root, "NOTICE", irc->user->nick, "COMPLETIONS help %s", h->title );
[0298d11]701       
[d7d677d]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 );
[0298d11]704       
[d7d677d]705        irc_send_msg_raw( irc->root, "NOTICE", irc->user->nick, "COMPLETIONS END" );
[0298d11]706}
707
[f73b969]708static void irc_cmd_rehash( irc_t *irc, char **cmd )
[f4a5940]709{
[5424c76]710        if( global.conf->runmode == RUNMODE_INETD )
711                ipc_master_cmd_rehash( NULL, NULL );
712        else
713                ipc_to_master( cmd );
[f4a5940]714       
[3ddb7477]715        irc_send_num( irc, 382, "%s :Rehashing", global.conf_file );
[f4a5940]716}
717
[0298d11]718static const command_t irc_commands[] = {
[a199d33]719        { "pass",        1, irc_cmd_pass,        0 },
[0298d11]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 },
[3923003]724        { "pong",        0, irc_cmd_pong,        IRC_CMD_LOGGED_IN },
[b9e020a]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 },
[b95932e]728        { "whois",       1, irc_cmd_whois,       IRC_CMD_LOGGED_IN },
729        { "whowas",      1, irc_cmd_whowas,      IRC_CMD_LOGGED_IN },
[9b69eb7]730        { "motd",        0, irc_cmd_motd,        IRC_CMD_LOGGED_IN },
[b919363]731        { "mode",        1, irc_cmd_mode,        IRC_CMD_LOGGED_IN },
[2f53ada]732        { "who",         0, irc_cmd_who,         IRC_CMD_LOGGED_IN },
[280c56a]733        { "privmsg",     1, irc_cmd_privmsg,     IRC_CMD_LOGGED_IN },
[d7f8500]734        { "notice",      1, irc_cmd_notice,      IRC_CMD_LOGGED_IN },
[d860a8d]735        { "nickserv",    1, irc_cmd_nickserv,    IRC_CMD_LOGGED_IN },
736        { "ns",          1, irc_cmd_nickserv,    IRC_CMD_LOGGED_IN },
[81186cab]737        { "away",        0, irc_cmd_away,        IRC_CMD_LOGGED_IN },
[d7d677d]738        { "version",     0, irc_cmd_version,     IRC_CMD_LOGGED_IN },
739        { "completions", 0, irc_cmd_completions, IRC_CMD_LOGGED_IN },
[0298d11]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 },
[003a12b]743        { "invite",      2, irc_cmd_invite,      IRC_CMD_LOGGED_IN },
[4469e7e]744        { "topic",       1, irc_cmd_topic,       IRC_CMD_LOGGED_IN },
[003a12b]745        { "oper",        2, irc_cmd_oper,        IRC_CMD_LOGGED_IN },
[23d6165]746        { "list",        0, irc_cmd_list,        IRC_CMD_LOGGED_IN },
[0431ea1]747        { "die",         0, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
[565a1ea]748        { "deaf",        0, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
[48721c3]749        { "wallops",     1, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
[dfc8a46]750        { "wall",        1, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
[f4a5940]751        { "rehash",      0, irc_cmd_rehash,      IRC_CMD_OPER_ONLY },
[54879ab]752        { "restart",     0, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
[48721c3]753        { "kill",        2, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
[0298d11]754        { NULL }
755};
756
[f73b969]757void irc_exec( irc_t *irc, char *cmd[] )
[0298d11]758{       
[f1d38f2]759        int i, n_arg;
[0298d11]760       
761        if( !cmd[0] )
[f73b969]762                return;
[0298d11]763       
764        for( i = 0; irc_commands[i].command; i++ )
765                if( g_strcasecmp( irc_commands[i].command, cmd[0] ) == 0 )
766                {
[f1d38f2]767                        /* There should be no typo in the next line: */
768                        for( n_arg = 0; cmd[n_arg]; n_arg ++ ); n_arg --;
769                       
[79e826a]770                        if( irc_commands[i].flags & IRC_CMD_PRE_LOGIN && irc->status & USTATUS_LOGGED_IN )
[edf9657]771                        {
[3ddb7477]772                                irc_send_num( irc, 462, ":Only allowed before logging in" );
[edf9657]773                        }
[3af70b0]774                        else if( irc_commands[i].flags & IRC_CMD_LOGGED_IN && !( irc->status & USTATUS_LOGGED_IN ) )
[edf9657]775                        {
[3ddb7477]776                                irc_send_num( irc, 451, ":Register first" );
[edf9657]777                        }
[f73b969]778                        else if( irc_commands[i].flags & IRC_CMD_OPER_ONLY && !strchr( irc->umode, 'o' ) )
[edf9657]779                        {
[3ddb7477]780                                irc_send_num( irc, 481, ":Permission denied - You're not an IRC operator" );
[edf9657]781                        }
[f1d38f2]782                        else if( n_arg < irc_commands[i].required_parameters )
[f73b969]783                        {
[3ddb7477]784                                irc_send_num( irc, 461, "%s :Need more parameters", cmd[0] );
[f73b969]785                        }
786                        else if( irc_commands[i].flags & IRC_CMD_TO_MASTER )
787                        {
[5424c76]788                                /* IPC doesn't make sense in inetd mode,
789                                    but the function will catch that. */
[0431ea1]790                                ipc_to_master( cmd );
[f73b969]791                        }
[0431ea1]792                        else
[f73b969]793                        {
794                                irc_commands[i].execute( irc, cmd );
795                        }
796                       
[2f13222]797                        return;
[0298d11]798                }
[2f13222]799       
[060d066]800        if( irc->status & USTATUS_LOGGED_IN )
[3ddb7477]801                irc_send_num( irc, 421, "%s :Unknown command", cmd[0] );
[0298d11]802}
Note: See TracBrowser for help on using the repository browser.