Shut up a flood of GLib-related compiler warnings.
[bitlbee.git] / irc_send.c
blobba9678bb6e121c536ec98d720af2fafaf6c0bb46
1 /********************************************************************\
2 * BitlBee -- An IRC to other IM-networks gateway *
3 * *
4 * Copyright 2002-2010 Wilmer van der Gaast and others *
5 \********************************************************************/
7 /* The IRC-based UI - Sending responses to commands/etc. */
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.
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.
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
26 #include "bitlbee.h"
28 void irc_send_num( irc_t *irc, int code, char *format, ... )
30 char text[IRC_MAX_LINE];
31 va_list params;
33 va_start( params, format );
34 g_vsnprintf( text, IRC_MAX_LINE, format, params );
35 va_end( params );
37 irc_write( irc, ":%s %03d %s %s", irc->root->host, code, irc->user->nick ? : "*", text );
40 void irc_send_login( irc_t *irc )
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 );
54 void irc_send_motd( irc_t *irc )
56 char motd[2048];
57 size_t len;
58 int fd;
60 fd = open( global.conf->motdfile, O_RDONLY );
61 if( fd == -1 || ( len = read( fd, motd, sizeof( motd ) - 1 ) ) <= 0 )
63 irc_send_num( irc, 422, ":We don't need MOTDs." );
65 else
67 char linebuf[80];
68 char *add = "", max, *in;
70 in = motd;
71 motd[len] = '\0';
72 linebuf[79] = len = 0;
73 max = sizeof( linebuf ) - 1;
75 irc_send_num( irc, 375, ":- %s Message Of The Day - ", irc->root->host );
76 while( ( linebuf[len] = *(in++) ) )
78 if( linebuf[len] == '\n' || len == max )
80 linebuf[len] = 0;
81 irc_send_num( irc, 372, ":- %s", linebuf );
82 len = 0;
84 else if( linebuf[len] == '%' )
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 = "%";
98 strncpy( linebuf + len, add, max - len );
99 while( linebuf[++len] );
101 else if( len < max )
103 len ++;
106 irc_send_num( irc, 376, ":End of MOTD" );
109 if( fd != -1 )
110 close( fd );
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. */
116 const char *irc_user_msgdest( irc_user_t *iu )
118 irc_t *irc = iu->irc;
119 irc_channel_t *ic = NULL;
121 if( iu->last_channel )
123 if( iu->last_channel->flags & IRC_CHANNEL_JOINED )
124 ic = iu->last_channel;
125 else
126 ic = irc_channel_with_user( irc, iu );
129 if( ic )
130 return ic->name;
131 else
132 return irc->user->nick;
135 /* cmd = "PRIVMSG" or "NOTICE" */
136 static void irc_usermsg_( const char *cmd, irc_user_t *iu, const char *format, va_list params )
138 char text[2048];
139 const char *dst;
141 g_vsnprintf( text, sizeof( text ), format, params );
143 dst = irc_user_msgdest( iu );
144 irc_send_msg( iu, cmd, dst, text, NULL );
147 void irc_usermsg(irc_user_t *iu, char *format, ... )
149 va_list params;
150 va_start( params, format );
151 irc_usermsg_( "PRIVMSG", iu, format, params );
152 va_end( params );
155 void irc_usernotice(irc_user_t *iu, char *format, ... )
157 va_list params;
158 va_start( params, format );
159 irc_usermsg_( "NOTICE", iu, format, params );
160 va_end( params );
163 void irc_rootmsg( irc_t *irc, char *format, ... )
165 va_list params;
166 va_start( params, format );
167 irc_usermsg_( "PRIVMSG", irc->root, format, params );
168 va_end( params );
171 void irc_send_join( irc_channel_t *ic, irc_user_t *iu )
173 irc_t *irc = ic->irc;
175 irc_write( irc, ":%s!%s@%s JOIN :%s", iu->nick, iu->user, iu->host, ic->name );
177 if( iu == irc->user )
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 );
186 void irc_send_part( irc_channel_t *ic, irc_user_t *iu, const char *reason )
188 irc_write( ic->irc, ":%s!%s@%s PART %s :%s", iu->nick, iu->user, iu->host, ic->name, reason ? : "" );
191 void irc_send_quit( irc_user_t *iu, const char *reason )
193 irc_write( iu->irc, ":%s!%s@%s QUIT :%s", iu->nick, iu->user, iu->host, reason ? : "" );
196 void irc_send_kick( irc_channel_t *ic, irc_user_t *iu, irc_user_t *kicker, const char *reason )
198 irc_write( ic->irc, ":%s!%s@%s KICK %s %s :%s", kicker->nick, kicker->user,
199 kicker->host, ic->name, iu->nick, reason ? : "" );
202 void irc_send_names( irc_channel_t *ic )
204 GSList *l;
205 char namelist[385] = "";
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 )
211 irc_channel_user_t *icu = l->data;
212 irc_user_t *iu = icu->iu;
214 if( strlen( namelist ) + strlen( iu->nick ) > sizeof( namelist ) - 4 )
216 irc_send_num( ic->irc, 353, "= %s :%s", ic->name, namelist );
217 *namelist = 0;
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, "+" );
227 strcat( namelist, iu->nick );
228 strcat( namelist, " " );
231 if( *namelist )
232 irc_send_num( ic->irc, 353, "= %s :%s", ic->name, namelist );
234 irc_send_num( ic->irc, 366, "%s :End of /NAMES list", ic->name );
237 void irc_send_topic( irc_channel_t *ic, gboolean topic_change )
239 if( topic_change && ic->topic_who )
241 irc_write( ic->irc, ":%s TOPIC %s :%s", ic->topic_who,
242 ic->name, ic->topic && *ic->topic ? ic->topic : "" );
244 else if( ic->topic )
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 );
251 else
252 irc_send_num( ic->irc, 331, "%s :No topic for this channel", ic->name );
255 void irc_send_whois( irc_user_t *iu )
257 irc_t *irc = iu->irc;
259 irc_send_num( irc, 311, "%s %s %s * :%s",
260 iu->nick, iu->user, iu->host, iu->fullname );
262 if( iu->bu )
264 bee_user_t *bu = iu->bu;
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 );
270 if( ( bu->status && *bu->status ) ||
271 ( bu->status_msg && *bu->status_msg ) )
273 int num = bu->flags & BEE_USER_AWAY ? 301 : 320;
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 );
280 else if( !( bu->flags & BEE_USER_ONLINE ) )
282 irc_send_num( irc, 301, "%s :%s", iu->nick, "User is offline" );
285 if( bu->idle_time || bu->login_time )
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 );
293 else
295 irc_send_num( irc, 312, "%s %s :%s", iu->nick, irc->root->host, IRCD_INFO );
298 irc_send_num( irc, 318, "%s :End of /WHOIS list", iu->nick );
301 void irc_send_who( irc_t *irc, GSList *l, const char *channel )
303 gboolean is_channel = strchr( CTYPES, channel[0] ) != NULL;
305 while( l )
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;
318 irc_send_num( irc, 315, "%s :End of /WHO list", channel );
321 void irc_send_msg( irc_user_t *iu, const char *type, const char *dst, const char *msg, const char *prefix )
323 char last = 0;
324 const char *s = msg, *line = msg;
325 char raw_msg[strlen(msg)+1024];
327 while( !last )
329 if( *s == '\r' && *(s+1) == '\n' )
330 s++;
331 if( *s == '\n' )
333 last = s[1] == 0;
335 else
337 last = s[0] == 0;
339 if( *s == 0 || *s == '\n' )
341 if( g_strncasecmp( line, "/me ", 4 ) == 0 && ( !prefix || !*prefix ) &&
342 g_strcasecmp( type, "PRIVMSG" ) == 0 )
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 );
349 else
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 );
357 line = s + 1;
359 s ++;
363 void irc_send_msg_raw( irc_user_t *iu, const char *type, const char *dst, const char *msg )
365 irc_write( iu->irc, ":%s!%s@%s %s %s :%s",
366 iu->nick, iu->user, iu->host, type, dst, msg && *msg ? msg : " " );
369 void irc_send_msg_f( irc_user_t *iu, const char *type, const char *dst, const char *format, ... )
371 char text[IRC_MAX_LINE];
372 va_list params;
374 va_start( params, format );
375 g_vsnprintf( text, IRC_MAX_LINE, format, params );
376 va_end( params );
378 irc_write( iu->irc, ":%s!%s@%s %s %s :%s",
379 iu->nick, iu->user, iu->host, type, dst, text );
382 void irc_send_nick( irc_user_t *iu, const char *new )
384 irc_write( iu->irc, ":%s!%s@%s NICK %s",
385 iu->nick, iu->user, iu->host, new );
388 /* Send an update of a user's mode inside a channel, compared to what it was. */
389 void 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 )
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;
396 *changes = '\0'; n = 0;
397 if( ( old & IRC_CHANNEL_USER_OP ) != ( new & IRC_CHANNEL_USER_OP ) )
399 n ++;
400 if( new & IRC_CHANNEL_USER_OP )
401 strcat( changes, "+o" );
402 else
403 strcat( changes, "-o" );
405 if( ( old & IRC_CHANNEL_USER_HALFOP ) != ( new & IRC_CHANNEL_USER_HALFOP ) )
407 n ++;
408 if( new & IRC_CHANNEL_USER_HALFOP )
409 strcat( changes, "+h" );
410 else
411 strcat( changes, "-h" );
413 if( ( old & IRC_CHANNEL_USER_VOICE ) != ( new & IRC_CHANNEL_USER_VOICE ) )
415 n ++;
416 if( new & IRC_CHANNEL_USER_VOICE )
417 strcat( changes, "+v" );
418 else
419 strcat( changes, "-v" );
421 while( n )
423 strcat( changes, " " );
424 strcat( changes, iu->nick );
425 n --;
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 );
434 if( *changes )
435 irc_write( ic->irc, ":%s MODE %s %s", from, ic->name, changes );
438 void irc_send_invite( irc_user_t *iu, irc_channel_t *ic )
440 irc_t *irc = iu->irc;
442 irc_write( iu->irc, ":%s!%s@%s INVITE %s :%s",
443 iu->nick, iu->user, iu->host, irc->user->nick, ic->name );