source: irc_commands.c @ 46511b3

Last change on this file since 46511b3 was 269580c, checked in by dequis <dx@…>, at 2014-07-24T03:51:07Z

Add limit param to split_command_parts(), fix twitter quotes bug

Only took me a few months to write. I even added a test case.

  • Property mode set to 100644
File size: 21.7 KB
RevLine 
[0298d11]1  /********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
[0e788f5]4  * Copyright 2002-2012 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        }
[e277e80]82        else if( !nick_ok( NULL, cmd[1] ) )
[0298d11]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        {
[e277e80]293                if( nick_cmp( NULL, 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                       
[269580c]367                        ctcp = split_command_parts( cmd[2] + 1, 0 );
[24b8bbb]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{
[bbc69f7]384        irc_user_t *iu;
385       
[d7f8500]386        if( !cmd[2] ) 
387        {
388                irc_send_num( irc, 412, ":No text to send" );
389                return;
390        }
391       
392        /* At least for now just echo. IIRC some IRC clients use self-notices
393           for lag checks, so try to support that. */
[e277e80]394        if( nick_cmp( NULL, cmd[1], irc->user->nick ) == 0 )
[d7f8500]395                irc_send_msg( irc->user, "NOTICE", irc->user->nick, cmd[2], NULL );
[bbc69f7]396        else if( ( iu = irc_user_by_name( irc, cmd[1] ) ) )
397                iu->f->privmsg( iu, cmd[2] );
[d7f8500]398}
399
[d860a8d]400static void irc_cmd_nickserv( irc_t *irc, char **cmd )
401{
402        /* [SH] This aliases the NickServ command to PRIVMSG root */
403        /* [TV] This aliases the NS command to PRIVMSG root as well */
404        root_command( irc, cmd + 1 );
405}
406
[060d066]407static void irc_cmd_oper_hack( irc_t *irc, char **cmd );
[280c56a]408
409static void irc_cmd_oper( irc_t *irc, char **cmd )
410{
[060d066]411        /* Very non-standard evil but useful/secure hack, see below. */
412        if( irc->status & OPER_HACK_ANY )
413                return irc_cmd_oper_hack( irc, cmd );
[9564e55]414       
[280c56a]415        if( global.conf->oper_pass &&
416            ( strncmp( global.conf->oper_pass, "md5:", 4 ) == 0 ?
417                md5_verify_password( cmd[2], global.conf->oper_pass + 4 ) == 0 :
418                strcmp( cmd[2], global.conf->oper_pass ) == 0 ) )
419        {
420                irc_umode_set( irc, "+o", 1 );
421                irc_send_num( irc, 381, ":Password accepted" );
422        }
423        else
424        {
[bedad20]425                irc_send_num( irc, 491, ":Incorrect password" );
[280c56a]426        }
427}
428
[060d066]429static void irc_cmd_oper_hack( irc_t *irc, char **cmd )
430{
431        char *password = g_strjoinv( " ", cmd + 2 );
432       
433        /* /OPER can now also be used to enter IM/identify passwords without
434           echoing. It's a hack but the extra password security is worth it. */
[35987a1]435        if( irc->status & OPER_HACK_ACCOUNT_PASSWORD )
[060d066]436        {
437                account_t *a;
438               
439                for( a = irc->b->accounts; a; a = a->next )
440                        if( strcmp( a->pass, PASSWORD_PENDING ) == 0 )
441                        {
442                                set_setstr( &a->set, "password", password );
[e67e513]443                                irc_rootmsg( irc, "Password added to IM account "
[17f057d]444                                             "%s", a->tag );
[060d066]445                                /* The IRC client may expect this. 491 suggests the OPER
446                                   password was wrong, so the client won't expect a +o.
447                                   It may however repeat the password prompt. We'll see. */
448                                irc_send_num( irc, 491, ":Password added to IM account "
[17f057d]449                                              "%s", a->tag );
[060d066]450                        }
451        }
452        else if( irc->status & OPER_HACK_IDENTIFY )
453        {
[fda194f]454                char *send_cmd[] = { "identify", password, NULL, NULL };
455                irc->status &= ~OPER_HACK_IDENTIFY;
456                if( irc->status & OPER_HACK_IDENTIFY_NOLOAD )
457                {
458                        send_cmd[1] = "-noload";
459                        send_cmd[2] = password;
460                }
461                else if( irc->status & OPER_HACK_IDENTIFY_FORCE )
462                {
463                        send_cmd[1] = "-force";
464                        send_cmd[2] = password;
465                }
[060d066]466                irc_send_num( irc, 491, ":Trying to identify" );
467                root_command( irc, send_cmd );
468        }
469        else if( irc->status & OPER_HACK_REGISTER )
470        {
471                char *send_cmd[] = { "register", password, NULL };
472                irc_send_num( irc, 491, ":Trying to identify" );
473                root_command( irc, send_cmd );
474        }
475       
476        irc->status &= ~OPER_HACK_ANY;
477        g_free( password );
478}
479
[280c56a]480static void irc_cmd_invite( irc_t *irc, char **cmd )
481{
[66b9e36a]482        irc_channel_t *ic;
483        irc_user_t *iu;
[280c56a]484       
[66b9e36a]485        if( ( iu = irc_user_by_name( irc, cmd[1] ) ) == NULL )
486        {
487                irc_send_num( irc, 401, "%s :No such nick", cmd[1] );
488                return;
489        }
490        else if( ( ic = irc_channel_by_name( irc, cmd[2] ) ) == NULL )
491        {
492                irc_send_num( irc, 403, "%s :No such channel", cmd[2] );
493                return;
494        }
[280c56a]495       
[46d215d]496        if( !ic->f->invite )
[66b9e36a]497                irc_send_num( irc, 482, "%s :Can't invite people here", cmd[2] );
[46d215d]498        else if( ic->f->invite( ic, iu ) )
499                irc_send_num( irc, 341, "%s %s", iu->nick, ic->name );
[0298d11]500}
501
[f73b969]502static void irc_cmd_userhost( irc_t *irc, char **cmd )
[0298d11]503{
504        int i;
505       
506        /* [TV] Usable USERHOST-implementation according to
507                RFC1459. Without this, mIRC shows an error
508                while connecting, and the used way of rejecting
509                breaks standards.
510        */
511       
512        for( i = 1; cmd[i]; i ++ )
[003a12b]513        {
514                irc_user_t *iu = irc_user_by_name( irc, cmd[i] );
515               
516                if( iu )
517                        irc_send_num( irc, 302, ":%s=%c%s@%s", iu->nick,
518                                      irc_user_get_away( iu ) ? '-' : '+',
519                                      iu->user, iu->host );
520        }
[0298d11]521}
522
[f73b969]523static void irc_cmd_ison( irc_t *irc, char **cmd )
[0298d11]524{
[b4e4b95]525        char buff[IRC_MAX_LINE];
[0298d11]526        int lenleft, i;
527       
528        buff[0] = '\0';
529       
530        /* [SH] Leave room for : and \0 */
531        lenleft = IRC_MAX_LINE - 2;
532       
533        for( i = 1; cmd[i]; i ++ )
534        {
[42616d1]535                char *this, *next;
536               
537                this = cmd[i];
538                while( *this )
[0298d11]539                {
[003a12b]540                        irc_user_t *iu;
541                       
[42616d1]542                        if( ( next = strchr( this, ' ' ) ) )
543                                *next = 0;
[0298d11]544                       
[003a12b]545                        if( ( iu = irc_user_by_name( irc, this ) ) &&
546                            iu->bu && iu->bu->flags & BEE_USER_ONLINE )
[0298d11]547                        {
[003a12b]548                                lenleft -= strlen( iu->nick ) + 1;
[42616d1]549                               
550                                if( lenleft < 0 )
551                                        break;
552                               
[003a12b]553                                strcat( buff, iu->nick );
[42616d1]554                                strcat( buff, " " );
[0298d11]555                        }
556                       
[42616d1]557                        if( next )
558                        {
559                                *next = ' ';
560                                this = next + 1;
561                        }
562                        else
563                        {
564                                break;
565                        }   
[0298d11]566                }
[42616d1]567               
568                /* *sigh* */
569                if( lenleft < 0 )
570                        break;
[0298d11]571        }
572       
573        if( strlen( buff ) > 0 )
574                buff[strlen(buff)-1] = '\0';
575       
[3ddb7477]576        irc_send_num( irc, 303, ":%s", buff );
[0298d11]577}
578
[f73b969]579static void irc_cmd_watch( irc_t *irc, char **cmd )
[0298d11]580{
581        int i;
582       
583        /* Obviously we could also mark a user structure as being
584           watched, but what if the WATCH command is sent right
585           after connecting? The user won't exist yet then... */
586        for( i = 1; cmd[i]; i ++ )
587        {
588                char *nick;
[003a12b]589                irc_user_t *iu;
[0298d11]590               
591                if( !cmd[i][0] || !cmd[i][1] )
592                        break;
593               
594                nick = g_strdup( cmd[i] + 1 );
[e277e80]595                nick_lc( irc, nick );
[0298d11]596               
[003a12b]597                iu = irc_user_by_name( irc, nick );
[0298d11]598               
599                if( cmd[i][0] == '+' )
600                {
601                        if( !g_hash_table_lookup( irc->watches, nick ) )
602                                g_hash_table_insert( irc->watches, nick, nick );
603                       
[003a12b]604                        if( iu && iu->bu && iu->bu->flags & BEE_USER_ONLINE )
605                                irc_send_num( irc, 604, "%s %s %s %d :%s", iu->nick, iu->user,
606                                              iu->host, (int) time( NULL ), "is online" );
[0298d11]607                        else
[003a12b]608                                irc_send_num( irc, 605, "%s %s %s %d :%s", nick, "*", "*",
609                                              (int) time( NULL ), "is offline" );
[0298d11]610                }
611                else if( cmd[i][0] == '-' )
612                {
613                        gpointer okey, ovalue;
614                       
615                        if( g_hash_table_lookup_extended( irc->watches, nick, &okey, &ovalue ) )
616                        {
617                                g_hash_table_remove( irc->watches, okey );
[e59b4f6]618                                g_free( okey );
[0298d11]619                               
[3ddb7477]620                                irc_send_num( irc, 602, "%s %s %s %d :%s", nick, "*", "*", 0, "Stopped watching" );
[0298d11]621                        }
622                }
623        }
624}
625
[f73b969]626static void irc_cmd_topic( irc_t *irc, char **cmd )
[0298d11]627{
[4469e7e]628        irc_channel_t *ic = irc_channel_by_name( irc, cmd[1] );
629        const char *new = cmd[2];
[50e1776]630       
[4469e7e]631        if( ic == NULL )
[50e1776]632        {
[4469e7e]633                irc_send_num( irc, 403, "%s :No such channel", cmd[1] );
634        }
635        else if( new )
636        {
637                if( ic->f->topic == NULL )
638                        irc_send_num( irc, 482, "%s :Can't change this channel's topic", ic->name );
639                else if( ic->f->topic( ic, new ) )
640                        irc_send_topic( ic, TRUE );
[50e1776]641        }
[0298d11]642        else
[50e1776]643        {
[4469e7e]644                irc_send_topic( ic, FALSE );
[50e1776]645        }
[0298d11]646}
647
[f73b969]648static void irc_cmd_away( irc_t *irc, char **cmd )
[0298d11]649{
[81186cab]650        if( cmd[1] && *cmd[1] )
[0298d11]651        {
[81186cab]652                char away[strlen(cmd[1])+1];
[0298d11]653                int i, j;
654               
655                /* Copy away string, but skip control chars. Mainly because
656                   Jabber really doesn't like them. */
[81186cab]657                for( i = j = 0; cmd[1][i]; i ++ )
[14df98f]658                        if( (unsigned char) ( away[j] = cmd[1][i] ) >= ' ' )
[0298d11]659                                j ++;
[81186cab]660                away[j] = '\0';
[0298d11]661               
[81186cab]662                irc_send_num( irc, 306, ":You're now away: %s", away );
[4a9fd5f]663                set_setstr( &irc->b->set, "away", away );
[0298d11]664        }
665        else
666        {
[3ddb7477]667                irc_send_num( irc, 305, ":Welcome back" );
[4a9fd5f]668                set_setstr( &irc->b->set, "away", NULL );
[0298d11]669        }
670}
671
[23d6165]672static void irc_cmd_list( irc_t *irc, char **cmd )
673{
674        GSList *l;
675       
676        for( l = irc->channels; l; l = l->next )
677        {
678                irc_channel_t *ic = l->data;
679               
680                irc_send_num( irc, 322, "%s %d :%s",
681                              ic->name, g_slist_length( ic->users ), ic->topic ? : "" );
682        }
683        irc_send_num( irc, 323, ":%s", "End of /LIST" );
684}
685
[82898af]686static void irc_cmd_version( irc_t *irc, char **cmd )
687{
[18e1f3b]688        irc_send_num( irc, 351, "%s-%s. %s :%s/%s ",
689                      PACKAGE, BITLBEE_VERSION, irc->root->host, ARCH, CPU );
[82898af]690}
691
[f73b969]692static void irc_cmd_completions( irc_t *irc, char **cmd )
[0298d11]693{
694        help_t *h;
695        set_t *s;
696        int i;
697       
[d7d677d]698        irc_send_msg_raw( irc->root, "NOTICE", irc->user->nick, "COMPLETIONS OK" );
[0298d11]699       
[8358691]700        for( i = 0; root_commands[i].command; i ++ )
701                irc_send_msg_f( irc->root, "NOTICE", irc->user->nick, "COMPLETIONS %s", root_commands[i].command );
[0298d11]702       
703        for( h = global.help; h; h = h->next )
[d7d677d]704                irc_send_msg_f( irc->root, "NOTICE", irc->user->nick, "COMPLETIONS help %s", h->title );
[0298d11]705       
[d7d677d]706        for( s = irc->b->set; s; s = s->next )
707                irc_send_msg_f( irc->root, "NOTICE", irc->user->nick, "COMPLETIONS set %s", s->key );
[0298d11]708       
[d7d677d]709        irc_send_msg_raw( irc->root, "NOTICE", irc->user->nick, "COMPLETIONS END" );
[0298d11]710}
711
[f73b969]712static void irc_cmd_rehash( irc_t *irc, char **cmd )
[f4a5940]713{
[5424c76]714        if( global.conf->runmode == RUNMODE_INETD )
715                ipc_master_cmd_rehash( NULL, NULL );
716        else
717                ipc_to_master( cmd );
[f4a5940]718       
[3ddb7477]719        irc_send_num( irc, 382, "%s :Rehashing", global.conf_file );
[f4a5940]720}
721
[0298d11]722static const command_t irc_commands[] = {
[a199d33]723        { "pass",        1, irc_cmd_pass,        0 },
[0298d11]724        { "user",        4, irc_cmd_user,        IRC_CMD_PRE_LOGIN },
725        { "nick",        1, irc_cmd_nick,        0 },
726        { "quit",        0, irc_cmd_quit,        0 },
727        { "ping",        0, irc_cmd_ping,        0 },
[3923003]728        { "pong",        0, irc_cmd_pong,        IRC_CMD_LOGGED_IN },
[b9e020a]729        { "join",        1, irc_cmd_join,        IRC_CMD_LOGGED_IN },
730        { "names",       1, irc_cmd_names,       IRC_CMD_LOGGED_IN },
731        { "part",        1, irc_cmd_part,        IRC_CMD_LOGGED_IN },
[b95932e]732        { "whois",       1, irc_cmd_whois,       IRC_CMD_LOGGED_IN },
733        { "whowas",      1, irc_cmd_whowas,      IRC_CMD_LOGGED_IN },
[9b69eb7]734        { "motd",        0, irc_cmd_motd,        IRC_CMD_LOGGED_IN },
[b919363]735        { "mode",        1, irc_cmd_mode,        IRC_CMD_LOGGED_IN },
[2f53ada]736        { "who",         0, irc_cmd_who,         IRC_CMD_LOGGED_IN },
[280c56a]737        { "privmsg",     1, irc_cmd_privmsg,     IRC_CMD_LOGGED_IN },
[d7f8500]738        { "notice",      1, irc_cmd_notice,      IRC_CMD_LOGGED_IN },
[d860a8d]739        { "nickserv",    1, irc_cmd_nickserv,    IRC_CMD_LOGGED_IN },
740        { "ns",          1, irc_cmd_nickserv,    IRC_CMD_LOGGED_IN },
[81186cab]741        { "away",        0, irc_cmd_away,        IRC_CMD_LOGGED_IN },
[d7d677d]742        { "version",     0, irc_cmd_version,     IRC_CMD_LOGGED_IN },
743        { "completions", 0, irc_cmd_completions, IRC_CMD_LOGGED_IN },
[0298d11]744        { "userhost",    1, irc_cmd_userhost,    IRC_CMD_LOGGED_IN },
745        { "ison",        1, irc_cmd_ison,        IRC_CMD_LOGGED_IN },
746        { "watch",       1, irc_cmd_watch,       IRC_CMD_LOGGED_IN },
[003a12b]747        { "invite",      2, irc_cmd_invite,      IRC_CMD_LOGGED_IN },
[4469e7e]748        { "topic",       1, irc_cmd_topic,       IRC_CMD_LOGGED_IN },
[003a12b]749        { "oper",        2, irc_cmd_oper,        IRC_CMD_LOGGED_IN },
[23d6165]750        { "list",        0, irc_cmd_list,        IRC_CMD_LOGGED_IN },
[0431ea1]751        { "die",         0, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
[565a1ea]752        { "deaf",        0, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
[48721c3]753        { "wallops",     1, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
[dfc8a46]754        { "wall",        1, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
[f4a5940]755        { "rehash",      0, irc_cmd_rehash,      IRC_CMD_OPER_ONLY },
[54879ab]756        { "restart",     0, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
[48721c3]757        { "kill",        2, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
[0298d11]758        { NULL }
759};
760
[f73b969]761void irc_exec( irc_t *irc, char *cmd[] )
[0298d11]762{       
[f1d38f2]763        int i, n_arg;
[0298d11]764       
765        if( !cmd[0] )
[f73b969]766                return;
[0298d11]767       
768        for( i = 0; irc_commands[i].command; i++ )
769                if( g_strcasecmp( irc_commands[i].command, cmd[0] ) == 0 )
770                {
[f1d38f2]771                        /* There should be no typo in the next line: */
772                        for( n_arg = 0; cmd[n_arg]; n_arg ++ ); n_arg --;
773                       
[79e826a]774                        if( irc_commands[i].flags & IRC_CMD_PRE_LOGIN && irc->status & USTATUS_LOGGED_IN )
[edf9657]775                        {
[3ddb7477]776                                irc_send_num( irc, 462, ":Only allowed before logging in" );
[edf9657]777                        }
[3af70b0]778                        else if( irc_commands[i].flags & IRC_CMD_LOGGED_IN && !( irc->status & USTATUS_LOGGED_IN ) )
[edf9657]779                        {
[3ddb7477]780                                irc_send_num( irc, 451, ":Register first" );
[edf9657]781                        }
[f73b969]782                        else if( irc_commands[i].flags & IRC_CMD_OPER_ONLY && !strchr( irc->umode, 'o' ) )
[edf9657]783                        {
[3ddb7477]784                                irc_send_num( irc, 481, ":Permission denied - You're not an IRC operator" );
[edf9657]785                        }
[f1d38f2]786                        else if( n_arg < irc_commands[i].required_parameters )
[f73b969]787                        {
[3ddb7477]788                                irc_send_num( irc, 461, "%s :Need more parameters", cmd[0] );
[f73b969]789                        }
790                        else if( irc_commands[i].flags & IRC_CMD_TO_MASTER )
791                        {
[5424c76]792                                /* IPC doesn't make sense in inetd mode,
793                                    but the function will catch that. */
[0431ea1]794                                ipc_to_master( cmd );
[f73b969]795                        }
[0431ea1]796                        else
[f73b969]797                        {
798                                irc_commands[i].execute( irc, cmd );
799                        }
800                       
[2f13222]801                        return;
[0298d11]802                }
[2f13222]803       
[060d066]804        if( irc->status & USTATUS_LOGGED_IN )
[3ddb7477]805                irc_send_num( irc, 421, "%s :Unknown command", cmd[0] );
[0298d11]806}
Note: See TracBrowser for help on using the repository browser.