source: irc_send.c @ e9caacd

Last change on this file since e9caacd was 18e1f3b, checked in by Wilmer van der Gaast <wilmer@…>, at 2012-05-02T07:58:22Z

Set PACKAGE to BitlBee-LIBPURPLE for the libpurple variant, because in many
ways it's not BitlBee and I'm tired of getting libpurple-related bug reports.

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