source: irc_send.c @ ea2828f

Last change on this file since ea2828f was 0e788f5, checked in by Wilmer van der Gaast <wilmer@…>, at 2013-02-21T19:15:59Z

I'm still bored on a long flight. Wrote a script to automatically update
my copyright mentions since some were getting pretty stale. Left files not
touched since before 2012 alone so that this change doesn't touch almost
EVERY source file.

  • 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-2012 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.