source: irc_commands.c @ 74f1cde

Last change on this file since 74f1cde was 280c56a, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-03-27T17:36:47Z

Added privmsg handlers to users/channels. root commands are coming back.

  • 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_join( irc_t *irc, char **cmd )
111{
112        irc_channel_t *ic;
113       
114        if( ( ic = irc_channel_by_name( irc, cmd[1] ) ) == NULL )
115                ic = irc_channel_new( irc, cmd[1] );
116       
117        if( ic == NULL )
118                irc_send_num( irc, 479, "%s :Invalid channel name", cmd[1] );
119       
120        if( ic->flags & IRC_CHANNEL_JOINED )
121                return; /* Dude, you're already there...
122                           RFC doesn't have any reply for that though? */
123       
124        irc_channel_add_user( ic, irc->user );
125}
126
127static void irc_cmd_names( irc_t *irc, char **cmd )
128{
129        irc_channel_t *ic;
130       
131        if( cmd[1] && ( ic = irc_channel_by_name( irc, cmd[1] ) ) )
132                irc_send_names( ic );
133        /* With no args, we should show /names of all chans. Make the code
134           below work well if necessary.
135        else
136        {
137                GSList *l;
138               
139                for( l = irc->channels; l; l = l->next )
140                        irc_send_names( l->data );
141        }
142        */
143}
144
145static void irc_cmd_part( irc_t *irc, char **cmd )
146{
147        irc_channel_t *ic;
148       
149        if( ( ic = irc_channel_by_name( irc, cmd[1] ) ) == NULL )
150        {
151                irc_send_num( irc, 403, "%s :No such channel", cmd[1] );
152        }
153        else if( !irc_channel_del_user( ic, irc->user ) )
154        {
155                irc_send_num( irc, 442, "%s :You're not on that channel", cmd[1] );
156        }
157}
158
159static void irc_cmd_whois( irc_t *irc, char **cmd )
160{
161        char *nick = cmd[1];
162        irc_user_t *iu = irc_user_by_name( irc, nick );
163       
164        if( iu )
165                irc_send_whois( iu );
166        else
167                irc_send_num( irc, 401, "%s :Nick does not exist", nick );
168}
169
170static void irc_cmd_whowas( irc_t *irc, char **cmd )
171{
172        /* For some reason irssi tries a whowas when whois fails. We can
173           ignore this, but then the user never gets a "user not found"
174           message from irssi which is a bit annoying. So just respond
175           with not-found and irssi users will get better error messages */
176       
177        irc_send_num( irc, 406, "%s :Nick does not exist", cmd[1] );
178        irc_send_num( irc, 369, "%s :End of WHOWAS", cmd[1] );
179}
180
181static void irc_cmd_motd( irc_t *irc, char **cmd )
182{
183        irc_send_motd( irc );
184}
185
186static void irc_cmd_mode( irc_t *irc, char **cmd )
187{
188        if( irc_channel_name_ok( cmd[1] ) )
189        {
190                irc_channel_t *ic;
191               
192                if( ( ic = irc_channel_by_name( irc, cmd[1] ) ) == NULL )
193                        irc_send_num( irc, 403, "%s :No such channel", cmd[1] );
194                else if( cmd[2] )
195                {
196                        if( *cmd[2] == '+' || *cmd[2] == '-' )
197                                irc_send_num( irc, 477, "%s :Can't change channel modes", cmd[1] );
198                        else if( *cmd[2] == 'b' )
199                                irc_send_num( irc, 368, "%s :No bans possible", cmd[1] );
200                }
201                else
202                        irc_send_num( irc, 324, "%s +%s", cmd[1], ic->mode );
203        }
204        else
205        {
206                if( nick_cmp( cmd[1], irc->user->nick ) == 0 )
207                {
208                        if( cmd[2] )
209                                irc_umode_set( irc, cmd[2], 0 );
210                        else
211                                irc_send_num( irc, 221, "+%s", irc->umode );
212                }
213                else
214                        irc_send_num( irc, 502, ":Don't touch their modes" );
215        }
216}
217
218static void irc_cmd_who( irc_t *irc, char **cmd )
219{
220        char *channel = cmd[1];
221        irc_channel_t *ic;
222       
223        if( !channel || *channel == '0' || *channel == '*' || !*channel )
224                irc_send_who( irc, irc->users, "**" );
225        else if( ( ic = irc_channel_by_name( irc, channel ) ) )
226                irc_send_who( irc, ic->users, channel );
227        else
228                irc_send_num( irc, 403, "%s :No such channel", channel );
229}
230
231static void irc_cmd_privmsg( irc_t *irc, char **cmd )
232{
233        irc_channel_t *ic;
234        irc_user_t *iu;
235       
236        if( !cmd[2] ) 
237        {
238                irc_send_num( irc, 412, ":No text to send" );
239        }
240        else if( irc_channel_name_ok( cmd[1] ) &&
241                 ( ic = irc_channel_by_name( irc, cmd[1] ) ) )
242        {
243                if( ic->f->privmsg )
244                        ic->f->privmsg( ic, cmd[2] );
245        }
246        else if( ( iu = irc_user_by_name( irc, cmd[1] ) ) )
247        {
248                if( iu->f->privmsg )
249                        iu->f->privmsg( iu, cmd[2] );
250        }
251        else
252        {
253                irc_send_num( irc, 401, "%s :No such nick/channel", cmd[1] );
254        }
255
256
257#if 0
258        else if( irc->nick && g_strcasecmp( cmd[1], irc->nick ) == 0 )
259        {
260        }
261        else
262        {
263                if( g_strcasecmp( cmd[1], irc->channel ) == 0 )
264                {
265                        unsigned int i;
266                        char *t = set_getstr( &irc->set, "default_target" );
267                       
268                        if( g_strcasecmp( t, "last" ) == 0 && irc->last_target )
269                                cmd[1] = irc->last_target;
270                        else if( g_strcasecmp( t, "root" ) == 0 )
271                                cmd[1] = irc->mynick;
272                       
273                        for( i = 0; i < strlen( cmd[2] ); i ++ )
274                        {
275                                if( cmd[2][i] == ' ' ) break;
276                                if( cmd[2][i] == ':' || cmd[2][i] == ',' )
277                                {
278                                        cmd[1] = cmd[2];
279                                        cmd[2] += i;
280                                        *cmd[2] = 0;
281                                        while( *(++cmd[2]) == ' ' );
282                                        break;
283                                }
284                        }
285                       
286                        irc->is_private = 0;
287                       
288                        if( cmd[1] != irc->last_target )
289                        {
290                                g_free( irc->last_target );
291                                irc->last_target = g_strdup( cmd[1] );
292                        }
293                }
294                else
295                {
296                        irc->is_private = 1;
297                }
298                irc_send( irc, cmd[1], cmd[2], ( g_strcasecmp( cmd[0], "NOTICE" ) == 0 ) ? OPT_AWAY : 0 );
299        }
300#endif
301}
302
303
304
305#if 0
306//#if 0
307static void irc_cmd_oper( irc_t *irc, char **cmd )
308{
309        if( global.conf->oper_pass &&
310            ( strncmp( global.conf->oper_pass, "md5:", 4 ) == 0 ?
311                md5_verify_password( cmd[2], global.conf->oper_pass + 4 ) == 0 :
312                strcmp( cmd[2], global.conf->oper_pass ) == 0 ) )
313        {
314                irc_umode_set( irc, "+o", 1 );
315                irc_send_num( irc, 381, ":Password accepted" );
316        }
317        else
318        {
319                irc_send_num( irc, 432, ":Incorrect password" );
320        }
321}
322
323static void irc_cmd_invite( irc_t *irc, char **cmd )
324{
325        char *nick = cmd[1], *channel = cmd[2];
326        struct groupchat *c = irc_chat_by_channel( irc, channel );
327        user_t *u = user_find( irc, nick );
328       
329        if( u && c && ( u->ic == c->ic ) )
330                if( c->ic && c->ic->acc->prpl->chat_invite )
331                {
332                        c->ic->acc->prpl->chat_invite( c, u->handle, NULL );
333                        irc_send_num( irc, 341, "%s %s", nick, channel );
334                        return;
335                }
336       
337        irc_send_num( irc, 482, "%s :Invite impossible; User/Channel non-existent or incompatible", channel );
338}
339
340static void irc_cmd_userhost( irc_t *irc, char **cmd )
341{
342        user_t *u;
343        int i;
344       
345        /* [TV] Usable USERHOST-implementation according to
346                RFC1459. Without this, mIRC shows an error
347                while connecting, and the used way of rejecting
348                breaks standards.
349        */
350       
351        for( i = 1; cmd[i]; i ++ )
352                if( ( u = user_find( irc, cmd[i] ) ) )
353                {
354                        if( u->online && u->away )
355                                irc_send_num( irc, 302, ":%s=-%s@%s", u->nick, u->user, u->host );
356                        else
357                                irc_send_num( irc, 302, ":%s=+%s@%s", u->nick, u->user, u->host );
358                }
359}
360
361static void irc_cmd_ison( irc_t *irc, char **cmd )
362{
363        user_t *u;
364        char buff[IRC_MAX_LINE];
365        int lenleft, i;
366       
367        buff[0] = '\0';
368       
369        /* [SH] Leave room for : and \0 */
370        lenleft = IRC_MAX_LINE - 2;
371       
372        for( i = 1; cmd[i]; i ++ )
373        {
374                char *this, *next;
375               
376                this = cmd[i];
377                while( *this )
378                {
379                        if( ( next = strchr( this, ' ' ) ) )
380                                *next = 0;
381                       
382                        if( ( u = user_find( irc, this ) ) && u->online )
383                        {
384                                lenleft -= strlen( u->nick ) + 1;
385                               
386                                if( lenleft < 0 )
387                                        break;
388                               
389                                strcat( buff, u->nick );
390                                strcat( buff, " " );
391                        }
392                       
393                        if( next )
394                        {
395                                *next = ' ';
396                                this = next + 1;
397                        }
398                        else
399                        {
400                                break;
401                        }   
402                }
403               
404                /* *sigh* */
405                if( lenleft < 0 )
406                        break;
407        }
408       
409        if( strlen( buff ) > 0 )
410                buff[strlen(buff)-1] = '\0';
411       
412        irc_send_num( irc, 303, ":%s", buff );
413}
414
415static void irc_cmd_watch( irc_t *irc, char **cmd )
416{
417        int i;
418       
419        /* Obviously we could also mark a user structure as being
420           watched, but what if the WATCH command is sent right
421           after connecting? The user won't exist yet then... */
422        for( i = 1; cmd[i]; i ++ )
423        {
424                char *nick;
425                user_t *u;
426               
427                if( !cmd[i][0] || !cmd[i][1] )
428                        break;
429               
430                nick = g_strdup( cmd[i] + 1 );
431                nick_lc( nick );
432               
433                u = user_find( irc, nick );
434               
435                if( cmd[i][0] == '+' )
436                {
437                        if( !g_hash_table_lookup( irc->watches, nick ) )
438                                g_hash_table_insert( irc->watches, nick, nick );
439                       
440                        if( u && u->online )
441                                irc_send_num( irc, 604, "%s %s %s %d :%s", u->nick, u->user, u->host, (int) time( NULL ), "is online" );
442                        else
443                                irc_send_num( irc, 605, "%s %s %s %d :%s", nick, "*", "*", (int) time( NULL ), "is offline" );
444                }
445                else if( cmd[i][0] == '-' )
446                {
447                        gpointer okey, ovalue;
448                       
449                        if( g_hash_table_lookup_extended( irc->watches, nick, &okey, &ovalue ) )
450                        {
451                                g_hash_table_remove( irc->watches, okey );
452                                g_free( okey );
453                               
454                                irc_send_num( irc, 602, "%s %s %s %d :%s", nick, "*", "*", 0, "Stopped watching" );
455                        }
456                }
457        }
458}
459
460static void irc_cmd_topic( irc_t *irc, char **cmd )
461{
462        char *channel = cmd[1];
463        char *topic = cmd[2];
464       
465        if( topic )
466        {
467                /* Send the topic */
468                struct groupchat *c = irc_chat_by_channel( irc, channel );
469                if( c && c->ic && c->ic->acc->prpl->chat_topic )
470                        c->ic->acc->prpl->chat_topic( c, topic );
471        }
472        else
473        {
474                /* Get the topic */
475                irc_topic( irc, channel );
476        }
477}
478
479static void irc_cmd_away( irc_t *irc, char **cmd )
480{
481        user_t *u = user_find( irc, irc->nick );
482        char *away = cmd[1];
483       
484        if( !u ) return;
485       
486        if( away && *away )
487        {
488                int i, j;
489               
490                /* Copy away string, but skip control chars. Mainly because
491                   Jabber really doesn't like them. */
492                u->away = g_malloc( strlen( away ) + 1 );
493                for( i = j = 0; away[i]; i ++ )
494                        if( ( u->away[j] = away[i] ) >= ' ' )
495                                j ++;
496                u->away[j] = 0;
497               
498                irc_send_num( irc, 306, ":You're now away: %s", u->away );
499                /* irc_umode_set( irc, irc->myhost, "+a" ); */
500        }
501        else
502        {
503                if( u->away ) g_free( u->away );
504                u->away = NULL;
505                /* irc_umode_set( irc, irc->myhost, "-a" ); */
506                irc_send_num( irc, 305, ":Welcome back" );
507        }
508       
509        set_setstr( &irc->set, "away", u->away );
510}
511
512static void irc_cmd_nickserv( irc_t *irc, char **cmd )
513{
514        /* [SH] This aliases the NickServ command to PRIVMSG root */
515        /* [TV] This aliases the NS command to PRIVMSG root as well */
516        root_command( irc, cmd + 1 );
517}
518
519static void irc_cmd_pong( irc_t *irc, char **cmd )
520{
521        /* We could check the value we get back from the user, but in
522           fact we don't care, we're just happy he's still alive. */
523        irc->last_pong = gettime();
524        irc->pinging = 0;
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        { "join",        1, irc_cmd_join,        IRC_CMD_LOGGED_IN },
571        { "names",       1, irc_cmd_names,       IRC_CMD_LOGGED_IN },
572        { "part",        1, irc_cmd_part,        IRC_CMD_LOGGED_IN },
573        { "whois",       1, irc_cmd_whois,       IRC_CMD_LOGGED_IN },
574        { "whowas",      1, irc_cmd_whowas,      IRC_CMD_LOGGED_IN },
575        { "motd",        0, irc_cmd_motd,        IRC_CMD_LOGGED_IN },
576        { "mode",        1, irc_cmd_mode,        IRC_CMD_LOGGED_IN },
577        { "who",         0, irc_cmd_who,         IRC_CMD_LOGGED_IN },
578        { "privmsg",     1, irc_cmd_privmsg,     IRC_CMD_LOGGED_IN },
579#if 0
580        { "oper",        2, irc_cmd_oper,        IRC_CMD_LOGGED_IN },
581        { "invite",      2, irc_cmd_invite,      IRC_CMD_LOGGED_IN },
582        { "notice",      1, irc_cmd_privmsg,     IRC_CMD_LOGGED_IN },
583        { "userhost",    1, irc_cmd_userhost,    IRC_CMD_LOGGED_IN },
584        { "ison",        1, irc_cmd_ison,        IRC_CMD_LOGGED_IN },
585        { "watch",       1, irc_cmd_watch,       IRC_CMD_LOGGED_IN },
586        { "topic",       1, irc_cmd_topic,       IRC_CMD_LOGGED_IN },
587        { "away",        0, irc_cmd_away,        IRC_CMD_LOGGED_IN },
588        { "nickserv",    1, irc_cmd_nickserv,    IRC_CMD_LOGGED_IN },
589        { "ns",          1, irc_cmd_nickserv,    IRC_CMD_LOGGED_IN },
590        { "pong",        0, irc_cmd_pong,        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.