source: irc_send.c @ a72af0d

Last change on this file since a72af0d was a72af0d, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-08-25T00:18:27Z

Fix /WHO on single nicks.

  • Property mode set to 100644
File size: 11.3 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 = strchr( CTYPES, channel[0] ) != NULL;
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                              is_channel ? 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 && *msg ? 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}
400
401void irc_send_invite( irc_user_t *iu, irc_channel_t *ic )
402{
403        irc_t *irc = iu->irc;
404       
405        irc_write( iu->irc, ":%s!%s@%s INVITE %s :%s",
406                   iu->nick, iu->user, iu->host, irc->user->nick, ic->name );
407}
Note: See TracBrowser for help on using the repository browser.