source: irc_send.c @ 5e98ff0

Last change on this file since 5e98ff0 was 006a84f, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-07-04T20:40:15Z

Kick the user instead of parting him/her when cleaning up a channel. This is
what the older version also did so that Irssi won't clean up the window.

  • Property mode set to 100644
File size: 11.2 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/* The IRC-based UI - Sending responses to commands/etc.                */
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#include "bitlbee.h"
27
28void irc_send_num( irc_t *irc, int code, char *format, ... )
29{
30        char text[IRC_MAX_LINE];
31        va_list params;
32       
33        va_start( params, format );
34        g_vsnprintf( text, IRC_MAX_LINE, format, params );
35        va_end( params );
36       
37        irc_write( irc, ":%s %03d %s %s", irc->root->host, code, irc->user->nick ? : "*", text );
38}
39
40void irc_send_login( irc_t *irc )
41{
42        irc_send_num( irc,   1, ":Welcome to the BitlBee gateway, %s", irc->user->nick );
43        irc_send_num( irc,   2, ":Host %s is running BitlBee " BITLBEE_VERSION " " ARCH "/" CPU ".", irc->root->host );
44        irc_send_num( irc,   3, ":%s", IRCD_INFO );
45        irc_send_num( irc,   4, "%s %s %s %s", irc->root->host, BITLBEE_VERSION, UMODES UMODES_PRIV, CMODES );
46        irc_send_num( irc,   5, "PREFIX=(ov)@+ CHANTYPES=%s CHANMODES=,,,%s NICKLEN=%d NETWORK=BitlBee "
47                                "CASEMAPPING=rfc1459 MAXTARGETS=1 WATCH=128 :are supported by this server",
48                                CTYPES, CMODES, MAX_NICK_LENGTH - 1 );
49        irc_send_motd( irc );
50}
51
52void irc_send_motd( irc_t *irc )
53{
54        int fd;
55       
56        fd = open( global.conf->motdfile, O_RDONLY );
57        if( fd == -1 )
58        {
59                irc_send_num( irc, 422, ":We don't need MOTDs." );
60        }
61        else
62        {
63                char linebuf[80];       /* Max. line length for MOTD's is 79 chars. It's what most IRC networks seem to do. */
64                char *add, max;
65                int len;
66               
67                linebuf[79] = len = 0;
68                max = sizeof( linebuf ) - 1;
69               
70                irc_send_num( irc, 375, ":- %s Message Of The Day - ", irc->root->host );
71                while( read( fd, linebuf + len, 1 ) == 1 )
72                {
73                        if( linebuf[len] == '\n' || len == max )
74                        {
75                                linebuf[len] = 0;
76                                irc_send_num( irc, 372, ":- %s", linebuf );
77                                len = 0;
78                        }
79                        else if( linebuf[len] == '%' )
80                        {
81                                read( fd, linebuf + len, 1 );
82                                if( linebuf[len] == 'h' )
83                                        add = irc->root->host;
84                                else if( linebuf[len] == 'v' )
85                                        add = BITLBEE_VERSION;
86                                else if( linebuf[len] == 'n' )
87                                        add = irc->user->nick;
88                                else
89                                        add = "%";
90                               
91                                strncpy( linebuf + len, add, max - len );
92                                while( linebuf[++len] );
93                        }
94                        else if( len < max )
95                        {
96                                len ++;
97                        }
98                }
99                irc_send_num( irc, 376, ":End of MOTD" );
100                close( fd );
101        }
102}
103
104void irc_usermsg( irc_t *irc, char *format, ... )
105{
106        irc_channel_t *ic;
107        irc_user_t *iu;
108        char text[1024];
109        va_list params;
110       
111        va_start( params, format );
112        g_vsnprintf( text, sizeof( text ), format, params );
113        va_end( params );
114       
115        if( irc->last_root_cmd &&
116            irc_channel_name_ok( irc->last_root_cmd ) && 
117            ( ic = irc_channel_by_name( irc, irc->last_root_cmd ) ) &&
118            ic->flags & IRC_CHANNEL_JOINED )
119                irc_send_msg( irc->root, "PRIVMSG", irc->last_root_cmd, text, NULL );
120        else if( irc->last_root_cmd &&
121                 ( iu = irc_user_by_name( irc, irc->last_root_cmd ) ) &&
122                 iu->f == &irc_user_root_funcs )
123                irc_send_msg( iu, "PRIVMSG", irc->user->nick, text, NULL );
124        else
125        {
126                g_free( irc->last_root_cmd );
127                irc->last_root_cmd = NULL;
128               
129                irc_send_msg( irc->root, "PRIVMSG", irc->user->nick, text, NULL );
130        }
131       
132        /*return( irc_msgfrom( irc, u->nick, text ) );*/
133}
134
135void irc_send_join( irc_channel_t *ic, irc_user_t *iu )
136{
137        irc_t *irc = ic->irc;
138       
139        irc_write( irc, ":%s!%s@%s JOIN :%s", iu->nick, iu->user, iu->host, ic->name );
140       
141        if( iu == irc->user )
142        {
143                irc_write( irc, ":%s MODE %s +%s", irc->root->host, ic->name, ic->mode );
144                irc_send_names( ic );
145                if( ic->topic && *ic->topic )
146                        irc_send_topic( ic, FALSE );
147        }
148}
149
150void irc_send_part( irc_channel_t *ic, irc_user_t *iu, const char *reason )
151{
152        irc_write( ic->irc, ":%s!%s@%s PART %s :%s", iu->nick, iu->user, iu->host, ic->name, reason ? : "" );
153}
154
155void irc_send_quit( irc_user_t *iu, const char *reason )
156{
157        irc_write( iu->irc, ":%s!%s@%s QUIT :%s", iu->nick, iu->user, iu->host, reason ? : "" );
158}
159
160void irc_send_kick( irc_channel_t *ic, irc_user_t *iu, irc_user_t *kicker, const char *reason )
161{
162        irc_write( ic->irc, ":%s!%s@%s KICK %s %s :%s", kicker->nick, kicker->user,
163                   kicker->host, ic->name, iu->nick, reason ? : "" );
164}
165
166void irc_send_names( irc_channel_t *ic )
167{
168        GSList *l;
169        char namelist[385] = "";
170       
171        /* RFCs say there is no error reply allowed on NAMES, so when the
172           channel is invalid, just give an empty reply. */
173        for( l = ic->users; l; l = l->next )
174        {
175                irc_channel_user_t *icu = l->data;
176                irc_user_t *iu = icu->iu;
177               
178                if( strlen( namelist ) + strlen( iu->nick ) > sizeof( namelist ) - 4 )
179                {
180                        irc_send_num( ic->irc, 353, "= %s :%s", ic->name, namelist );
181                        *namelist = 0;
182                }
183               
184                if( icu->flags & IRC_CHANNEL_USER_OP )
185                        strcat( namelist, "@" );
186                else if( icu->flags & IRC_CHANNEL_USER_HALFOP )
187                        strcat( namelist, "%" );
188                else if( icu->flags & IRC_CHANNEL_USER_VOICE )
189                        strcat( namelist, "+" );
190               
191                strcat( namelist, iu->nick );
192                strcat( namelist, " " );
193        }
194       
195        if( *namelist )
196                irc_send_num( ic->irc, 353, "= %s :%s", ic->name, namelist );
197       
198        irc_send_num( ic->irc, 366, "%s :End of /NAMES list", ic->name );
199}
200
201void irc_send_topic( irc_channel_t *ic, gboolean topic_change )
202{
203        if( topic_change && ic->topic_who )
204        {
205                irc_write( ic->irc, ":%s TOPIC %s :%s", ic->topic_who, 
206                           ic->name, ic->topic && *ic->topic ? ic->topic : "" );
207        }
208        else if( ic->topic )
209        {
210                irc_send_num( ic->irc, 332, "%s :%s", ic->name, ic->topic );
211                if( ic->topic_who )
212                        irc_send_num( ic->irc, 333, "%s %s %d",
213                                      ic->name, ic->topic_who, (int) ic->topic_time );
214        }
215        else
216                irc_send_num( ic->irc, 331, "%s :No topic for this channel", ic->name );
217}
218
219void irc_send_whois( irc_user_t *iu )
220{
221        irc_t *irc = iu->irc;
222       
223        irc_send_num( irc, 311, "%s %s %s * :%s",
224                      iu->nick, iu->user, iu->host, iu->fullname );
225       
226        if( iu->bu )
227        {
228                bee_user_t *bu = iu->bu;
229               
230                irc_send_num( irc, 312, "%s %s.%s :%s network", iu->nick, bu->ic->acc->user,
231                           bu->ic->acc->server && *bu->ic->acc->server ? bu->ic->acc->server : "",
232                           bu->ic->acc->prpl->name );
233               
234                if( ( bu->status && *bu->status ) ||
235                    ( bu->status_msg && *bu->status_msg ) )
236                {
237                        int num = bu->flags & BEE_USER_AWAY ? 301 : 320;
238                       
239                        if( bu->status && bu->status_msg )
240                                irc_send_num( irc, num, "%s :%s (%s)", iu->nick, bu->status, bu->status_msg );
241                        else
242                                irc_send_num( irc, num, "%s :%s", iu->nick, bu->status ? : bu->status_msg );
243                }
244                else if( !( bu->flags & BEE_USER_ONLINE ) )
245                {
246                        irc_send_num( irc, 301, "%s :%s", iu->nick, "User is offline" );
247                }
248               
249                if( bu->idle_time || bu->login_time )
250                {
251                        irc_send_num( irc, 317, "%s %d %d :seconds idle, signon time",
252                                      iu->nick,
253                                      bu->idle_time ? (int) ( time( NULL ) - bu->idle_time ) : 0,
254                                      (int) bu->login_time );
255                }
256        }
257        else
258        {
259                irc_send_num( irc, 312, "%s %s :%s", iu->nick, irc->root->host, IRCD_INFO );
260        }
261       
262        irc_send_num( irc, 318, "%s :End of /WHOIS list", iu->nick );
263}
264
265void irc_send_who( irc_t *irc, GSList *l, const char *channel )
266{
267        gboolean is_channel = strcmp( channel, "**" ) != 0;
268       
269        while( l )
270        {
271                irc_user_t *iu = l->data;
272                if( is_channel )
273                        iu = ((irc_channel_user_t*)iu)->iu;
274                /* TODO(wilmer): Restore away/channel information here */
275                irc_send_num( irc, 352, "%s %s %s %s %s %c :0 %s",
276                              channel ? : "*", iu->user, iu->host, irc->root->host,
277                              iu->nick, iu->flags & IRC_USER_AWAY ? 'G' : 'H',
278                              iu->fullname );
279                l = l->next;
280        }
281       
282        irc_send_num( irc, 315, "%s :End of /WHO list", channel );
283}
284
285void irc_send_msg( irc_user_t *iu, const char *type, const char *dst, const char *msg, const char *prefix )
286{
287        char last = 0;
288        const char *s = msg, *line = msg;
289        char raw_msg[strlen(msg)+1024];
290       
291        while( !last )
292        {
293                if( *s == '\r' && *(s+1) == '\n' )
294                        s++;
295                if( *s == '\n' )
296                {
297                        last = s[1] == 0;
298                }
299                else
300                {
301                        last = s[0] == 0;
302                }
303                if( *s == 0 || *s == '\n' )
304                {
305                        if( g_strncasecmp( line, "/me ", 4 ) == 0 && ( !prefix || !*prefix ) &&
306                            g_strcasecmp( type, "PRIVMSG" ) == 0 )
307                        {
308                                strcpy( raw_msg, "\001ACTION " );
309                                strncat( raw_msg, line + 4, s - line - 4 );
310                                strcat( raw_msg, "\001" );
311                                irc_send_msg_raw( iu, type, dst, raw_msg );
312                        }
313                        else
314                        {
315                                *raw_msg = '\0';
316                                if( prefix && *prefix )
317                                        strcpy( raw_msg, prefix );
318                                strncat( raw_msg, line, s - line );
319                                irc_send_msg_raw( iu, type, dst, raw_msg );
320                        }
321                        line = s + 1;
322                }
323                s ++;
324        }
325}
326
327void irc_send_msg_raw( irc_user_t *iu, const char *type, const char *dst, const char *msg )
328{
329        irc_write( iu->irc, ":%s!%s@%s %s %s :%s",
330                   iu->nick, iu->user, iu->host, type, dst, msg );
331}
332
333void irc_send_msg_f( irc_user_t *iu, const char *type, const char *dst, const char *format, ... )
334{
335        char text[IRC_MAX_LINE];
336        va_list params;
337       
338        va_start( params, format );
339        g_vsnprintf( text, IRC_MAX_LINE, format, params );
340        va_end( params );
341       
342        irc_write( iu->irc, ":%s!%s@%s %s %s :%s",
343                   iu->nick, iu->user, iu->host, type, dst, text );
344}
345
346void irc_send_nick( irc_user_t *iu, const char *new )
347{
348        irc_write( iu->irc, ":%s!%s@%s NICK %s",
349                   iu->nick, iu->user, iu->host, new );
350}
351
352/* Send an update of a user's mode inside a channel, compared to what it was. */
353void irc_send_channel_user_mode_diff( irc_channel_t *ic, irc_user_t *iu,
354        irc_channel_user_flags_t old, irc_channel_user_flags_t new )
355{
356        char changes[3*(5+strlen(iu->nick))];
357        char from[strlen(ic->irc->root->nick)+strlen(ic->irc->root->user)+strlen(ic->irc->root->host)+3];
358        int n;
359       
360        *changes = '\0'; n = 0;
361        if( ( old & IRC_CHANNEL_USER_OP ) != ( new & IRC_CHANNEL_USER_OP ) )
362        {
363                n ++;
364                if( new & IRC_CHANNEL_USER_OP )
365                        strcat( changes, "+o" );
366                else
367                        strcat( changes, "-o" );
368        }
369        if( ( old & IRC_CHANNEL_USER_HALFOP ) != ( new & IRC_CHANNEL_USER_HALFOP ) )
370        {
371                n ++;
372                if( new & IRC_CHANNEL_USER_HALFOP )
373                        strcat( changes, "+h" );
374                else
375                        strcat( changes, "-h" );
376        }
377        if( ( old & IRC_CHANNEL_USER_VOICE ) != ( new & IRC_CHANNEL_USER_VOICE ) )
378        {
379                n ++;
380                if( new & IRC_CHANNEL_USER_VOICE )
381                        strcat( changes, "+v" );
382                else
383                        strcat( changes, "-v" );
384        }
385        while( n )
386        {
387                strcat( changes, " " );
388                strcat( changes, iu->nick );
389                n --;
390        }
391       
392        if( set_getbool( &ic->irc->b->set, "simulate_netsplit" ) )
393                g_snprintf( from, sizeof( from ), "%s", ic->irc->root->host );
394        else
395                g_snprintf( from, sizeof( from ), "%s!%s@%s", ic->irc->root->nick,
396                            ic->irc->root->user, ic->irc->root->host );
397       
398        irc_write( ic->irc, ":%s MODE %s %s", from, ic->name, changes );
399}
Note: See TracBrowser for help on using the repository browser.