source: irc_commands.c @ ff94563

Last change on this file since ff94563 was 060d066, checked in by Wilmer van der Gaast <wilmer@…>, at 2011-02-01T13:05:58Z

More password paranoia: Allow omitting the identify/register password as
well (and enter it using /OPER instead).

This is a gross hack and indeed still not solid: In irssi one can still
use /RAWLOG SAVE to find the OPER line sent to BitlBee (and of course not
everyone uses SSL to talk to remote BitlBee servers). This only works
within 10-30 minutes after entering the password though.

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