source: irc_commands.c @ e00da63

Last change on this file since e00da63 was fb117aee, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-04-02T02:29:45Z

Cleaned lots of compiler warnings so I can get some signal again.

  • Property mode set to 100644
File size: 17.0 KB
Line 
1  /********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
4  * Copyright 2002-2010 Wilmer van der Gaast and others                *
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"
28#include "ipc.h"
29#include "chat.h"
30
31static void irc_cmd_pass( irc_t *irc, char **cmd )
32{
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. */
41                return root_command( irc, send_cmd );
42        }
43        /* Handling in pre-logged-in state, first see if this server is
44           password-protected: */
45        else if( global.conf->auth_pass &&
46            ( strncmp( global.conf->auth_pass, "md5:", 4 ) == 0 ?
47                md5_verify_password( cmd[1], global.conf->auth_pass + 4 ) == 0 :
48                strcmp( cmd[1], global.conf->auth_pass ) == 0 ) )
49        {
50                irc->status |= USTATUS_AUTHORIZED;
51                irc_check_login( irc );
52        }
53        else if( global.conf->auth_pass )
54        {
55                irc_send_num( irc, 464, ":Incorrect password" );
56        }
57        else
58        {
59                /* Remember the password and try to identify after USER/NICK. */
60                /*irc_setpass( irc, cmd[1] ); */
61                irc_check_login( irc );
62        }
63}
64
65static void irc_cmd_user( irc_t *irc, char **cmd )
66{
67        irc->user->user = g_strdup( cmd[1] );
68        irc->user->fullname = g_strdup( cmd[4] );
69       
70        irc_check_login( irc );
71}
72
73static void irc_cmd_nick( irc_t *irc, char **cmd )
74{
75        if( irc->user->nick )
76        {
77                irc_send_num( irc, 438, ":The hand of the deity is upon thee, thy nick may not change" );
78        }
79        else if( irc_user_by_name( irc, cmd[1] ) )
80        {
81                irc_send_num( irc, 433, ":This nick is already in use" );
82        }
83        else if( !nick_ok( cmd[1] ) )
84        {
85                /* [SH] Invalid characters. */
86                irc_send_num( irc, 432, ":This nick contains invalid characters" );
87        }
88        else
89        {
90                irc->user->nick = g_strdup( cmd[1] );
91               
92                irc_check_login( irc );
93        }
94}
95
96static void irc_cmd_quit( irc_t *irc, char **cmd )
97{
98        if( cmd[1] && *cmd[1] )
99                irc_abort( irc, 0, "Quit: %s", cmd[1] );
100        else
101                irc_abort( irc, 0, "Leaving..." );
102}
103
104static void irc_cmd_ping( irc_t *irc, char **cmd )
105{
106        irc_write( irc, ":%s PONG %s :%s", irc->root->host,
107                   irc->root->host, cmd[1]?cmd[1]:irc->root->host );
108}
109
110static void irc_cmd_pong( irc_t *irc, char **cmd )
111{
112        /* We could check the value we get back from the user, but in
113           fact we don't care, we're just happy s/he's still alive. */
114        irc->last_pong = gettime();
115        irc->pinging = 0;
116}
117
118static void irc_cmd_join( irc_t *irc, char **cmd )
119{
120        irc_channel_t *ic;
121       
122        if( ( ic = irc_channel_by_name( irc, cmd[1] ) ) == NULL )
123                ic = irc_channel_new( irc, cmd[1] );
124       
125        if( ic == NULL )
126                irc_send_num( irc, 479, "%s :Invalid channel name", cmd[1] );
127       
128        if( ic->flags & IRC_CHANNEL_JOINED )
129                return; /* Dude, you're already there...
130                           RFC doesn't have any reply for that though? */
131       
132        irc_channel_add_user( ic, irc->user );
133}
134
135static void irc_cmd_names( irc_t *irc, char **cmd )
136{
137        irc_channel_t *ic;
138       
139        if( cmd[1] && ( ic = irc_channel_by_name( irc, cmd[1] ) ) )
140                irc_send_names( ic );
141        /* With no args, we should show /names of all chans. Make the code
142           below work well if necessary.
143        else
144        {
145                GSList *l;
146               
147                for( l = irc->channels; l; l = l->next )
148                        irc_send_names( l->data );
149        }
150        */
151}
152
153static void irc_cmd_part( irc_t *irc, char **cmd )
154{
155        irc_channel_t *ic;
156       
157        if( ( ic = irc_channel_by_name( irc, cmd[1] ) ) == NULL )
158        {
159                irc_send_num( irc, 403, "%s :No such channel", cmd[1] );
160        }
161        else if( !irc_channel_del_user( ic, irc->user ) )
162        {
163                irc_send_num( irc, 442, "%s :You're not on that channel", cmd[1] );
164        }
165}
166
167static void irc_cmd_whois( irc_t *irc, char **cmd )
168{
169        char *nick = cmd[1];
170        irc_user_t *iu = irc_user_by_name( irc, nick );
171       
172        if( iu )
173                irc_send_whois( iu );
174        else
175                irc_send_num( irc, 401, "%s :Nick does not exist", nick );
176}
177
178static void irc_cmd_whowas( irc_t *irc, char **cmd )
179{
180        /* For some reason irssi tries a whowas when whois fails. We can
181           ignore this, but then the user never gets a "user not found"
182           message from irssi which is a bit annoying. So just respond
183           with not-found and irssi users will get better error messages */
184       
185        irc_send_num( irc, 406, "%s :Nick does not exist", cmd[1] );
186        irc_send_num( irc, 369, "%s :End of WHOWAS", cmd[1] );
187}
188
189static void irc_cmd_motd( irc_t *irc, char **cmd )
190{
191        irc_send_motd( irc );
192}
193
194static void irc_cmd_mode( irc_t *irc, char **cmd )
195{
196        if( irc_channel_name_ok( cmd[1] ) )
197        {
198                irc_channel_t *ic;
199               
200                if( ( ic = irc_channel_by_name( irc, cmd[1] ) ) == NULL )
201                        irc_send_num( irc, 403, "%s :No such channel", cmd[1] );
202                else if( cmd[2] )
203                {
204                        if( *cmd[2] == '+' || *cmd[2] == '-' )
205                                irc_send_num( irc, 477, "%s :Can't change channel modes", cmd[1] );
206                        else if( *cmd[2] == 'b' )
207                                irc_send_num( irc, 368, "%s :No bans possible", cmd[1] );
208                }
209                else
210                        irc_send_num( irc, 324, "%s +%s", cmd[1], ic->mode );
211        }
212        else
213        {
214                if( nick_cmp( cmd[1], irc->user->nick ) == 0 )
215                {
216                        if( cmd[2] )
217                                irc_umode_set( irc, cmd[2], 0 );
218                        else
219                                irc_send_num( irc, 221, "+%s", irc->umode );
220                }
221                else
222                        irc_send_num( irc, 502, ":Don't touch their modes" );
223        }
224}
225
226static void irc_cmd_who( irc_t *irc, char **cmd )
227{
228        char *channel = cmd[1];
229        irc_channel_t *ic;
230       
231        if( !channel || *channel == '0' || *channel == '*' || !*channel )
232                irc_send_who( irc, irc->users, "**" );
233        else if( ( ic = irc_channel_by_name( irc, channel ) ) )
234                irc_send_who( irc, ic->users, channel );
235        else
236                irc_send_num( irc, 403, "%s :No such channel", channel );
237}
238
239static void irc_cmd_privmsg( irc_t *irc, char **cmd )
240{
241        irc_channel_t *ic;
242        irc_user_t *iu;
243       
244        if( !cmd[2] ) 
245        {
246                irc_send_num( irc, 412, ":No text to send" );
247        }
248        else if( irc_channel_name_ok( cmd[1] ) &&
249                 ( ic = irc_channel_by_name( irc, cmd[1] ) ) )
250        {
251                if( ic->f->privmsg )
252                        ic->f->privmsg( ic, cmd[2] );
253        }
254        else if( ( iu = irc_user_by_name( irc, cmd[1] ) ) )
255        {
256                if( iu->f->privmsg )
257                        iu->f->privmsg( iu, cmd[2] );
258        }
259        else
260        {
261                irc_send_num( irc, 401, "%s :No such nick/channel", cmd[1] );
262        }
263
264
265#if 0
266        else if( irc->nick && g_strcasecmp( cmd[1], irc->nick ) == 0 )
267        {
268        }
269        else
270        {
271                if( g_strcasecmp( cmd[1], irc->channel ) == 0 )
272                {
273                        unsigned int i;
274                        char *t = set_getstr( &irc->set, "default_target" );
275                       
276                        if( g_strcasecmp( t, "last" ) == 0 && irc->last_target )
277                                cmd[1] = irc->last_target;
278                        else if( g_strcasecmp( t, "root" ) == 0 )
279                                cmd[1] = irc->mynick;
280                       
281                        for( i = 0; i < strlen( cmd[2] ); i ++ )
282                        {
283                                if( cmd[2][i] == ' ' ) break;
284                                if( cmd[2][i] == ':' || cmd[2][i] == ',' )
285                                {
286                                        cmd[1] = cmd[2];
287                                        cmd[2] += i;
288                                        *cmd[2] = 0;
289                                        while( *(++cmd[2]) == ' ' );
290                                        break;
291                                }
292                        }
293                       
294                        irc->is_private = 0;
295                       
296                        if( cmd[1] != irc->last_target )
297                        {
298                                g_free( irc->last_target );
299                                irc->last_target = g_strdup( cmd[1] );
300                        }
301                }
302                else
303                {
304                        irc->is_private = 1;
305                }
306                irc_send( irc, cmd[1], cmd[2], ( g_strcasecmp( cmd[0], "NOTICE" ) == 0 ) ? OPT_AWAY : 0 );
307        }
308#endif
309}
310
311static void irc_cmd_nickserv( irc_t *irc, char **cmd )
312{
313        /* [SH] This aliases the NickServ command to PRIVMSG root */
314        /* [TV] This aliases the NS command to PRIVMSG root as well */
315        root_command( irc, cmd + 1 );
316}
317
318
319
320#if 0
321//#if 0
322static void irc_cmd_oper( irc_t *irc, char **cmd )
323{
324        if( global.conf->oper_pass &&
325            ( strncmp( global.conf->oper_pass, "md5:", 4 ) == 0 ?
326                md5_verify_password( cmd[2], global.conf->oper_pass + 4 ) == 0 :
327                strcmp( cmd[2], global.conf->oper_pass ) == 0 ) )
328        {
329                irc_umode_set( irc, "+o", 1 );
330                irc_send_num( irc, 381, ":Password accepted" );
331        }
332        else
333        {
334                irc_send_num( irc, 432, ":Incorrect password" );
335        }
336}
337
338static void irc_cmd_invite( irc_t *irc, char **cmd )
339{
340        char *nick = cmd[1], *channel = cmd[2];
341        struct groupchat *c = irc_chat_by_channel( irc, channel );
342        user_t *u = user_find( irc, nick );
343       
344        if( u && c && ( u->ic == c->ic ) )
345                if( c->ic && c->ic->acc->prpl->chat_invite )
346                {
347                        c->ic->acc->prpl->chat_invite( c, u->handle, NULL );
348                        irc_send_num( irc, 341, "%s %s", nick, channel );
349                        return;
350                }
351       
352        irc_send_num( irc, 482, "%s :Invite impossible; User/Channel non-existent or incompatible", channel );
353}
354
355static void irc_cmd_userhost( irc_t *irc, char **cmd )
356{
357        user_t *u;
358        int i;
359       
360        /* [TV] Usable USERHOST-implementation according to
361                RFC1459. Without this, mIRC shows an error
362                while connecting, and the used way of rejecting
363                breaks standards.
364        */
365       
366        for( i = 1; cmd[i]; i ++ )
367                if( ( u = user_find( irc, cmd[i] ) ) )
368                {
369                        if( u->online && u->away )
370                                irc_send_num( irc, 302, ":%s=-%s@%s", u->nick, u->user, u->host );
371                        else
372                                irc_send_num( irc, 302, ":%s=+%s@%s", u->nick, u->user, u->host );
373                }
374}
375
376static void irc_cmd_ison( irc_t *irc, char **cmd )
377{
378        user_t *u;
379        char buff[IRC_MAX_LINE];
380        int lenleft, i;
381       
382        buff[0] = '\0';
383       
384        /* [SH] Leave room for : and \0 */
385        lenleft = IRC_MAX_LINE - 2;
386       
387        for( i = 1; cmd[i]; i ++ )
388        {
389                char *this, *next;
390               
391                this = cmd[i];
392                while( *this )
393                {
394                        if( ( next = strchr( this, ' ' ) ) )
395                                *next = 0;
396                       
397                        if( ( u = user_find( irc, this ) ) && u->online )
398                        {
399                                lenleft -= strlen( u->nick ) + 1;
400                               
401                                if( lenleft < 0 )
402                                        break;
403                               
404                                strcat( buff, u->nick );
405                                strcat( buff, " " );
406                        }
407                       
408                        if( next )
409                        {
410                                *next = ' ';
411                                this = next + 1;
412                        }
413                        else
414                        {
415                                break;
416                        }   
417                }
418               
419                /* *sigh* */
420                if( lenleft < 0 )
421                        break;
422        }
423       
424        if( strlen( buff ) > 0 )
425                buff[strlen(buff)-1] = '\0';
426       
427        irc_send_num( irc, 303, ":%s", buff );
428}
429
430static void irc_cmd_watch( irc_t *irc, char **cmd )
431{
432        int i;
433       
434        /* Obviously we could also mark a user structure as being
435           watched, but what if the WATCH command is sent right
436           after connecting? The user won't exist yet then... */
437        for( i = 1; cmd[i]; i ++ )
438        {
439                char *nick;
440                user_t *u;
441               
442                if( !cmd[i][0] || !cmd[i][1] )
443                        break;
444               
445                nick = g_strdup( cmd[i] + 1 );
446                nick_lc( nick );
447               
448                u = user_find( irc, nick );
449               
450                if( cmd[i][0] == '+' )
451                {
452                        if( !g_hash_table_lookup( irc->watches, nick ) )
453                                g_hash_table_insert( irc->watches, nick, nick );
454                       
455                        if( u && u->online )
456                                irc_send_num( irc, 604, "%s %s %s %d :%s", u->nick, u->user, u->host, (int) time( NULL ), "is online" );
457                        else
458                                irc_send_num( irc, 605, "%s %s %s %d :%s", nick, "*", "*", (int) time( NULL ), "is offline" );
459                }
460                else if( cmd[i][0] == '-' )
461                {
462                        gpointer okey, ovalue;
463                       
464                        if( g_hash_table_lookup_extended( irc->watches, nick, &okey, &ovalue ) )
465                        {
466                                g_hash_table_remove( irc->watches, okey );
467                                g_free( okey );
468                               
469                                irc_send_num( irc, 602, "%s %s %s %d :%s", nick, "*", "*", 0, "Stopped watching" );
470                        }
471                }
472        }
473}
474
475static void irc_cmd_topic( irc_t *irc, char **cmd )
476{
477        char *channel = cmd[1];
478        char *topic = cmd[2];
479       
480        if( topic )
481        {
482                /* Send the topic */
483                struct groupchat *c = irc_chat_by_channel( irc, channel );
484                if( c && c->ic && c->ic->acc->prpl->chat_topic )
485                        c->ic->acc->prpl->chat_topic( c, topic );
486        }
487        else
488        {
489                /* Get the topic */
490                irc_topic( irc, channel );
491        }
492}
493
494static void irc_cmd_away( irc_t *irc, char **cmd )
495{
496        user_t *u = user_find( irc, irc->nick );
497        char *away = cmd[1];
498       
499        if( !u ) return;
500       
501        if( away && *away )
502        {
503                int i, j;
504               
505                /* Copy away string, but skip control chars. Mainly because
506                   Jabber really doesn't like them. */
507                u->away = g_malloc( strlen( away ) + 1 );
508                for( i = j = 0; away[i]; i ++ )
509                        if( ( u->away[j] = away[i] ) >= ' ' )
510                                j ++;
511                u->away[j] = 0;
512               
513                irc_send_num( irc, 306, ":You're now away: %s", u->away );
514                /* irc_umode_set( irc, irc->myhost, "+a" ); */
515        }
516        else
517        {
518                if( u->away ) g_free( u->away );
519                u->away = NULL;
520                /* irc_umode_set( irc, irc->myhost, "-a" ); */
521                irc_send_num( irc, 305, ":Welcome back" );
522        }
523       
524        set_setstr( &irc->set, "away", u->away );
525}
526
527static void irc_cmd_version( irc_t *irc, char **cmd )
528{
529        irc_send_num( irc, 351, "bitlbee-%s. %s :%s/%s ", BITLBEE_VERSION, irc->myhost, ARCH, CPU );
530}
531
532static void irc_cmd_completions( irc_t *irc, char **cmd )
533{
534        user_t *u = user_find( irc, irc->mynick );
535        help_t *h;
536        set_t *s;
537        int i;
538       
539        irc_privmsg( irc, u, "NOTICE", irc->nick, "COMPLETIONS ", "OK" );
540       
541        for( i = 0; commands[i].command; i ++ )
542                irc_privmsg( irc, u, "NOTICE", irc->nick, "COMPLETIONS ", commands[i].command );
543       
544        for( h = global.help; h; h = h->next )
545                irc_privmsg( irc, u, "NOTICE", irc->nick, "COMPLETIONS help ", h->title );
546       
547        for( s = irc->set; s; s = s->next )
548                irc_privmsg( irc, u, "NOTICE", irc->nick, "COMPLETIONS set ", s->key );
549       
550        irc_privmsg( irc, u, "NOTICE", irc->nick, "COMPLETIONS ", "END" );
551}
552
553static void irc_cmd_rehash( irc_t *irc, char **cmd )
554{
555        if( global.conf->runmode == RUNMODE_INETD )
556                ipc_master_cmd_rehash( NULL, NULL );
557        else
558                ipc_to_master( cmd );
559       
560        irc_send_num( irc, 382, "%s :Rehashing", global.conf_file );
561}
562#endif
563
564static const command_t irc_commands[] = {
565        { "pass",        1, irc_cmd_pass,        0 },
566        { "user",        4, irc_cmd_user,        IRC_CMD_PRE_LOGIN },
567        { "nick",        1, irc_cmd_nick,        0 },
568        { "quit",        0, irc_cmd_quit,        0 },
569        { "ping",        0, irc_cmd_ping,        0 },
570        { "pong",        0, irc_cmd_pong,        IRC_CMD_LOGGED_IN },
571        { "join",        1, irc_cmd_join,        IRC_CMD_LOGGED_IN },
572        { "names",       1, irc_cmd_names,       IRC_CMD_LOGGED_IN },
573        { "part",        1, irc_cmd_part,        IRC_CMD_LOGGED_IN },
574        { "whois",       1, irc_cmd_whois,       IRC_CMD_LOGGED_IN },
575        { "whowas",      1, irc_cmd_whowas,      IRC_CMD_LOGGED_IN },
576        { "motd",        0, irc_cmd_motd,        IRC_CMD_LOGGED_IN },
577        { "mode",        1, irc_cmd_mode,        IRC_CMD_LOGGED_IN },
578        { "who",         0, irc_cmd_who,         IRC_CMD_LOGGED_IN },
579        { "privmsg",     1, irc_cmd_privmsg,     IRC_CMD_LOGGED_IN },
580        { "nickserv",    1, irc_cmd_nickserv,    IRC_CMD_LOGGED_IN },
581        { "ns",          1, irc_cmd_nickserv,    IRC_CMD_LOGGED_IN },
582#if 0
583        { "oper",        2, irc_cmd_oper,        IRC_CMD_LOGGED_IN },
584        { "invite",      2, irc_cmd_invite,      IRC_CMD_LOGGED_IN },
585        { "notice",      1, irc_cmd_privmsg,     IRC_CMD_LOGGED_IN },
586        { "userhost",    1, irc_cmd_userhost,    IRC_CMD_LOGGED_IN },
587        { "ison",        1, irc_cmd_ison,        IRC_CMD_LOGGED_IN },
588        { "watch",       1, irc_cmd_watch,       IRC_CMD_LOGGED_IN },
589        { "topic",       1, irc_cmd_topic,       IRC_CMD_LOGGED_IN },
590        { "away",        0, irc_cmd_away,        IRC_CMD_LOGGED_IN },
591        { "version",     0, irc_cmd_version,     IRC_CMD_LOGGED_IN },
592        { "completions", 0, irc_cmd_completions, IRC_CMD_LOGGED_IN },
593        { "die",         0, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
594        { "deaf",        0, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
595        { "wallops",     1, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
596        { "wall",        1, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
597        { "rehash",      0, irc_cmd_rehash,      IRC_CMD_OPER_ONLY },
598        { "restart",     0, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
599        { "kill",        2, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
600#endif
601        { NULL }
602};
603
604void irc_exec( irc_t *irc, char *cmd[] )
605{       
606        int i, n_arg;
607       
608        if( !cmd[0] )
609                return;
610       
611        for( i = 0; irc_commands[i].command; i++ )
612                if( g_strcasecmp( irc_commands[i].command, cmd[0] ) == 0 )
613                {
614                        /* There should be no typo in the next line: */
615                        for( n_arg = 0; cmd[n_arg]; n_arg ++ ); n_arg --;
616                       
617                        if( irc_commands[i].flags & IRC_CMD_PRE_LOGIN && irc->status & USTATUS_LOGGED_IN )
618                        {
619                                irc_send_num( irc, 462, ":Only allowed before logging in" );
620                        }
621                        else if( irc_commands[i].flags & IRC_CMD_LOGGED_IN && !( irc->status & USTATUS_LOGGED_IN ) )
622                        {
623                                irc_send_num( irc, 451, ":Register first" );
624                        }
625                        else if( irc_commands[i].flags & IRC_CMD_OPER_ONLY && !strchr( irc->umode, 'o' ) )
626                        {
627                                irc_send_num( irc, 481, ":Permission denied - You're not an IRC operator" );
628                        }
629                        else if( n_arg < irc_commands[i].required_parameters )
630                        {
631                                irc_send_num( irc, 461, "%s :Need more parameters", cmd[0] );
632                        }
633                        else if( irc_commands[i].flags & IRC_CMD_TO_MASTER )
634                        {
635                                /* IPC doesn't make sense in inetd mode,
636                                    but the function will catch that. */
637                                ipc_to_master( cmd );
638                        }
639                        else
640                        {
641                                irc_commands[i].execute( irc, cmd );
642                        }
643                       
644                        return;
645                }
646       
647        if( irc->status >= USTATUS_LOGGED_IN )
648                irc_send_num( irc, 421, "%s :Unknown command", cmd[0] );
649}
Note: See TracBrowser for help on using the repository browser.