source: irc_commands.c @ 8d96b78

Last change on this file since 8d96b78 was 3558fea, checked in by Wilmer van der Gaast <wilmer@…>, at 2011-12-29T20:18:12Z

When changing nicks and the change is case-only (or no change *at all*),
do not reset identify status. Bug #880.

  • Property mode set to 100644
File size: 21.3 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        {
450                char *send_cmd[] = { "identify", password, NULL };
451                irc_send_num( irc, 491, ":Trying to identify" );
452                root_command( irc, send_cmd );
453        }
454        else if( irc->status & OPER_HACK_REGISTER )
455        {
456                char *send_cmd[] = { "register", password, NULL };
457                irc_send_num( irc, 491, ":Trying to identify" );
458                root_command( irc, send_cmd );
459        }
460       
461        irc->status &= ~OPER_HACK_ANY;
462        g_free( password );
463}
464
[280c56a]465static void irc_cmd_invite( irc_t *irc, char **cmd )
466{
[66b9e36a]467        irc_channel_t *ic;
468        irc_user_t *iu;
[280c56a]469       
[66b9e36a]470        if( ( iu = irc_user_by_name( irc, cmd[1] ) ) == NULL )
471        {
472                irc_send_num( irc, 401, "%s :No such nick", cmd[1] );
473                return;
474        }
475        else if( ( ic = irc_channel_by_name( irc, cmd[2] ) ) == NULL )
476        {
477                irc_send_num( irc, 403, "%s :No such channel", cmd[2] );
478                return;
479        }
[280c56a]480       
[46d215d]481        if( !ic->f->invite )
[66b9e36a]482                irc_send_num( irc, 482, "%s :Can't invite people here", cmd[2] );
[46d215d]483        else if( ic->f->invite( ic, iu ) )
484                irc_send_num( irc, 341, "%s %s", iu->nick, ic->name );
[0298d11]485}
486
[f73b969]487static void irc_cmd_userhost( irc_t *irc, char **cmd )
[0298d11]488{
489        int i;
490       
491        /* [TV] Usable USERHOST-implementation according to
492                RFC1459. Without this, mIRC shows an error
493                while connecting, and the used way of rejecting
494                breaks standards.
495        */
496       
497        for( i = 1; cmd[i]; i ++ )
[003a12b]498        {
499                irc_user_t *iu = irc_user_by_name( irc, cmd[i] );
500               
501                if( iu )
502                        irc_send_num( irc, 302, ":%s=%c%s@%s", iu->nick,
503                                      irc_user_get_away( iu ) ? '-' : '+',
504                                      iu->user, iu->host );
505        }
[0298d11]506}
507
[f73b969]508static void irc_cmd_ison( irc_t *irc, char **cmd )
[0298d11]509{
[b4e4b95]510        char buff[IRC_MAX_LINE];
[0298d11]511        int lenleft, i;
512       
513        buff[0] = '\0';
514       
515        /* [SH] Leave room for : and \0 */
516        lenleft = IRC_MAX_LINE - 2;
517       
518        for( i = 1; cmd[i]; i ++ )
519        {
[42616d1]520                char *this, *next;
521               
522                this = cmd[i];
523                while( *this )
[0298d11]524                {
[003a12b]525                        irc_user_t *iu;
526                       
[42616d1]527                        if( ( next = strchr( this, ' ' ) ) )
528                                *next = 0;
[0298d11]529                       
[003a12b]530                        if( ( iu = irc_user_by_name( irc, this ) ) &&
531                            iu->bu && iu->bu->flags & BEE_USER_ONLINE )
[0298d11]532                        {
[003a12b]533                                lenleft -= strlen( iu->nick ) + 1;
[42616d1]534                               
535                                if( lenleft < 0 )
536                                        break;
537                               
[003a12b]538                                strcat( buff, iu->nick );
[42616d1]539                                strcat( buff, " " );
[0298d11]540                        }
541                       
[42616d1]542                        if( next )
543                        {
544                                *next = ' ';
545                                this = next + 1;
546                        }
547                        else
548                        {
549                                break;
550                        }   
[0298d11]551                }
[42616d1]552               
553                /* *sigh* */
554                if( lenleft < 0 )
555                        break;
[0298d11]556        }
557       
558        if( strlen( buff ) > 0 )
559                buff[strlen(buff)-1] = '\0';
560       
[3ddb7477]561        irc_send_num( irc, 303, ":%s", buff );
[0298d11]562}
563
[f73b969]564static void irc_cmd_watch( irc_t *irc, char **cmd )
[0298d11]565{
566        int i;
567       
568        /* Obviously we could also mark a user structure as being
569           watched, but what if the WATCH command is sent right
570           after connecting? The user won't exist yet then... */
571        for( i = 1; cmd[i]; i ++ )
572        {
573                char *nick;
[003a12b]574                irc_user_t *iu;
[0298d11]575               
576                if( !cmd[i][0] || !cmd[i][1] )
577                        break;
578               
579                nick = g_strdup( cmd[i] + 1 );
580                nick_lc( nick );
581               
[003a12b]582                iu = irc_user_by_name( irc, nick );
[0298d11]583               
584                if( cmd[i][0] == '+' )
585                {
586                        if( !g_hash_table_lookup( irc->watches, nick ) )
587                                g_hash_table_insert( irc->watches, nick, nick );
588                       
[003a12b]589                        if( iu && iu->bu && iu->bu->flags & BEE_USER_ONLINE )
590                                irc_send_num( irc, 604, "%s %s %s %d :%s", iu->nick, iu->user,
591                                              iu->host, (int) time( NULL ), "is online" );
[0298d11]592                        else
[003a12b]593                                irc_send_num( irc, 605, "%s %s %s %d :%s", nick, "*", "*",
594                                              (int) time( NULL ), "is offline" );
[0298d11]595                }
596                else if( cmd[i][0] == '-' )
597                {
598                        gpointer okey, ovalue;
599                       
600                        if( g_hash_table_lookup_extended( irc->watches, nick, &okey, &ovalue ) )
601                        {
602                                g_hash_table_remove( irc->watches, okey );
[e59b4f6]603                                g_free( okey );
[0298d11]604                               
[3ddb7477]605                                irc_send_num( irc, 602, "%s %s %s %d :%s", nick, "*", "*", 0, "Stopped watching" );
[0298d11]606                        }
607                }
608        }
609}
610
[f73b969]611static void irc_cmd_topic( irc_t *irc, char **cmd )
[0298d11]612{
[4469e7e]613        irc_channel_t *ic = irc_channel_by_name( irc, cmd[1] );
614        const char *new = cmd[2];
[50e1776]615       
[4469e7e]616        if( ic == NULL )
[50e1776]617        {
[4469e7e]618                irc_send_num( irc, 403, "%s :No such channel", cmd[1] );
619        }
620        else if( new )
621        {
622                if( ic->f->topic == NULL )
623                        irc_send_num( irc, 482, "%s :Can't change this channel's topic", ic->name );
624                else if( ic->f->topic( ic, new ) )
625                        irc_send_topic( ic, TRUE );
[50e1776]626        }
[0298d11]627        else
[50e1776]628        {
[4469e7e]629                irc_send_topic( ic, FALSE );
[50e1776]630        }
[0298d11]631}
632
[f73b969]633static void irc_cmd_away( irc_t *irc, char **cmd )
[0298d11]634{
[81186cab]635        if( cmd[1] && *cmd[1] )
[0298d11]636        {
[81186cab]637                char away[strlen(cmd[1])+1];
[0298d11]638                int i, j;
639               
640                /* Copy away string, but skip control chars. Mainly because
641                   Jabber really doesn't like them. */
[81186cab]642                for( i = j = 0; cmd[1][i]; i ++ )
[14df98f]643                        if( (unsigned char) ( away[j] = cmd[1][i] ) >= ' ' )
[0298d11]644                                j ++;
[81186cab]645                away[j] = '\0';
[0298d11]646               
[81186cab]647                irc_send_num( irc, 306, ":You're now away: %s", away );
[4a9fd5f]648                set_setstr( &irc->b->set, "away", away );
[0298d11]649        }
650        else
651        {
[3ddb7477]652                irc_send_num( irc, 305, ":Welcome back" );
[4a9fd5f]653                set_setstr( &irc->b->set, "away", NULL );
[0298d11]654        }
655}
656
[23d6165]657static void irc_cmd_list( irc_t *irc, char **cmd )
658{
659        GSList *l;
660       
661        for( l = irc->channels; l; l = l->next )
662        {
663                irc_channel_t *ic = l->data;
664               
665                irc_send_num( irc, 322, "%s %d :%s",
666                              ic->name, g_slist_length( ic->users ), ic->topic ? : "" );
667        }
668        irc_send_num( irc, 323, ":%s", "End of /LIST" );
669}
670
[82898af]671static void irc_cmd_version( irc_t *irc, char **cmd )
672{
[d7d677d]673        irc_send_num( irc, 351, "bitlbee-%s. %s :%s/%s ",
674                      BITLBEE_VERSION, irc->root->host, ARCH, CPU );
[82898af]675}
676
[f73b969]677static void irc_cmd_completions( irc_t *irc, char **cmd )
[0298d11]678{
679        help_t *h;
680        set_t *s;
681        int i;
682       
[d7d677d]683        irc_send_msg_raw( irc->root, "NOTICE", irc->user->nick, "COMPLETIONS OK" );
[0298d11]684       
[8358691]685        for( i = 0; root_commands[i].command; i ++ )
686                irc_send_msg_f( irc->root, "NOTICE", irc->user->nick, "COMPLETIONS %s", root_commands[i].command );
[0298d11]687       
688        for( h = global.help; h; h = h->next )
[d7d677d]689                irc_send_msg_f( irc->root, "NOTICE", irc->user->nick, "COMPLETIONS help %s", h->title );
[0298d11]690       
[d7d677d]691        for( s = irc->b->set; s; s = s->next )
692                irc_send_msg_f( irc->root, "NOTICE", irc->user->nick, "COMPLETIONS set %s", s->key );
[0298d11]693       
[d7d677d]694        irc_send_msg_raw( irc->root, "NOTICE", irc->user->nick, "COMPLETIONS END" );
[0298d11]695}
696
[f73b969]697static void irc_cmd_rehash( irc_t *irc, char **cmd )
[f4a5940]698{
[5424c76]699        if( global.conf->runmode == RUNMODE_INETD )
700                ipc_master_cmd_rehash( NULL, NULL );
701        else
702                ipc_to_master( cmd );
[f4a5940]703       
[3ddb7477]704        irc_send_num( irc, 382, "%s :Rehashing", global.conf_file );
[f4a5940]705}
706
[0298d11]707static const command_t irc_commands[] = {
[a199d33]708        { "pass",        1, irc_cmd_pass,        0 },
[0298d11]709        { "user",        4, irc_cmd_user,        IRC_CMD_PRE_LOGIN },
710        { "nick",        1, irc_cmd_nick,        0 },
711        { "quit",        0, irc_cmd_quit,        0 },
712        { "ping",        0, irc_cmd_ping,        0 },
[3923003]713        { "pong",        0, irc_cmd_pong,        IRC_CMD_LOGGED_IN },
[b9e020a]714        { "join",        1, irc_cmd_join,        IRC_CMD_LOGGED_IN },
715        { "names",       1, irc_cmd_names,       IRC_CMD_LOGGED_IN },
716        { "part",        1, irc_cmd_part,        IRC_CMD_LOGGED_IN },
[b95932e]717        { "whois",       1, irc_cmd_whois,       IRC_CMD_LOGGED_IN },
718        { "whowas",      1, irc_cmd_whowas,      IRC_CMD_LOGGED_IN },
[9b69eb7]719        { "motd",        0, irc_cmd_motd,        IRC_CMD_LOGGED_IN },
[b919363]720        { "mode",        1, irc_cmd_mode,        IRC_CMD_LOGGED_IN },
[2f53ada]721        { "who",         0, irc_cmd_who,         IRC_CMD_LOGGED_IN },
[280c56a]722        { "privmsg",     1, irc_cmd_privmsg,     IRC_CMD_LOGGED_IN },
[d7f8500]723        { "notice",      1, irc_cmd_notice,      IRC_CMD_LOGGED_IN },
[d860a8d]724        { "nickserv",    1, irc_cmd_nickserv,    IRC_CMD_LOGGED_IN },
725        { "ns",          1, irc_cmd_nickserv,    IRC_CMD_LOGGED_IN },
[81186cab]726        { "away",        0, irc_cmd_away,        IRC_CMD_LOGGED_IN },
[d7d677d]727        { "version",     0, irc_cmd_version,     IRC_CMD_LOGGED_IN },
728        { "completions", 0, irc_cmd_completions, IRC_CMD_LOGGED_IN },
[0298d11]729        { "userhost",    1, irc_cmd_userhost,    IRC_CMD_LOGGED_IN },
730        { "ison",        1, irc_cmd_ison,        IRC_CMD_LOGGED_IN },
731        { "watch",       1, irc_cmd_watch,       IRC_CMD_LOGGED_IN },
[003a12b]732        { "invite",      2, irc_cmd_invite,      IRC_CMD_LOGGED_IN },
[4469e7e]733        { "topic",       1, irc_cmd_topic,       IRC_CMD_LOGGED_IN },
[003a12b]734        { "oper",        2, irc_cmd_oper,        IRC_CMD_LOGGED_IN },
[23d6165]735        { "list",        0, irc_cmd_list,        IRC_CMD_LOGGED_IN },
[0431ea1]736        { "die",         0, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
[565a1ea]737        { "deaf",        0, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
[48721c3]738        { "wallops",     1, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
[dfc8a46]739        { "wall",        1, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
[f4a5940]740        { "rehash",      0, irc_cmd_rehash,      IRC_CMD_OPER_ONLY },
[54879ab]741        { "restart",     0, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
[48721c3]742        { "kill",        2, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
[0298d11]743        { NULL }
744};
745
[f73b969]746void irc_exec( irc_t *irc, char *cmd[] )
[0298d11]747{       
[f1d38f2]748        int i, n_arg;
[0298d11]749       
750        if( !cmd[0] )
[f73b969]751                return;
[0298d11]752       
753        for( i = 0; irc_commands[i].command; i++ )
754                if( g_strcasecmp( irc_commands[i].command, cmd[0] ) == 0 )
755                {
[f1d38f2]756                        /* There should be no typo in the next line: */
757                        for( n_arg = 0; cmd[n_arg]; n_arg ++ ); n_arg --;
758                       
[79e826a]759                        if( irc_commands[i].flags & IRC_CMD_PRE_LOGIN && irc->status & USTATUS_LOGGED_IN )
[edf9657]760                        {
[3ddb7477]761                                irc_send_num( irc, 462, ":Only allowed before logging in" );
[edf9657]762                        }
[3af70b0]763                        else if( irc_commands[i].flags & IRC_CMD_LOGGED_IN && !( irc->status & USTATUS_LOGGED_IN ) )
[edf9657]764                        {
[3ddb7477]765                                irc_send_num( irc, 451, ":Register first" );
[edf9657]766                        }
[f73b969]767                        else if( irc_commands[i].flags & IRC_CMD_OPER_ONLY && !strchr( irc->umode, 'o' ) )
[edf9657]768                        {
[3ddb7477]769                                irc_send_num( irc, 481, ":Permission denied - You're not an IRC operator" );
[edf9657]770                        }
[f1d38f2]771                        else if( n_arg < irc_commands[i].required_parameters )
[f73b969]772                        {
[3ddb7477]773                                irc_send_num( irc, 461, "%s :Need more parameters", cmd[0] );
[f73b969]774                        }
775                        else if( irc_commands[i].flags & IRC_CMD_TO_MASTER )
776                        {
[5424c76]777                                /* IPC doesn't make sense in inetd mode,
778                                    but the function will catch that. */
[0431ea1]779                                ipc_to_master( cmd );
[f73b969]780                        }
[0431ea1]781                        else
[f73b969]782                        {
783                                irc_commands[i].execute( irc, cmd );
784                        }
785                       
[2f13222]786                        return;
[0298d11]787                }
[2f13222]788       
[060d066]789        if( irc->status & USTATUS_LOGGED_IN )
[3ddb7477]790                irc_send_num( irc, 421, "%s :Unknown command", cmd[0] );
[0298d11]791}
Note: See TracBrowser for help on using the repository browser.