/********************************************************************\ * BitlBee -- An IRC to other IM-networks gateway * * * * Copyright 2002-2010 Wilmer van der Gaast and others * \********************************************************************/ /* Some glue to put the IRC and the IM stuff together. */ /* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License with the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "bitlbee.h" #include "dcc.h" /* IM->IRC callbacks: Simple IM/buddy-related stuff. */ static const struct irc_user_funcs irc_user_im_funcs; static void bee_irc_imc_connected( struct im_connection *ic ) { irc_t *irc = (irc_t*) ic->bee->ui_data; irc_channel_auto_joins( irc, ic->acc ); } static void bee_irc_imc_disconnected( struct im_connection *ic ) { /* Maybe try to send /QUITs here instead of later on. */ } static gboolean bee_irc_user_new( bee_t *bee, bee_user_t *bu ) { irc_user_t *iu; irc_t *irc = (irc_t*) bee->ui_data; char nick[MAX_NICK_LENGTH+1], *s; memset( nick, 0, MAX_NICK_LENGTH + 1 ); strcpy( nick, nick_get( bu ) ); bu->ui_data = iu = irc_user_new( irc, nick ); iu->bu = bu; if( set_getbool( &irc->b->set, "private" ) ) iu->last_channel = NULL; else iu->last_channel = irc_channel_with_user( irc, iu ); if( ( s = strchr( bu->handle, '@' ) ) ) { iu->host = g_strdup( s + 1 ); iu->user = g_strndup( bu->handle, s - bu->handle ); } else { iu->user = g_strdup( bu->handle ); if( bu->ic->acc->server ) iu->host = g_strdup( bu->ic->acc->server ); else iu->host = g_strdup( bu->ic->acc->prpl->name ); } while( ( s = strchr( iu->user, ' ' ) ) ) *s = '_'; if( bu->flags & BEE_USER_LOCAL ) { char *s = set_getstr( &bee->set, "handle_unknown" ); if( strcmp( s, "add_private" ) == 0 ) iu->last_channel = NULL; else if( strcmp( s, "add_channel" ) == 0 ) iu->last_channel = irc->default_channel; } iu->f = &irc_user_im_funcs; return TRUE; } static gboolean bee_irc_user_free( bee_t *bee, bee_user_t *bu ) { return irc_user_free( bee->ui_data, (irc_user_t *) bu->ui_data ); } static gboolean bee_irc_user_status( bee_t *bee, bee_user_t *bu, bee_user_t *old ) { irc_t *irc = bee->ui_data; irc_user_t *iu = bu->ui_data; /* Do this outside the if below since away state can change without the online state changing. */ iu->flags &= ~IRC_USER_AWAY; if( bu->flags & BEE_USER_AWAY || !( bu->flags & BEE_USER_ONLINE ) ) iu->flags |= IRC_USER_AWAY; if( ( bu->flags & BEE_USER_ONLINE ) != ( old->flags & BEE_USER_ONLINE ) ) { if( bu->flags & BEE_USER_ONLINE ) { if( g_hash_table_lookup( irc->watches, iu->key ) ) irc_send_num( irc, 600, "%s %s %s %d :%s", iu->nick, iu->user, iu->host, (int) time( NULL ), "logged online" ); } else { if( g_hash_table_lookup( irc->watches, iu->key ) ) irc_send_num( irc, 601, "%s %s %s %d :%s", iu->nick, iu->user, iu->host, (int) time( NULL ), "logged offline" ); /* Send a QUIT since those will also show up in any query windows the user may have, plus it's only one QUIT instead of possibly many (in case of multiple control chans). If there's a channel that shows offline people, a JOIN will follow. */ if( set_getbool( &bee->set, "offline_user_quits" ) ) irc_user_quit( iu, "Leaving..." ); } } /* Reset this one since the info may have changed. */ iu->away_reply_timeout = 0; bee_irc_channel_update( irc, NULL, iu ); return TRUE; } void bee_irc_channel_update( irc_t *irc, irc_channel_t *ic, irc_user_t *iu ) { GSList *l; if( ic == NULL ) { for( l = irc->channels; l; l = l->next ) { ic = l->data; /* TODO: Just add a type flag or so.. */ if( ic->f == irc->default_channel->f && ( ic->flags & IRC_CHANNEL_JOINED ) ) bee_irc_channel_update( irc, ic, iu ); } return; } if( iu == NULL ) { for( l = irc->users; l; l = l->next ) { iu = l->data; if( iu->bu ) bee_irc_channel_update( irc, ic, l->data ); } return; } if( !irc_channel_wants_user( ic, iu ) ) { irc_channel_del_user( ic, iu, IRC_CDU_PART, NULL ); } else { struct irc_control_channel *icc = ic->data; int mode = 0; if( !( iu->bu->flags & BEE_USER_ONLINE ) ) mode = icc->modes[0]; else if( iu->bu->flags & BEE_USER_AWAY ) mode = icc->modes[1]; else mode = icc->modes[2]; if( !mode ) irc_channel_del_user( ic, iu, IRC_CDU_PART, NULL ); else { irc_channel_add_user( ic, iu ); irc_channel_user_set_mode( ic, iu, mode ); } } } static gboolean bee_irc_user_msg( bee_t *bee, bee_user_t *bu, const char *msg_, time_t sent_at ) { irc_t *irc = bee->ui_data; irc_user_t *iu = (irc_user_t *) bu->ui_data; char *dst, *prefix = NULL; char *wrapped, *ts = NULL; irc_channel_t *ic = NULL; char *msg = g_strdup( msg_ ); GSList *l; if( sent_at > 0 && set_getbool( &irc->b->set, "display_timestamps" ) ) ts = irc_format_timestamp( irc, sent_at ); /* Too similar to irc_usermsg()... */ if( iu->last_channel ) { if( iu->last_channel->flags & IRC_CHANNEL_JOINED ) ic = iu->last_channel; else ic = irc_channel_with_user( irc, iu ); } if( ic ) { dst = ic->name; prefix = g_strdup_printf( "%s%s%s", irc->user->nick, set_getstr( &bee->set, "to_char" ), ts ? : "" ); } else { dst = irc->user->nick; prefix = ts; ts = NULL; } for( l = irc_plugins; l; l = l->next ) { irc_plugin_t *p = l->data; if( p->filter_msg_in ) { char *s = p->filter_msg_in( iu, msg, 0 ); if( s ) { if( s != msg ) g_free( msg ); msg = s; } else { /* Modules can swallow messages. */ return TRUE; } } } if( ( g_strcasecmp( set_getstr( &bee->set, "strip_html" ), "always" ) == 0 ) || ( ( bu->ic->flags & OPT_DOES_HTML ) && set_getbool( &bee->set, "strip_html" ) ) ) { char *s = g_strdup( msg ); strip_html( s ); g_free( msg ); msg = s; } wrapped = word_wrap( msg, 425 ); irc_send_msg( iu, "PRIVMSG", dst, wrapped, prefix ); g_free( wrapped ); g_free( prefix ); g_free( msg ); g_free( ts ); return TRUE; } static gboolean bee_irc_user_typing( bee_t *bee, bee_user_t *bu, uint32_t flags ) { irc_t *irc = (irc_t *) bee->ui_data; if( set_getbool( &bee->set, "typing_notice" ) ) irc_send_msg_f( (irc_user_t *) bu->ui_data, "PRIVMSG", irc->user->nick, "\001TYPING %d\001", ( flags >> 8 ) & 3 ); else return FALSE; return TRUE; } static gboolean bee_irc_user_action_response( bee_t *bee, bee_user_t *bu, const char *action, char * const args[], void *data ) { irc_t *irc = (irc_t *) bee->ui_data; GString *msg = g_string_new( "\001" ); g_string_append( msg, action ); while( *args ) { if( strchr( *args, ' ' ) ) g_string_append_printf( msg, " \"%s\"", *args ); else g_string_append_printf( msg, " %s", *args ); args ++; } g_string_append_c( msg, '\001' ); irc_send_msg( (irc_user_t *) bu->ui_data, "NOTICE", irc->user->nick, msg->str, NULL ); return TRUE; } static gboolean bee_irc_user_nick_update( irc_user_t *iu ); static gboolean bee_irc_user_fullname( bee_t *bee, bee_user_t *bu ) { irc_user_t *iu = (irc_user_t *) bu->ui_data; char *s; if( iu->fullname != iu->nick ) g_free( iu->fullname ); iu->fullname = g_strdup(
<?xml version='1.0'?>
<!--
Convert BitlBee XML documentation to DocBook
(C) 2004 Jelmer Vernooij
-->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
version="1.1"
extension-element-prefixes="exsl">
<xsl:output method="xml" encoding="UTF-8" doctype-public="-//OASIS//DTD DocBook XML V4.2//EN" indent="yes" doctype-system="http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"/>
<xsl:strip-space elements="*"/>
<xsl:template match="ircline">
<xsl:element name="prompt"><xsl:text>< </xsl:text><xsl:value-of select="@nick"/><xsl:text>> </xsl:text></xsl:element>
<xsl:element name="userinput"><xsl:value-of select="normalize-space(.)"/></xsl:element><xsl:text> </xsl:text>
</xsl:template>
<xsl:template match="ircaction">
<xsl:text> * </xsl:text><xsl:value-of select="@nick"/><xsl:text> </xsl:text><xsl:value-of select="normalize-space(.)"/><xsl:text> </xsl:text>
</xsl:template>
<xsl:template match="ircexample">
<xsl:element name="screen">
<xsl:for-each select="ircline|ircaction">
<xsl:apply-templates select="."/>
</xsl:for-each>
</xsl:element>
</xsl:template>
<!-- This is needed to copy content unchanged -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template name="subcmd-list">
<xsl:if test="bitlbee-command != ''">
<xsl:element name="variablelist">
<xsl:for-each select="bitlbee-command">
<xsl:element name="varlistentry">
<xsl:element name="term">
<xsl:value-of select="@name"/>
</xsl:element>
<xsl:element name="listitem">
<xsl:element name="para">
<xsl:value-of select="short-description"/>
</xsl:element>
</xsl:element>
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:if>
</xsl:template>
<xsl:template match="command-list">
<xsl:call-template name="subcmd-list"/>
</xsl:template>
<xsl:template match="bitlbee-setting">
<xsl:element name="sect1">
<xsl:attribute name="id">
<xsl:text>set_</xsl:text>
<xsl:value-of select="@name"/>
</xsl:attribute>
<xsl:element name="title"><xsl:value-of select="@name"/></xsl:element>
<xsl:element name="simplelist">
<xsl:element name="member">
<xsl:text>Type: </xsl:text><xsl:value-of select="@type"/>
</xsl:element>
</xsl:element>
<xsl:for-each select="description/para">
<xsl:apply-templates select="."/>
</xsl:for-each>
</xsl:element>
</xsl:template>
<xsl:template name="cmd">
<xsl:param name="prefix"/>
<xsl:variable name="thiscmd"><xsl:value-of select="$prefix"/><xsl:value-of select="@name"/></xsl:variable>
<xsl:attribute name="id">
<xsl:text>cmd_</xsl:text>
<xsl:value-of select="translate($thiscmd,' ','_')"/>
</xsl:attribute>
<xsl:element name="title"><xsl:value-of select="$thiscmd"/>
<xsl:if test="short-description">
<xsl:text> - </xsl:text>
<xsl:value-of select="short-description"