source: irc_send.c @ 0dd6570

Last change on this file since 0dd6570 was 3864c08, checked in by Wilmer van der Gaast <wilmer@…>, at 2011-10-21T03:00:54Z

Big merge from pesco, closing some OTR issues: #759, #824, #839, #830.

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