source: irc_send.c @ 762d96f

Last change on this file since 762d96f was f32c14c, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-08-09T13:39:50Z

Oops. We're using channel mode +h (halfop) for a while already, but so far
without advertising this in 005. This confuses irssi ans possibly other
clients. Fixing that now. Also adding SAFELIST which may make some clients
less afraid of using /LIST, and CHANNELLEN with an artifical limit (the
code has no max. channel name limit).

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