1 /********************************************************************\
2 * BitlBee -- An IRC to other IM-networks gateway *
4 * Copyright 2002-2010 Wilmer van der Gaast and others *
5 \********************************************************************/
7 /* The IRC-based UI - Sending responses to commands/etc. */
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
28 void irc_send_num( irc_t
*irc
, int code
, char *format
, ... )
30 char text
[IRC_MAX_LINE
];
33 va_start( params
, format
);
34 g_vsnprintf( text
, IRC_MAX_LINE
, format
, 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 );
54 void irc_send_motd( irc_t
*irc
)
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." );
68 char *add
= "", max
, *in
;
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
)
81 irc_send_num( irc
, 372, ":- %s", linebuf
);
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' )
98 strncpy( linebuf
+ len
, add
, max
- len
);
99 while( linebuf
[++len
] );
106 irc_send_num( irc
, 376, ":End of MOTD" );
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
;
126 ic
= irc_channel_with_user( irc
, iu
);
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
)
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
, ... )
150 va_start( params
, format
);
151 irc_usermsg_( "PRIVMSG", iu
, format
, params
);
155 void irc_usernotice(irc_user_t
*iu
, char *format
, ... )
158 va_start( params
, format
);
159 irc_usermsg_( "NOTICE", iu
, format
, params
);
163 void irc_rootmsg( irc_t
*irc
, char *format
, ... )
166 va_start( params
, format
);
167 irc_usermsg_( "PRIVMSG", irc
->root
, format
, 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
)
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
);
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
, " " );
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
: "" );
246 irc_send_num( ic
->irc
, 332, "%s :%s", ic
->name
, ic
->topic
);
248 irc_send_num( ic
->irc
, 333, "%s %s %d",
249 ic
->name
, ic
->topic_who
, (int) ic
->topic_time
);
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
);
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
);
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",
289 bu
->idle_time
? (int) ( time( NULL
) - bu
->idle_time
) : 0,
290 (int) bu
->login_time
);
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
;
307 irc_user_t
*iu
= l
->data
;
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',
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
)
324 const char *s
= msg
, *line
= msg
;
325 char raw_msg
[strlen(msg
)+1024];
329 if( *s
== '\r' && *(s
+1) == '\n' )
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
);
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
);
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
];
374 va_start( params
, format
);
375 g_vsnprintf( text
, IRC_MAX_LINE
, format
, 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];
396 *changes
= '\0'; n
= 0;
397 if( ( old
& IRC_CHANNEL_USER_OP
) != ( new & IRC_CHANNEL_USER_OP
) )
400 if( new & IRC_CHANNEL_USER_OP
)
401 strcat( changes
, "+o" );
403 strcat( changes
, "-o" );
405 if( ( old
& IRC_CHANNEL_USER_HALFOP
) != ( new & IRC_CHANNEL_USER_HALFOP
) )
408 if( new & IRC_CHANNEL_USER_HALFOP
)
409 strcat( changes
, "+h" );
411 strcat( changes
, "-h" );
413 if( ( old
& IRC_CHANNEL_USER_VOICE
) != ( new & IRC_CHANNEL_USER_VOICE
) )
416 if( new & IRC_CHANNEL_USER_VOICE
)
417 strcat( changes
, "+v" );
419 strcat( changes
, "-v" );
423 strcat( changes
, " " );
424 strcat( changes
, iu
->nick
);
428 if( set_getbool( &ic
->irc
->b
->set
, "simulate_netsplit" ) )
429 g_snprintf( from
, sizeof( from
), "%s", ic
->irc
->root
->host
);
431 g_snprintf( from
, sizeof( from
), "%s!%s@%s", ic
->irc
->root
->nick
,
432 ic
->irc
->root
->user
, ic
->irc
->root
->host
);
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
);