source: irc_commands.c @ 2f53ada

Last change on this file since 2f53ada was 2f53ada, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-03-27T15:16:16Z

/WHO

  • Property mode set to 100644
File size: 16.8 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_find( 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_find( 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        struct groupchat *c;
223        GList *l;
224       
225        if( !channel || *channel == '0' || *channel == '*' || !*channel )
226                irc_send_who( irc, irc->users, "**" );
227        else if( ( ic = irc_channel_by_name( irc, channel ) ) )
228                irc_send_who( irc, ic->users, channel );
229        else
230                irc_send_num( irc, 403, "%s :No such channel", channel );
231}
232
233#if 0
234//#if 0
235static void irc_cmd_oper( irc_t *irc, char **cmd )
236{
237        if( global.conf->oper_pass &&
238            ( strncmp( global.conf->oper_pass, "md5:", 4 ) == 0 ?
239                md5_verify_password( cmd[2], global.conf->oper_pass + 4 ) == 0 :
240                strcmp( cmd[2], global.conf->oper_pass ) == 0 ) )
241        {
242                irc_umode_set( irc, "+o", 1 );
243                irc_send_num( irc, 381, ":Password accepted" );
244        }
245        else
246        {
247                irc_send_num( irc, 432, ":Incorrect password" );
248        }
249}
250
251static void irc_cmd_invite( irc_t *irc, char **cmd )
252{
253        char *nick = cmd[1], *channel = cmd[2];
254        struct groupchat *c = irc_chat_by_channel( irc, channel );
255        user_t *u = user_find( irc, nick );
256       
257        if( u && c && ( u->ic == c->ic ) )
258                if( c->ic && c->ic->acc->prpl->chat_invite )
259                {
260                        c->ic->acc->prpl->chat_invite( c, u->handle, NULL );
261                        irc_send_num( irc, 341, "%s %s", nick, channel );
262                        return;
263                }
264       
265        irc_send_num( irc, 482, "%s :Invite impossible; User/Channel non-existent or incompatible", channel );
266}
267
268static void irc_cmd_privmsg( irc_t *irc, char **cmd )
269{
270        if( !cmd[2] )
271        {
272                irc_send_num( irc, 412, ":No text to send" );
273        }
274        else if( irc->nick && g_strcasecmp( cmd[1], irc->nick ) == 0 )
275        {
276                irc_write( irc, ":%s!%s@%s %s %s :%s", irc->nick, irc->user, irc->host, cmd[0], cmd[1], cmd[2] );
277        }
278        else
279        {
280                if( g_strcasecmp( cmd[1], irc->channel ) == 0 )
281                {
282                        unsigned int i;
283                        char *t = set_getstr( &irc->set, "default_target" );
284                       
285                        if( g_strcasecmp( t, "last" ) == 0 && irc->last_target )
286                                cmd[1] = irc->last_target;
287                        else if( g_strcasecmp( t, "root" ) == 0 )
288                                cmd[1] = irc->mynick;
289                       
290                        for( i = 0; i < strlen( cmd[2] ); i ++ )
291                        {
292                                if( cmd[2][i] == ' ' ) break;
293                                if( cmd[2][i] == ':' || cmd[2][i] == ',' )
294                                {
295                                        cmd[1] = cmd[2];
296                                        cmd[2] += i;
297                                        *cmd[2] = 0;
298                                        while( *(++cmd[2]) == ' ' );
299                                        break;
300                                }
301                        }
302                       
303                        irc->is_private = 0;
304                       
305                        if( cmd[1] != irc->last_target )
306                        {
307                                g_free( irc->last_target );
308                                irc->last_target = g_strdup( cmd[1] );
309                        }
310                }
311                else
312                {
313                        irc->is_private = 1;
314                }
315                irc_send( irc, cmd[1], cmd[2], ( g_strcasecmp( cmd[0], "NOTICE" ) == 0 ) ? OPT_AWAY : 0 );
316        }
317}
318
319static void irc_cmd_userhost( irc_t *irc, char **cmd )
320{
321        user_t *u;
322        int i;
323       
324        /* [TV] Usable USERHOST-implementation according to
325                RFC1459. Without this, mIRC shows an error
326                while connecting, and the used way of rejecting
327                breaks standards.
328        */
329       
330        for( i = 1; cmd[i]; i ++ )
331                if( ( u = user_find( irc, cmd[i] ) ) )
332                {
333                        if( u->online && u->away )
334                                irc_send_num( irc, 302, ":%s=-%s@%s", u->nick, u->user, u->host );
335                        else
336                                irc_send_num( irc, 302, ":%s=+%s@%s", u->nick, u->user, u->host );
337                }
338}
339
340static void irc_cmd_ison( irc_t *irc, char **cmd )
341{
342        user_t *u;
343        char buff[IRC_MAX_LINE];
344        int lenleft, i;
345       
346        buff[0] = '\0';
347       
348        /* [SH] Leave room for : and \0 */
349        lenleft = IRC_MAX_LINE - 2;
350       
351        for( i = 1; cmd[i]; i ++ )
352        {
353                char *this, *next;
354               
355                this = cmd[i];
356                while( *this )
357                {
358                        if( ( next = strchr( this, ' ' ) ) )
359                                *next = 0;
360                       
361                        if( ( u = user_find( irc, this ) ) && u->online )
362                        {
363                                lenleft -= strlen( u->nick ) + 1;
364                               
365                                if( lenleft < 0 )
366                                        break;
367                               
368                                strcat( buff, u->nick );
369                                strcat( buff, " " );
370                        }
371                       
372                        if( next )
373                        {
374                                *next = ' ';
375                                this = next + 1;
376                        }
377                        else
378                        {
379                                break;
380                        }   
381                }
382               
383                /* *sigh* */
384                if( lenleft < 0 )
385                        break;
386        }
387       
388        if( strlen( buff ) > 0 )
389                buff[strlen(buff)-1] = '\0';
390       
391        irc_send_num( irc, 303, ":%s", buff );
392}
393
394static void irc_cmd_watch( irc_t *irc, char **cmd )
395{
396        int i;
397       
398        /* Obviously we could also mark a user structure as being
399           watched, but what if the WATCH command is sent right
400           after connecting? The user won't exist yet then... */
401        for( i = 1; cmd[i]; i ++ )
402        {
403                char *nick;
404                user_t *u;
405               
406                if( !cmd[i][0] || !cmd[i][1] )
407                        break;
408               
409                nick = g_strdup( cmd[i] + 1 );
410                nick_lc( nick );
411               
412                u = user_find( irc, nick );
413               
414                if( cmd[i][0] == '+' )
415                {
416                        if( !g_hash_table_lookup( irc->watches, nick ) )
417                                g_hash_table_insert( irc->watches, nick, nick );
418                       
419                        if( u && u->online )
420                                irc_send_num( irc, 604, "%s %s %s %d :%s", u->nick, u->user, u->host, (int) time( NULL ), "is online" );
421                        else
422                                irc_send_num( irc, 605, "%s %s %s %d :%s", nick, "*", "*", (int) time( NULL ), "is offline" );
423                }
424                else if( cmd[i][0] == '-' )
425                {
426                        gpointer okey, ovalue;
427                       
428                        if( g_hash_table_lookup_extended( irc->watches, nick, &okey, &ovalue ) )
429                        {
430                                g_hash_table_remove( irc->watches, okey );
431                                g_free( okey );
432                               
433                                irc_send_num( irc, 602, "%s %s %s %d :%s", nick, "*", "*", 0, "Stopped watching" );
434                        }
435                }
436        }
437}
438
439static void irc_cmd_topic( irc_t *irc, char **cmd )
440{
441        char *channel = cmd[1];
442        char *topic = cmd[2];
443       
444        if( topic )
445        {
446                /* Send the topic */
447                struct groupchat *c = irc_chat_by_channel( irc, channel );
448                if( c && c->ic && c->ic->acc->prpl->chat_topic )
449                        c->ic->acc->prpl->chat_topic( c, topic );
450        }
451        else
452        {
453                /* Get the topic */
454                irc_topic( irc, channel );
455        }
456}
457
458static void irc_cmd_away( irc_t *irc, char **cmd )
459{
460        user_t *u = user_find( irc, irc->nick );
461        char *away = cmd[1];
462       
463        if( !u ) return;
464       
465        if( away && *away )
466        {
467                int i, j;
468               
469                /* Copy away string, but skip control chars. Mainly because
470                   Jabber really doesn't like them. */
471                u->away = g_malloc( strlen( away ) + 1 );
472                for( i = j = 0; away[i]; i ++ )
473                        if( ( u->away[j] = away[i] ) >= ' ' )
474                                j ++;
475                u->away[j] = 0;
476               
477                irc_send_num( irc, 306, ":You're now away: %s", u->away );
478                /* irc_umode_set( irc, irc->myhost, "+a" ); */
479        }
480        else
481        {
482                if( u->away ) g_free( u->away );
483                u->away = NULL;
484                /* irc_umode_set( irc, irc->myhost, "-a" ); */
485                irc_send_num( irc, 305, ":Welcome back" );
486        }
487       
488        set_setstr( &irc->set, "away", u->away );
489}
490
491static void irc_cmd_nickserv( irc_t *irc, char **cmd )
492{
493        /* [SH] This aliases the NickServ command to PRIVMSG root */
494        /* [TV] This aliases the NS command to PRIVMSG root as well */
495        root_command( irc, cmd + 1 );
496}
497
498static void irc_cmd_pong( irc_t *irc, char **cmd )
499{
500        /* We could check the value we get back from the user, but in
501           fact we don't care, we're just happy he's still alive. */
502        irc->last_pong = gettime();
503        irc->pinging = 0;
504}
505
506static void irc_cmd_version( irc_t *irc, char **cmd )
507{
508        irc_send_num( irc, 351, "bitlbee-%s. %s :%s/%s ", BITLBEE_VERSION, irc->myhost, ARCH, CPU );
509}
510
511static void irc_cmd_completions( irc_t *irc, char **cmd )
512{
513        user_t *u = user_find( irc, irc->mynick );
514        help_t *h;
515        set_t *s;
516        int i;
517       
518        irc_privmsg( irc, u, "NOTICE", irc->nick, "COMPLETIONS ", "OK" );
519       
520        for( i = 0; commands[i].command; i ++ )
521                irc_privmsg( irc, u, "NOTICE", irc->nick, "COMPLETIONS ", commands[i].command );
522       
523        for( h = global.help; h; h = h->next )
524                irc_privmsg( irc, u, "NOTICE", irc->nick, "COMPLETIONS help ", h->title );
525       
526        for( s = irc->set; s; s = s->next )
527                irc_privmsg( irc, u, "NOTICE", irc->nick, "COMPLETIONS set ", s->key );
528       
529        irc_privmsg( irc, u, "NOTICE", irc->nick, "COMPLETIONS ", "END" );
530}
531
532static void irc_cmd_rehash( irc_t *irc, char **cmd )
533{
534        if( global.conf->runmode == RUNMODE_INETD )
535                ipc_master_cmd_rehash( NULL, NULL );
536        else
537                ipc_to_master( cmd );
538       
539        irc_send_num( irc, 382, "%s :Rehashing", global.conf_file );
540}
541#endif
542
543static const command_t irc_commands[] = {
544        { "pass",        1, irc_cmd_pass,        0 },
545        { "user",        4, irc_cmd_user,        IRC_CMD_PRE_LOGIN },
546        { "nick",        1, irc_cmd_nick,        0 },
547        { "quit",        0, irc_cmd_quit,        0 },
548        { "ping",        0, irc_cmd_ping,        0 },
549        { "join",        1, irc_cmd_join,        IRC_CMD_LOGGED_IN },
550        { "names",       1, irc_cmd_names,       IRC_CMD_LOGGED_IN },
551        { "part",        1, irc_cmd_part,        IRC_CMD_LOGGED_IN },
552        { "whois",       1, irc_cmd_whois,       IRC_CMD_LOGGED_IN },
553        { "whowas",      1, irc_cmd_whowas,      IRC_CMD_LOGGED_IN },
554        { "motd",        0, irc_cmd_motd,        IRC_CMD_LOGGED_IN },
555        { "mode",        1, irc_cmd_mode,        IRC_CMD_LOGGED_IN },
556        { "who",         0, irc_cmd_who,         IRC_CMD_LOGGED_IN },
557#if 0
558        { "oper",        2, irc_cmd_oper,        IRC_CMD_LOGGED_IN },
559        { "invite",      2, irc_cmd_invite,      IRC_CMD_LOGGED_IN },
560        { "privmsg",     1, irc_cmd_privmsg,     IRC_CMD_LOGGED_IN },
561        { "notice",      1, irc_cmd_privmsg,     IRC_CMD_LOGGED_IN },
562        { "userhost",    1, irc_cmd_userhost,    IRC_CMD_LOGGED_IN },
563        { "ison",        1, irc_cmd_ison,        IRC_CMD_LOGGED_IN },
564        { "watch",       1, irc_cmd_watch,       IRC_CMD_LOGGED_IN },
565        { "topic",       1, irc_cmd_topic,       IRC_CMD_LOGGED_IN },
566        { "away",        0, irc_cmd_away,        IRC_CMD_LOGGED_IN },
567        { "nickserv",    1, irc_cmd_nickserv,    IRC_CMD_LOGGED_IN },
568        { "ns",          1, irc_cmd_nickserv,    IRC_CMD_LOGGED_IN },
569        { "pong",        0, irc_cmd_pong,        IRC_CMD_LOGGED_IN },
570        { "version",     0, irc_cmd_version,     IRC_CMD_LOGGED_IN },
571        { "completions", 0, irc_cmd_completions, IRC_CMD_LOGGED_IN },
572        { "die",         0, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
573        { "deaf",        0, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
574        { "wallops",     1, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
575        { "wall",        1, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
576        { "rehash",      0, irc_cmd_rehash,      IRC_CMD_OPER_ONLY },
577        { "restart",     0, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
578        { "kill",        2, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
579#endif
580        { NULL }
581};
582
583void irc_exec( irc_t *irc, char *cmd[] )
584{       
585        int i, n_arg;
586       
587        if( !cmd[0] )
588                return;
589       
590        for( i = 0; irc_commands[i].command; i++ )
591                if( g_strcasecmp( irc_commands[i].command, cmd[0] ) == 0 )
592                {
593                        /* There should be no typo in the next line: */
594                        for( n_arg = 0; cmd[n_arg]; n_arg ++ ); n_arg --;
595                       
596                        if( irc_commands[i].flags & IRC_CMD_PRE_LOGIN && irc->status & USTATUS_LOGGED_IN )
597                        {
598                                irc_send_num( irc, 462, ":Only allowed before logging in" );
599                        }
600                        else if( irc_commands[i].flags & IRC_CMD_LOGGED_IN && !( irc->status & USTATUS_LOGGED_IN ) )
601                        {
602                                irc_send_num( irc, 451, ":Register first" );
603                        }
604                        else if( irc_commands[i].flags & IRC_CMD_OPER_ONLY && !strchr( irc->umode, 'o' ) )
605                        {
606                                irc_send_num( irc, 481, ":Permission denied - You're not an IRC operator" );
607                        }
608                        else if( n_arg < irc_commands[i].required_parameters )
609                        {
610                                irc_send_num( irc, 461, "%s :Need more parameters", cmd[0] );
611                        }
612                        else if( irc_commands[i].flags & IRC_CMD_TO_MASTER )
613                        {
614                                /* IPC doesn't make sense in inetd mode,
615                                    but the function will catch that. */
616                                ipc_to_master( cmd );
617                        }
618                        else
619                        {
620                                irc_commands[i].execute( irc, cmd );
621                        }
622                       
623                        return;
624                }
625       
626        if( irc->status >= USTATUS_LOGGED_IN )
627                irc_send_num( irc, 421, "%s :Unknown command", cmd[0] );
628}
Note: See TracBrowser for help on using the repository browser.