Changeset 256899f


Ignore:
Timestamp:
2007-11-19T23:16:18Z (13 years ago)
Author:
Wilmer van der Gaast <wilmer@…>
Branches:
master
Children:
7df5a08
Parents:
cd428e4 (diff), ef5c185 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

Merging Jabber groupchat support.

Files:
1 added
25 edited

Legend:

Unmodified
Added
Removed
  • doc/user-guide/commands.xml

    rcd428e4 r256899f  
    685685        </bitlbee-setting>
    686686
     687        <bitlbee-setting name="xmlconsole" type="boolean" scope="account">
     688                <default>false</default>
     689
     690                <description>
     691                        <para>
     692                                The Jabber module allows you to add a buddy <emphasis>xmlconsole</emphasis> to your contact list, which will then show you the raw XMPP stream between you and the server. You can also send XMPP packets to this buddy, which will then be sent to the server.
     693                        </para>
     694                        <para>
     695                                If you want to enable this XML console permanently (and at login time already), you can set this setting.
     696                        </para>
     697                </description>
     698        </bitlbee-setting>
     699
    687700        <bitlbee-command name="rename">
    688701                <short-description>Rename (renick) a buddy</short-description>
  • doc/user-guide/misc.xml

    rcd428e4 r256899f  
    8181
    8282<para>
     83Some protocols (like Jabber) also support named groupchats. BitlBee now supports these too. You can use the <emphasis>join_chat</emphasis> command to join them. See <emphasis>help join_chat</emphasis> for more information.
     84</para>
     85
     86<para>
    8387This is all you'll probably need to know. If you have any problems, please read <emphasis>help groupchats3</emphasis>.
    8488</para>
  • irc.c

    rcd428e4 r256899f  
    641641                }
    642642        }
    643         else if( ( c = chat_by_channel( channel ) ) )
     643        else if( ( c = irc_chat_by_channel( irc, channel ) ) )
    644644        {
    645645                GList *l;
     
    788788void irc_topic( irc_t *irc, char *channel )
    789789{
    790         if( g_strcasecmp( channel, irc->channel ) == 0 )
    791         {
     790        struct groupchat *c = irc_chat_by_channel( irc, channel );
     791       
     792        if( c && c->topic )
     793                irc_reply( irc, 332, "%s :%s", channel, c->topic );
     794        else if( g_strcasecmp( channel, irc->channel ) == 0 )
    792795                irc_reply( irc, 332, "%s :%s", channel, CONTROL_TOPIC );
    793         }
    794796        else
    795         {
    796                 struct groupchat *c = chat_by_channel( channel );
    797                
    798                 if( c )
    799                         irc_reply( irc, 332, "%s :BitlBee groupchat: \"%s\". Please keep in mind that root-commands won't work here. Have fun!", channel, c->title );
    800                 else
    801                         irc_reply( irc, 331, "%s :No topic for this channel", channel );
    802         }
     797                irc_reply( irc, 331, "%s :No topic for this channel", channel );
    803798}
    804799
     
    932927        if( *nick == '#' || *nick == '&' )
    933928        {
    934                 if( !( c = chat_by_channel( nick ) ) )
     929                if( !( c = irc_chat_by_channel( irc, nick ) ) )
    935930                {
    936931                        irc_reply( irc, 403, "%s :Channel does not exist", nick );
     
    11981193        return TRUE;
    11991194}
     1195
     1196struct groupchat *irc_chat_by_channel( irc_t *irc, char *channel )
     1197{
     1198        struct groupchat *c;
     1199        account_t *a;
     1200       
     1201        /* This finds the connection which has a conversation which belongs to this channel */
     1202        for( a = irc->accounts; a; a = a->next )
     1203        {
     1204                if( a->ic == NULL )
     1205                        continue;
     1206               
     1207                c = a->ic->groupchats;
     1208                while( c )
     1209                {
     1210                        if( c->channel && g_strcasecmp( c->channel, channel ) == 0 )
     1211                                return c;
     1212                       
     1213                        c = c->next;
     1214                }
     1215        }
     1216       
     1217        return NULL;
     1218}
  • irc.h

    rcd428e4 r256899f  
    140140
    141141void buddy_send_handler( irc_t *irc, user_t *u, char *msg, int flags );
     142struct groupchat *irc_chat_by_channel( irc_t *irc, char *channel );
    142143
    143144#endif
  • irc_commands.c

    rcd428e4 r256899f  
    144144                irc_join( irc, u, irc->channel );
    145145        }
    146         else if( ( c = chat_by_channel( cmd[1] ) ) )
     146        else if( ( c = irc_chat_by_channel( irc, cmd[1] ) ) )
    147147        {
    148148                user_t *u = user_find( irc, irc->nick );
     
    201201{
    202202        char *nick = cmd[1], *channel = cmd[2];
    203         struct groupchat *c = chat_by_channel( channel );
     203        struct groupchat *c = irc_chat_by_channel( irc, channel );
    204204        user_t *u = user_find( irc, nick );
    205205       
     
    287287                        u = u->next;
    288288                }
    289         else if( ( c = chat_by_channel( channel ) ) )
     289        else if( ( c = irc_chat_by_channel( irc, channel ) ) )
    290290                for( l = c->in_room; l; l = l->next )
    291291                {
     
    421421static void irc_cmd_topic( irc_t *irc, char **cmd )
    422422{
    423         if( cmd[2] )
    424                 irc_reply( irc, 482, "%s :Cannot change topic", cmd[1] );
    425         else
    426                 irc_topic( irc, cmd[1] );
     423        char *channel = cmd[1];
     424        char *topic = cmd[2];
     425       
     426        if( topic )
     427        {
     428                /* Send the topic */
     429                struct groupchat *c = irc_chat_by_channel( irc, channel );
     430                if( c && c->ic && c->ic->acc->prpl->chat_topic )
     431                        c->ic->acc->prpl->chat_topic( c, topic );
     432        }
     433        else
     434        {
     435                /* Get the topic */
     436                irc_topic( irc, channel );
     437        }
    427438}
    428439
  • nick.c

    rcd428e4 r256899f  
    5757        static char nick[MAX_NICK_LENGTH+1];
    5858        char *store_handle, *found_nick;
    59         int inf_protection = 256;
    6059       
    6160        memset( nick, 0, MAX_NICK_LENGTH + 1 );
     
    8281        }
    8382        g_free( store_handle );
     83       
     84        /* Make sure the nick doesn't collide with an existing one by adding
     85           underscores and that kind of stuff, if necessary. */
     86        nick_dedupe( acc, handle, nick );
     87       
     88        return nick;
     89}
     90
     91void nick_dedupe( account_t *acc, const char *handle, char nick[MAX_NICK_LENGTH+1] )
     92{
     93        int inf_protection = 256;
    8494       
    8595        /* Now, find out if the nick is already in use at the moment, and make
     
    120130                }
    121131        }
    122        
    123         return nick;
    124132}
    125133
  • nick.h

    rcd428e4 r256899f  
    2626void nick_set( account_t *acc, const char *handle, const char *nick );
    2727char *nick_get( account_t *acc, const char *handle );
     28void nick_dedupe( account_t *acc, const char *handle, char nick[MAX_NICK_LENGTH+1] );
    2829int nick_saved( account_t *acc, const char *handle );
    2930void nick_del( account_t *acc, const char *handle );
  • protocols/jabber/Makefile

    rcd428e4 r256899f  
    1010
    1111# [SH] Program variables
    12 objects = io.o iq.o jabber.o jabber_util.o message.o presence.o sasl.o xmltree.o
     12objects = conference.o io.o iq.o jabber.o jabber_util.o message.o presence.o sasl.o xmltree.o
    1313
    1414CFLAGS += -Wall
  • protocols/jabber/io.c

    rcd428e4 r256899f  
    4444        struct jabber_data *jd = ic->proto_data;
    4545        gboolean ret;
     46       
     47        if( jd->flags & JFLAG_XMLCONSOLE )
     48        {
     49                char *msg;
     50               
     51                msg = g_strdup_printf( "TX: %s", buf );
     52                imcb_buddy_msg( ic, JABBER_XMLCONSOLE_HANDLE, msg, 0, 0 );
     53                g_free( msg );
     54        }
    4655       
    4756        if( jd->tx_len == 0 )
     
    427436{
    428437        struct im_connection *ic = data;
    429         struct xt_node *c;
    430         char *s, *type = NULL, *text = NULL;
    431438        int allow_reconnect = TRUE;
    432        
    433         for( c = node->children; c; c = c->next )
    434         {
    435                 if( !( s = xt_find_attr( c, "xmlns" ) ) ||
    436                     strcmp( s, XMLNS_STREAM_ERROR ) != 0 )
    437                         continue;
    438                
    439                 if( strcmp( c->name, "text" ) != 0 )
    440                 {
    441                         type = c->name;
    442                 }
    443                 /* Only use the text if it doesn't have an xml:lang attribute,
    444                    if it's empty or if it's set to something English. */
    445                 else if( !( s = xt_find_attr( c, "xml:lang" ) ) ||
    446                          !*s || strncmp( s, "en", 2 ) == 0 )
    447                 {
    448                         text = c->text;
    449                 }
    450         }
     439        struct jabber_error *err;
     440       
     441        err = jabber_error_parse( node, XMLNS_STREAM_ERROR );
    451442       
    452443        /* Tssk... */
    453         if( type == NULL )
     444        if( err->code == NULL )
    454445        {
    455446                imcb_error( ic, "Unknown stream error reported by server" );
    456447                imc_logout( ic, allow_reconnect );
     448                jabber_error_free( err );
    457449                return XT_ABORT;
    458450        }
     
    461453           should turn off auto-reconnect to make sure we won't get some nasty
    462454           infinite loop! */
    463         if( strcmp( type, "conflict" ) == 0 )
     455        if( strcmp( err->code, "conflict" ) == 0 )
    464456        {
    465457                imcb_error( ic, "Account and resource used from a different location" );
     
    468460        else
    469461        {
    470                 imcb_error( ic, "Stream error: %s%s%s", type, text ? ": " : "", text ? text : "" );
    471         }
    472        
     462                imcb_error( ic, "Stream error: %s%s%s", err->code, err->text ? ": " : "",
     463                            err->text ? err->text : "" );
     464        }
     465       
     466        jabber_error_free( err );
    473467        imc_logout( ic, allow_reconnect );
    474468       
     
    476470}
    477471
     472static xt_status jabber_xmlconsole( struct xt_node *node, gpointer data )
     473{
     474        struct im_connection *ic = data;
     475        struct jabber_data *jd = ic->proto_data;
     476       
     477        if( jd->flags & JFLAG_XMLCONSOLE )
     478        {
     479                char *msg, *pkt;
     480               
     481                pkt = xt_to_string( node );
     482                msg = g_strdup_printf( "RX: %s", pkt );
     483                imcb_buddy_msg( ic, JABBER_XMLCONSOLE_HANDLE, msg, 0, 0 );
     484                g_free( msg );
     485                g_free( pkt );
     486        }
     487       
     488        return XT_NEXT;
     489}
     490
    478491static const struct xt_handler_entry jabber_handlers[] = {
     492        { NULL,                 "stream:stream",        jabber_xmlconsole },
    479493        { "stream:stream",      "<root>",               jabber_end_of_stream },
    480494        { "message",            "stream:stream",        jabber_pkt_message },
  • protocols/jabber/iq.c

    rcd428e4 r256899f  
    9999                else if( strcmp( s, XMLNS_DISCOVER ) == 0 )
    100100                {
     101                        const char *features[] = { XMLNS_VERSION,
     102                                                   XMLNS_TIME,
     103                                                   XMLNS_CHATSTATES,
     104                                                   XMLNS_MUC,
     105                                                   NULL };
     106                        const char **f;
     107                       
    101108                        c = xt_new_node( "identity", NULL, NULL );
    102109                        xt_add_attr( c, "category", "client" );
     
    105112                        xt_add_child( reply, c );
    106113                       
    107                         c = xt_new_node( "feature", NULL, NULL );
    108                         xt_add_attr( c, "var", XMLNS_VERSION );
    109                         xt_add_child( reply, c );
    110                        
    111                         c = xt_new_node( "feature", NULL, NULL );
    112                         xt_add_attr( c, "var", XMLNS_TIME );
    113                         xt_add_child( reply, c );
    114                        
    115                         c = xt_new_node( "feature", NULL, NULL );
    116                         xt_add_attr( c, "var", XMLNS_CHATSTATES );
    117                         xt_add_child( reply, c );
    118                        
    119                         /* Later this can be useful to announce things like
    120                            MUC support. */
     114                        for( f = features; *f; f ++ )
     115                        {
     116                                c = xt_new_node( "feature", NULL, NULL );
     117                                xt_add_attr( c, "var", *f );
     118                                xt_add_child( reply, c );
     119                        }
    121120                }
    122121                else
     
    373372                                                                   group->text : NULL );
    374373                               
    375                                 imcb_rename_buddy( ic, jid, name );
     374                                if( name )
     375                                        imcb_rename_buddy( ic, jid, name );
    376376                        }
    377377                        else if( strcmp( sub, "remove" ) == 0 )
    378378                        {
    379                                 /* Don't have any API call for this yet! So let's
    380                                    just try to handle this as well as we can. */
    381379                                jabber_buddy_remove_bare( ic, jid );
    382                                 imcb_buddy_status( ic, jid, 0, NULL, NULL );
    383                                 /* FIXME! */
     380                                imcb_remove_buddy( ic, jid, NULL );
    384381                        }
    385382                }
  • protocols/jabber/jabber.c

    rcd428e4 r256899f  
    5454       
    5555        s = set_add( &acc->set, "tls", "try", set_eval_tls, acc );
     56        s->flags |= ACC_SET_OFFLINE_ONLY;
     57       
     58        s = set_add( &acc->set, "xmlconsole", "false", set_eval_bool, acc );
    5659        s->flags |= ACC_SET_OFFLINE_ONLY;
    5760}
     
    189192                imc_logout( ic, TRUE );
    190193        }
     194       
     195        if( set_getbool( &acc->set, "xmlconsole" ) )
     196        {
     197                jd->flags |= JFLAG_XMLCONSOLE;
     198                /* Shouldn't really do this at this stage already, maybe. But
     199                   I think this shouldn't break anything. */
     200                imcb_add_buddy( ic, JABBER_XMLCONSOLE_HANDLE, NULL );
     201        }
    191202}
    192203
     
    196207       
    197208        jabber_end_stream( ic );
     209       
     210        while( ic->groupchats )
     211                jabber_chat_free( ic->groupchats );
    198212       
    199213        if( jd->r_inpa >= 0 )
     
    224238        struct jabber_buddy *bud;
    225239        struct xt_node *node;
     240        char *s;
    226241        int st;
    227242       
    228         bud = jabber_buddy_by_jid( ic, who, 0 );
     243        if( g_strcasecmp( who, JABBER_XMLCONSOLE_HANDLE ) == 0 )
     244                return jabber_write( ic, message, strlen( message ) );
     245       
     246        if( ( s = strchr( who, '=' ) ) && jabber_chat_by_name( ic, s + 1 ) )
     247                bud = jabber_buddy_by_ext_jid( ic, who, 0 );
     248        else
     249                bud = jabber_buddy_by_jid( ic, who, 0 );
    229250       
    230251        node = xt_new_node( "body", message, NULL );
     
    311332static void jabber_add_buddy( struct im_connection *ic, char *who, char *group )
    312333{
     334        struct jabber_data *jd = ic->proto_data;
     335       
     336        if( g_strcasecmp( who, JABBER_XMLCONSOLE_HANDLE ) == 0 )
     337        {
     338                jd->flags |= JFLAG_XMLCONSOLE;
     339                imcb_add_buddy( ic, JABBER_XMLCONSOLE_HANDLE, NULL );
     340                return;
     341        }
     342       
    313343        if( jabber_add_to_roster( ic, who, NULL ) )
    314344                presence_send_request( ic, who, "subscribe" );
     
    317347static void jabber_remove_buddy( struct im_connection *ic, char *who, char *group )
    318348{
     349        struct jabber_data *jd = ic->proto_data;
     350       
     351        if( g_strcasecmp( who, JABBER_XMLCONSOLE_HANDLE ) == 0 )
     352        {
     353                jd->flags &= ~JFLAG_XMLCONSOLE;
     354                /* Not necessary for now. And for now the code isn't too
     355                   happy if the buddy is completely gone right after calling
     356                   this function already.
     357                imcb_remove_buddy( ic, JABBER_XMLCONSOLE_HANDLE, NULL );
     358                */
     359                return;
     360        }
     361       
    319362        /* We should always do this part. Clean up our administration a little bit. */
    320363        jabber_buddy_remove_bare( ic, who );
     
    322365        if( jabber_remove_from_roster( ic, who ) )
    323366                presence_send_request( ic, who, "unsubscribe" );
     367}
     368
     369static struct groupchat *jabber_chat_join_( struct im_connection *ic, char *room, char *nick, char *password )
     370{
     371        if( strchr( room, '@' ) == NULL )
     372                imcb_error( ic, "Invalid room name: %s", room );
     373        else if( jabber_chat_by_name( ic, room ) )
     374                imcb_error( ic, "Already present in chat `%s'", room );
     375        else
     376                return jabber_chat_join( ic, room, nick, password );
     377       
     378        return NULL;
     379}
     380
     381static void jabber_chat_msg_( struct groupchat *c, char *message, int flags )
     382{
     383        if( c && message )
     384                jabber_chat_msg( c, message, flags );
     385}
     386
     387static void jabber_chat_topic_( struct groupchat *c, char *topic )
     388{
     389        if( c && topic )
     390                jabber_chat_topic( c, topic );
     391}
     392
     393static void jabber_chat_leave_( struct groupchat *c )
     394{
     395        if( c )
     396                jabber_chat_leave( c, NULL );
    324397}
    325398
     
    388461        ret->buddy_msg = jabber_buddy_msg;
    389462        ret->away_states = jabber_away_states;
    390 //      ret->get_status_string = jabber_get_status_string;
    391463        ret->set_away = jabber_set_away;
    392464//      ret->set_info = jabber_set_info;
     
    394466        ret->add_buddy = jabber_add_buddy;
    395467        ret->remove_buddy = jabber_remove_buddy;
    396 //      ret->chat_msg = jabber_chat_msg;
     468        ret->chat_msg = jabber_chat_msg_;
     469        ret->chat_topic = jabber_chat_topic_;
    397470//      ret->chat_invite = jabber_chat_invite;
    398 //      ret->chat_leave = jabber_chat_leave;
    399 //      ret->chat_open = jabber_chat_open;
     471        ret->chat_leave = jabber_chat_leave_;
     472        ret->chat_join = jabber_chat_join_;
    400473        ret->keepalive = jabber_keepalive;
    401474        ret->send_typing = jabber_send_typing;
  • protocols/jabber/jabber.h

    rcd428e4 r256899f  
    3232typedef enum
    3333{
    34         JFLAG_STREAM_STARTED = 1,       /* Set when we detected the beginning of the stream
     34        JFLAG_STREAM_STARTED = 1,       /* Set when we detected the beginning of the stream
    3535                                           and want to do auth. */
    36         JFLAG_AUTHENTICATED = 2,        /* Set when we're successfully authenticatd. */
    37         JFLAG_STREAM_RESTART = 4,       /* Set when we want to restart the stream (after
     36        JFLAG_AUTHENTICATED = 2,        /* Set when we're successfully authenticatd. */
     37        JFLAG_STREAM_RESTART = 4,       /* Set when we want to restart the stream (after
    3838                                           SASL or TLS). */
    39         JFLAG_WAIT_SESSION = 8,         /* Set if we sent a <session> tag and need a reply
     39        JFLAG_WAIT_SESSION = 8,         /* Set if we sent a <session> tag and need a reply
    4040                                           before we continue. */
    41         JFLAG_WAIT_BIND = 16,           /* ... for <bind> tag. */
    42         JFLAG_WANT_TYPING = 32,         /* Set if we ever sent a typing notification, this
     41        JFLAG_WAIT_BIND = 16,           /* ... for <bind> tag. */
     42        JFLAG_WANT_TYPING = 32,         /* Set if we ever sent a typing notification, this
    4343                                           activates all XEP-85 related code. */
     44        JFLAG_XMLCONSOLE = 64,          /* If the user added an xmlconsole buddy. */
    4445} jabber_flags_t;
    4546
     
    5051        JBFLAG_DOES_XEP85 = 2,          /* Set this when the resource seems to support
    5152                                           XEP85 (typing notification shite). */
     53        JBFLAG_IS_CHATROOM = 4,         /* It's convenient to use this JID thingy for
     54                                           groupchat state info too. */
     55        JBFLAG_IS_ANONYMOUS = 8,        /* For anonymous chatrooms, when we don't have
     56                                           have a real JID. */
    5257} jabber_buddy_flags_t;
    53 
    54 #define JABBER_PORT_DEFAULT "5222"
    55 #define JABBER_PORT_MIN 5220
    56 #define JABBER_PORT_MAX 5229
    5758
    5859struct jabber_data
     
    101102        char *resource;
    102103       
     104        char *ext_jid; /* The JID to use in BitlBee. The real JID if possible, */
     105                       /* otherwise something similar to the conference JID. */
     106       
    103107        int priority;
    104108        struct jabber_away_state *away_state;
     
    110114        struct jabber_buddy *next;
    111115};
     116
     117struct jabber_chat
     118{
     119        int flags;
     120        char *name;
     121        char *my_full_jid; /* Separate copy because of case sensitivity. */
     122        struct jabber_buddy *me;
     123};
     124
     125#define JABBER_XMLCONSOLE_HANDLE "xmlconsole"
     126
     127#define JABBER_PORT_DEFAULT "5222"
     128#define JABBER_PORT_MIN 5220
     129#define JABBER_PORT_MAX 5229
    112130
    113131/* Prefixes to use for packet IDs (mainly for IQ packets ATM). Usually the
     
    132150#define XMLNS_TIME         "jabber:iq:time"                     /* XEP-0090 */
    133151#define XMLNS_VCARD        "vcard-temp"                         /* XEP-0054 */
     152#define XMLNS_DELAY        "jabber:x:delay"                     /* XEP-0091 */
    134153#define XMLNS_CHATSTATES   "http://jabber.org/protocol/chatstates"  /* 0085 */
    135154#define XMLNS_DISCOVER     "http://jabber.org/protocol/disco#info"  /* 0030 */
     155#define XMLNS_MUC          "http://jabber.org/protocol/muc"     /* XEP-0045 */
     156#define XMLNS_MUC_USER     "http://jabber.org/protocol/muc#user"/* XEP-0045 */
    136157
    137158/* iq.c */
     
    164185const struct jabber_away_state *jabber_away_state_by_name( char *name );
    165186void jabber_buddy_ask( struct im_connection *ic, char *handle );
    166 char *jabber_normalize( char *orig );
     187char *jabber_normalize( const char *orig );
    167188
    168189typedef enum
    169190{
    170191        GET_BUDDY_CREAT = 1,    /* Try to create it, if necessary. */
    171         GET_BUDDY_EXACT = 2,    /* Get an exact message (only makes sense with bare JIDs). */
     192        GET_BUDDY_EXACT = 2,    /* Get an exact match (only makes sense with bare JIDs). */
     193        GET_BUDDY_FIRST = 4,    /* No selection, simply get the first resource for this JID. */
    172194} get_buddy_flags_t;
     195
     196struct jabber_error
     197{
     198        char *code, *text, *type;
     199};
    173200
    174201struct jabber_buddy *jabber_buddy_add( struct im_connection *ic, char *full_jid );
    175202struct jabber_buddy *jabber_buddy_by_jid( struct im_connection *ic, char *jid, get_buddy_flags_t flags );
     203struct jabber_buddy *jabber_buddy_by_ext_jid( struct im_connection *ic, char *jid, get_buddy_flags_t flags );
    176204int jabber_buddy_remove( struct im_connection *ic, char *full_jid );
    177205int jabber_buddy_remove_bare( struct im_connection *ic, char *bare_jid );
     206struct groupchat *jabber_chat_by_name( struct im_connection *ic, const char *name );
     207time_t jabber_get_timestamp( struct xt_node *xt );
     208struct jabber_error *jabber_error_parse( struct xt_node *node, char *xmlns );
     209void jabber_error_free( struct jabber_error *err );
    178210
    179211extern const struct jabber_away_state jabber_away_state_list[];
     
    193225gboolean sasl_supported( struct im_connection *ic );
    194226
     227/* conference.c */
     228struct groupchat *jabber_chat_join( struct im_connection *ic, char *room, char *nick, char *password );
     229void jabber_chat_free( struct groupchat *c );
     230int jabber_chat_msg( struct groupchat *ic, char *message, int flags );
     231int jabber_chat_topic( struct groupchat *c, char *topic );
     232int jabber_chat_leave( struct groupchat *c, const char *reason );
     233void jabber_chat_pkt_presence( struct im_connection *ic, struct jabber_buddy *bud, struct xt_node *node );
     234void jabber_chat_pkt_message( struct im_connection *ic, struct jabber_buddy *bud, struct xt_node *node );
     235
    195236#endif
  • protocols/jabber/jabber_util.c

    rcd428e4 r256899f  
    4848                   call p_s_u() now to send the new prio setting, it would
    4949                   send the old setting because the set->value gets changed
    50                    when the eval returns a non-NULL value.
     50                   after the (this) eval returns a non-NULL value.
    5151                   
    5252                   So now I can choose between implementing post-set
     
    129129/* Cache a node/packet for later use. Mainly useful for IQ packets if you need
    130130   them when you receive the response. Use this BEFORE sending the packet so
    131    it'll get a new id= tag, and do NOT free() the packet after writing it! */
     131   it'll get a new id= tag, and do NOT free() the packet after sending it! */
    132132void jabber_cache_add( struct im_connection *ic, struct xt_node *node, jabber_cache_event func )
    133133{
     
    252252
    253253/* Returns a new string. Don't leak it! */
    254 char *jabber_normalize( char *orig )
     254char *jabber_normalize( const char *orig )
    255255{
    256256        int len, i;
     
    320320        else
    321321        {
     322                /* Keep in mind that full_jid currently isn't really
     323                   a full JID... */
    322324                new->bare_jid = g_strdup( full_jid );
    323325                g_hash_table_insert( jd->buddies, new->bare_jid, new );
     
    333335        {
    334336                /* Let's waste some more bytes of RAM instead of to make
    335                    memory management a total disaster here.. */
     337                   memory management a total disaster here. And it saves
     338                   me one g_free() call in this function. :-P */
    336339                new->full_jid = full_jid;
    337340        }
     
    353356        if( ( s = strchr( jid, '/' ) ) )
    354357        {
     358                int none_found = 0;
     359               
    355360                *s = 0;
    356361                if( ( bud = g_hash_table_lookup( jd->buddies, jid ) ) )
     
    370375                        }
    371376                }
    372                
    373                 if( bud == NULL && ( flags & GET_BUDDY_CREAT ) && imcb_find_buddy( ic, jid ) )
     377                else
     378                {
     379                        /* This hack is there to make sure that O_CREAT will
     380                           work if there's already another resouce present
     381                           for this JID, even if it's an unknown buddy. This
     382                           is done to handle conferences properly. */
     383                        none_found = 1;
     384                }
     385               
     386                if( bud == NULL && ( flags & GET_BUDDY_CREAT ) && ( imcb_find_buddy( ic, jid ) || !none_found ) )
    374387                {
    375388                        *s = '/';
     
    418431}
    419432
     433/* I'm keeping a separate ext_jid attribute to save a JID that makes sense
     434   to export to BitlBee. This is mainly for groupchats right now. It's
     435   a bit of a hack, but I just think having the user nickname in the hostname
     436   part of the hostmask doesn't look nice on IRC. Normally you can convert
     437   a normal JID to ext_jid by swapping the part before and after the / and
     438   replacing the / with a =. But there should be some stripping (@s are
     439   allowed in Jabber nicks...). */
     440struct jabber_buddy *jabber_buddy_by_ext_jid( struct im_connection *ic, char *jid_, get_buddy_flags_t flags )
     441{
     442        struct jabber_buddy *bud;
     443        char *s, *jid;
     444       
     445        jid = jabber_normalize( jid_ );
     446       
     447        if( ( s = strchr( jid, '=' ) ) == NULL )
     448                return NULL;
     449       
     450        for( bud = jabber_buddy_by_jid( ic, s + 1, GET_BUDDY_FIRST ); bud; bud = bud->next )
     451        {
     452                /* Hmmm, could happen if not all people in the chat are anonymized? */
     453                if( bud->ext_jid == NULL )
     454                        continue;
     455               
     456                if( strcmp( bud->ext_jid, jid ) == 0 )
     457                        break;
     458        }
     459       
     460        g_free( jid );
     461       
     462        return bud;
     463}
     464
    420465/* Remove one specific full JID from our list. Use this when a buddy goes
    421466   off-line (because (s)he can still be online from a different location.
     
    441486                        g_hash_table_remove( jd->buddies, bud->bare_jid );
    442487                        g_free( bud->bare_jid );
     488                        g_free( bud->ext_jid );
    443489                        g_free( bud->full_jid );
    444490                        g_free( bud->away_message );
     
    473519                                        g_hash_table_replace( jd->buddies, bi->bare_jid, bi->next );
    474520                               
     521                                g_free( bi->ext_jid );
    475522                                g_free( bi->full_jid );
    476523                                g_free( bi->away_message );
     
    495542   specified bare JID. Use this when removing someone from the contact
    496543   list, for example. */
    497 int jabber_buddy_remove_bare( struct im_connection *ic, char *bare_jid_ )
     544int jabber_buddy_remove_bare( struct im_connection *ic, char *bare_jid )
    498545{
    499546        struct jabber_data *jd = ic->proto_data;
    500547        struct jabber_buddy *bud, *next;
    501         char *bare_jid;
    502        
    503         if( strchr( bare_jid_, '/' ) )
     548       
     549        if( strchr( bare_jid, '/' ) )
    504550                return 0;
    505551       
    506         bare_jid = jabber_normalize( bare_jid_ );
    507        
    508         if( ( bud = g_hash_table_lookup( jd->buddies, bare_jid ) ) )
     552        if( ( bud = jabber_buddy_by_jid( ic, bare_jid, GET_BUDDY_FIRST ) ) )
    509553        {
    510554                /* Most important: Remove the hash reference. We don't know
    511555                   this buddy anymore. */
    512556                g_hash_table_remove( jd->buddies, bud->bare_jid );
     557                g_free( bud->bare_jid );
    513558               
    514559                /* Deallocate the linked list of resources. */
    515560                while( bud )
    516561                {
     562                        /* ext_jid && anonymous means that this buddy is
     563                           specific to one groupchat (the one we're
     564                           currently cleaning up) so it can be deleted
     565                           completely. */
     566                        if( bud->ext_jid && bud->flags & JBFLAG_IS_ANONYMOUS )
     567                                imcb_remove_buddy( ic, bud->ext_jid, NULL );
     568                       
    517569                        next = bud->next;
     570                        g_free( bud->ext_jid );
    518571                        g_free( bud->full_jid );
    519572                        g_free( bud->away_message );
     
    522575                }
    523576               
    524                 g_free( bare_jid );
    525577                return 1;
    526578        }
    527579        else
    528580        {
    529                 g_free( bare_jid );
    530581                return 0;
    531582        }
    532583}
     584
     585struct groupchat *jabber_chat_by_name( struct im_connection *ic, const char *name )
     586{
     587        char *normalized = jabber_normalize( name );
     588        struct groupchat *ret;
     589        struct jabber_chat *jc;
     590       
     591        for( ret = ic->groupchats; ret; ret = ret->next )
     592        {
     593                jc = ret->data;
     594                if( strcmp( normalized, jc->name ) == 0 )
     595                        break;
     596        }
     597        g_free( normalized );
     598       
     599        return ret;
     600}
     601
     602time_t jabber_get_timestamp( struct xt_node *xt )
     603{
     604        struct tm tp, utc;
     605        struct xt_node *c;
     606        time_t res, tres;
     607        char *s = NULL;
     608       
     609        for( c = xt->children; ( c = xt_find_node( c, "x" ) ); c = c->next )
     610        {
     611                if( ( s = xt_find_attr( c, "xmlns" ) ) && strcmp( s, XMLNS_DELAY ) == 0 )
     612                        break;
     613        }
     614       
     615        if( !c || !( s = xt_find_attr( c, "stamp" ) ) )
     616                return 0;
     617       
     618        memset( &tp, 0, sizeof( tp ) );
     619        if( sscanf( s, "%4d%2d%2dT%2d:%2d:%2d", &tp.tm_year, &tp.tm_mon, &tp.tm_mday,
     620                                                &tp.tm_hour, &tp.tm_min, &tp.tm_sec ) != 6 )
     621                return 0;
     622       
     623        tp.tm_year -= 1900;
     624        tp.tm_mon --;
     625        tp.tm_isdst = -1; /* GRRRRRRRRRRR */
     626       
     627        res = mktime( &tp );
     628        /* Problem is, mktime() just gave us the GMT timestamp for the
     629           given local time... While the given time WAS NOT local. So
     630           we should fix this now.
     631       
     632           Now I could choose between messing with environment variables
     633           (kludgy) or using timegm() (not portable)... Or doing the
     634           following, which I actually prefer... */
     635        gmtime_r( &res, &utc );
     636        utc.tm_isdst = -1; /* Once more: GRRRRRRRRRRRRRRRRRR!!! */
     637        if( utc.tm_hour == tp.tm_hour && utc.tm_min == tp.tm_min )
     638                /* Sweet! We're in UTC right now... */
     639                return res;
     640       
     641        tres = mktime( &utc );
     642        res += res - tres;
     643       
     644        /* Yes, this is a hack. And it will go wrong around DST changes.
     645           BUT this is more likely to be threadsafe than messing with
     646           environment variables, and possibly more portable... */
     647       
     648        return res;
     649}
     650
     651struct jabber_error *jabber_error_parse( struct xt_node *node, char *xmlns )
     652{
     653        struct jabber_error *err = g_new0( struct jabber_error, 1 );
     654        struct xt_node *c;
     655        char *s;
     656       
     657        err->type = xt_find_attr( node, "type" );
     658       
     659        for( c = node->children; c; c = c->next )
     660        {
     661                if( !( s = xt_find_attr( c, "xmlns" ) ) ||
     662                    strcmp( s, xmlns ) != 0 )
     663                        continue;
     664               
     665                if( strcmp( c->name, "text" ) != 0 )
     666                {
     667                        err->code = c->name;
     668                }
     669                /* Only use the text if it doesn't have an xml:lang attribute,
     670                   if it's empty or if it's set to something English. */
     671                else if( !( s = xt_find_attr( c, "xml:lang" ) ) ||
     672                         !*s || strncmp( s, "en", 2 ) == 0 )
     673                {
     674                        err->text = c->text;
     675                }
     676        }
     677       
     678        return err;
     679}
     680
     681void jabber_error_free( struct jabber_error *err )
     682{
     683        g_free( err );
     684}
  • protocols/jabber/message.c

    rcd428e4 r256899f  
    3030        char *type = xt_find_attr( node, "type" );
    3131        struct xt_node *body = xt_find_node( node->children, "body" ), *c;
     32        struct jabber_buddy *bud = NULL;
    3233        char *s;
     34       
     35        if( !from )
     36                return XT_HANDLED; /* Consider this packet corrupted. */
     37       
     38        bud = jabber_buddy_by_jid( ic, from, GET_BUDDY_EXACT );
    3339       
    3440        if( type && strcmp( type, "error" ) == 0 )
     
    3642                /* Handle type=error packet. */
    3743        }
    38         else if( type && strcmp( type, "groupchat" ) == 0 )
     44        else if( type && from && strcmp( type, "groupchat" ) == 0 )
    3945        {
    40                 /* TODO! */
     46                jabber_chat_pkt_message( ic, bud, node );
    4147        }
    4248        else /* "chat", "normal", "headline", no-type or whatever. Should all be pretty similar. */
    4349        {
    44                 struct jabber_buddy *bud = NULL;
    4550                GString *fullmsg = g_string_new( "" );
    4651               
    4752                if( ( s = strchr( from, '/' ) ) )
    4853                {
    49                         if( ( bud = jabber_buddy_by_jid( ic, from, GET_BUDDY_EXACT ) ) )
     54                        if( bud )
     55                        {
    5056                                bud->last_act = time( NULL );
     57                                from = bud->ext_jid ? : bud->bare_jid;
     58                        }
    5159                        else
    5260                                *s = 0; /* We need to generate a bare JID now. */
     
    7684               
    7785                if( fullmsg->len > 0 )
    78                         imcb_buddy_msg( ic, bud ? bud->bare_jid : from, fullmsg->str, 0, 0 );
     86                        imcb_buddy_msg( ic, from, fullmsg->str,
     87                                        0, jabber_get_timestamp( node ) );
    7988               
    8089                g_string_free( fullmsg, TRUE );
    8190               
    8291                /* Handling of incoming typing notifications. */
    83                 if( xt_find_node( node->children, "composing" ) )
     92                if( bud == NULL )
     93                {
     94                        /* Can't handle these for unknown buddies. */
     95                }
     96                else if( xt_find_node( node->children, "composing" ) )
    8497                {
    8598                        bud->flags |= JBFLAG_DOES_XEP85;
    86                         imcb_buddy_typing( ic, bud ? bud->bare_jid : from, OPT_TYPING );
     99                        imcb_buddy_typing( ic, from, OPT_TYPING );
    87100                }
    88101                /* No need to send a "stopped typing" signal when there's a message. */
     
    90103                {
    91104                        bud->flags |= JBFLAG_DOES_XEP85;
    92                         imcb_buddy_typing( ic, bud ? bud->bare_jid : from, 0 );
     105                        imcb_buddy_typing( ic, from, 0 );
    93106                }
    94107                else if( xt_find_node( node->children, "paused" ) )
    95108                {
    96109                        bud->flags |= JBFLAG_DOES_XEP85;
    97                         imcb_buddy_typing( ic, bud ? bud->bare_jid : from, OPT_THINKING );
     110                        imcb_buddy_typing( ic, from, OPT_THINKING );
    98111                }
    99112               
  • protocols/jabber/presence.c

    rcd428e4 r256899f  
    3131        struct xt_node *c;
    3232        struct jabber_buddy *bud;
     33        int is_chat = 0, is_away = 0;
    3334        char *s;
    3435       
     
    3637                return XT_HANDLED;
    3738       
     39        if( ( s = strchr( from, '/' ) ) )
     40        {
     41                *s = 0;
     42                if( jabber_chat_by_name( ic, from ) )
     43                        is_chat = 1;
     44                *s = '/';
     45        }
     46       
    3847        if( type == NULL )
    3948        {
    40                 int is_away = 0;
    41                
    4249                if( !( bud = jabber_buddy_by_jid( ic, from, GET_BUDDY_EXACT | GET_BUDDY_CREAT ) ) )
    4350                {
     
    7279                        bud->priority = 0;
    7380               
    74                 if( bud == jabber_buddy_by_jid( ic, bud->bare_jid, 0 ) )
     81                if( is_chat )
     82                        jabber_chat_pkt_presence( ic, bud, node );
     83                else if( bud == jabber_buddy_by_jid( ic, bud->bare_jid, 0 ) )
    7584                        imcb_buddy_status( ic, bud->bare_jid, OPT_LOGGED_IN | is_away,
    7685                                           ( is_away && bud->away_state ) ? bud->away_state->full_name : NULL,
     
    7988        else if( strcmp( type, "unavailable" ) == 0 )
    8089        {
    81                 if( jabber_buddy_by_jid( ic, from, GET_BUDDY_EXACT ) == NULL )
     90                if( ( bud = jabber_buddy_by_jid( ic, from, GET_BUDDY_EXACT ) ) == NULL )
    8291                {
    8392                        if( set_getbool( &ic->irc->set, "debug" ) )
     
    8695                }
    8796               
     97                /* Handle this before we delete the JID. */
     98                if( is_chat )
     99                {
     100                        jabber_chat_pkt_presence( ic, bud, node );
     101                }
     102               
    88103                jabber_buddy_remove( ic, from );
    89104               
    90                 if( ( s = strchr( from, '/' ) ) )
     105                if( is_chat )
     106                {
     107                        /* Nothing else to do for now? */
     108                }
     109                else if( ( s = strchr( from, '/' ) ) )
    91110                {
    92111                        *s = 0;
    93112               
    94                         /* Only count this as offline if there's no other resource
    95                            available anymore. */
    96                         if( jabber_buddy_by_jid( ic, from, 0 ) == NULL )
     113                        /* If another resource is still available, send its presence
     114                           information. */
     115                        if( ( bud = jabber_buddy_by_jid( ic, from, 0 ) ) )
     116                        {
     117                                if( bud->away_state && ( *bud->away_state->code == 0 ||
     118                                    strcmp( bud->away_state->code, "chat" ) == 0 ) )
     119                                        is_away = OPT_AWAY;
     120                               
     121                                imcb_buddy_status( ic, bud->bare_jid, OPT_LOGGED_IN | is_away,
     122                                                   ( is_away && bud->away_state ) ? bud->away_state->full_name : NULL,
     123                                                   bud->away_message );
     124                        }
     125                        else
     126                        {
     127                                /* Otherwise, count him/her as offline now. */
    97128                                imcb_buddy_status( ic, from, 0, NULL, NULL );
     129                        }
    98130                       
    99131                        *s = '/';
     
    126158        else if( strcmp( type, "error" ) == 0 )
    127159        {
    128                 /* What to do with it? */
     160                struct jabber_error *err;
     161               
     162                if( ( c = xt_find_node( node->children, "error" ) ) )
     163                {
     164                        err = jabber_error_parse( c, XMLNS_STANZA_ERROR );
     165                        imcb_error( ic, "Stanza (%s) error: %s%s%s", node->name,
     166                                    err->code, err->text ? ": " : "",
     167                                    err->text ? err->text : "" );
     168                        jabber_error_free( err );
     169                }
     170                /* What else to do with it? */
    129171        }
    130172       
     
    140182        char *show = jd->away_state->code;
    141183        char *status = jd->away_message;
     184        struct groupchat *c;
    142185        int st;
    143186       
     
    151194        st = jabber_write_packet( ic, node );
    152195       
     196        /* Have to send this update to all groupchats too, the server won't
     197           do this automatically. */
     198        for( c = ic->groupchats; c && st; c = c->next )
     199        {
     200                struct jabber_chat *jc = c->data;
     201               
     202                xt_add_attr( node, "to", jc->my_full_jid );
     203                st = jabber_write_packet( ic, node );
     204        }
     205       
    153206        xt_free_node( node );
    154207        return st;
  • protocols/jabber/sasl.c

    rcd428e4 r256899f  
    332332        struct jabber_data *jd = ic->proto_data;
    333333       
    334         return ( (void*) ( jd->xt && jd->xt->root && xt_find_attr( jd->xt->root, "version" ) ) ) != NULL;
    335 }
     334        return ( jd->xt && jd->xt->root && xt_find_attr( jd->xt->root, "version" ) ) != 0;
     335}
  • protocols/jabber/xmltree.c

    rcd428e4 r256899f  
    444444   list, not the node itself! The reason you have to do this by hand: So
    445445   that you can also use this function as a find-next. */
    446 struct xt_node *xt_find_node( struct xt_node *node, char *name )
     446struct xt_node *xt_find_node( struct xt_node *node, const char *name )
    447447{
    448448        while( node )
     
    457457}
    458458
    459 char *xt_find_attr( struct xt_node *node, char *key )
     459char *xt_find_attr( struct xt_node *node, const char *key )
    460460{
    461461        int i;
     
    526526}
    527527
    528 void xt_add_attr( struct xt_node *node, char *key, char *value )
     528void xt_add_attr( struct xt_node *node, const char *key, const char *value )
    529529{
    530530        int i;
     
    553553}
    554554
    555 int xt_remove_attr( struct xt_node *node, char *key )
     555int xt_remove_attr( struct xt_node *node, const char *key )
    556556{
    557557        int i, last;
  • protocols/jabber/xmltree.h

    rcd428e4 r256899f  
    8787void xt_free_node( struct xt_node *node );
    8888void xt_free( struct xt_parser *xt );
    89 struct xt_node *xt_find_node( struct xt_node *node, char *name );
    90 char *xt_find_attr( struct xt_node *node, char *key );
     89struct xt_node *xt_find_node( struct xt_node *node, const char *name );
     90char *xt_find_attr( struct xt_node *node, const char *key );
    9191
    9292struct xt_node *xt_new_node( char *name, char *text, struct xt_node *children );
    9393void xt_add_child( struct xt_node *parent, struct xt_node *child );
    94 void xt_add_attr( struct xt_node *node, char *key, char *value );
    95 int xt_remove_attr( struct xt_node *node, char *key );
     94void xt_add_attr( struct xt_node *node, const char *key, const char *value );
     95int xt_remove_attr( struct xt_node *node, const char *key );
    9696
    9797#endif
  • protocols/msn/sb.c

    rcd428e4 r256899f  
    232232        if( sb->chat )
    233233        {
    234                 imcb_chat_removed( sb->chat );
     234                imcb_chat_free( sb->chat );
    235235        }
    236236       
  • protocols/nogaim.c

    rcd428e4 r256899f  
    9999}
    100100
    101  
    102101struct prpl *find_protocol(const char *name)
    103102{
     
    289288{
    290289        irc_t *irc = ic->irc;
    291         user_t *t, *u = irc->users;
     290        user_t *t, *u;
    292291        account_t *a;
    293292       
     
    306305        b_event_remove( ic->inpa );
    307306       
     307        u = irc->users;
    308308        while( u )
    309309        {
     
    428428{
    429429        user_t *u = user_findhandle( ic, handle );
    430         char *s, newnick[MAX_NICK_LENGTH+1];
    431430       
    432431        if( !u || !realname ) return;
     
    440439                if( ( ic->flags & OPT_LOGGED_IN ) && set_getbool( &ic->irc->set, "display_namechanges" ) )
    441440                        imcb_log( ic, "User `%s' changed name to `%s'", u->nick, u->realname );
    442                
    443                 if( !u->online && !nick_saved( ic->acc, handle ) )
    444                 {
    445                         /* Detect numeric handles: */
    446                         for( s = u->user; isdigit( *s ); s++ );
     441        }
     442}
     443
     444void imcb_remove_buddy( struct im_connection *ic, char *handle, char *group )
     445{
     446        user_t *u;
     447       
     448        if( ( u = user_findhandle( ic, handle ) ) )
     449                user_del( ic->irc, u->nick );
     450}
     451
     452/* Mainly meant for ICQ (and now also for Jabber conferences) to allow IM
     453   modules to suggest a nickname for a handle. */
     454void imcb_buddy_nick_hint( struct im_connection *ic, char *handle, char *nick )
     455{
     456        user_t *u = user_findhandle( ic, handle );
     457        char newnick[MAX_NICK_LENGTH+1], *orig_nick;
     458       
     459        if( u && !u->online && !nick_saved( ic->acc, handle ) )
     460        {
     461                /* Only do this if the person isn't online yet (which should
     462                   be the case if we just added it) and if the user hasn't
     463                   assigned a nickname to this buddy already. */
     464               
     465                strncpy( newnick, nick, MAX_NICK_LENGTH );
     466                newnick[MAX_NICK_LENGTH] = 0;
     467               
     468                /* Some processing to make sure this string is a valid IRC nickname. */
     469                nick_strip( newnick );
     470                if( set_getbool( &ic->irc->set, "lcnicks" ) )
     471                        nick_lc( newnick );
     472               
     473                if( strcmp( u->nick, newnick ) != 0 )
     474                {
     475                        /* Only do this if newnick is different from the current one.
     476                           If rejoining a channel, maybe we got this nick already
     477                           (and dedupe would only add an underscore. */
     478                        nick_dedupe( ic->acc, handle, newnick );
    447479                       
    448                         if( *s == 0 )
    449                         {
    450                                 /* If we reached the end of the string, it only contained numbers.
    451                                    Seems to be an ICQ# then, so hopefully realname contains
    452                                    something more useful. */
    453                                 strcpy( newnick, realname );
    454                                
    455                                 /* Some processing to make sure this string is a valid IRC nickname. */
    456                                 nick_strip( newnick );
    457                                 if( set_getbool( &ic->irc->set, "lcnicks" ) )
    458                                         nick_lc( newnick );
    459                                
    460                                 u->nick = g_strdup( newnick );
    461                         }
    462                 }
    463         }
    464 }
    465 
     480                        /* u->nick will be freed halfway the process, so it can't be
     481                           passed as an argument. */
     482                        orig_nick = g_strdup( u->nick );
     483                        user_rename( ic->irc, orig_nick, newnick );
     484                        g_free( orig_nick );
     485                }
     486        }
     487}
    466488
    467489/* prpl.c */
     
    554576                u->online = 0;
    555577               
    556                 /* Remove him/her from the conversations to prevent PART messages after he/she QUIT already */
    557                 for( c = ic->conversations; c; c = c->next )
     578                /* Remove him/her from the groupchats to prevent PART messages after he/she QUIT already */
     579                for( c = ic->groupchats; c; c = c->next )
    558580                        remove_chat_buddy_silent( c, handle );
    559581        }
     
    670692}
    671693
    672 void imcb_chat_removed( struct groupchat *c )
     694void imcb_chat_free( struct groupchat *c )
    673695{
    674696        struct im_connection *ic = c->ic;
    675         struct groupchat *l = NULL;
     697        struct groupchat *l;
    676698        GList *ir;
    677699       
     
    693715                }
    694716               
     717                /* Find the previous chat in the linked list. */
     718                for( l = ic->groupchats; l && l->next != c; l = l->next );
     719               
    695720                if( l )
    696721                        l->next = c->next;
    697722                else
    698                         ic->conversations = c->next;
     723                        ic->groupchats = c->next;
    699724               
    700725                for( ir = c->in_room; ir; ir = ir->next )
     
    735760}
    736761
     762void imcb_chat_topic( struct groupchat *c, char *who, char *topic, time_t set_at )
     763{
     764        struct im_connection *ic = c->ic;
     765        user_t *u = NULL;
     766       
     767        if( who == NULL)
     768                u = user_find( ic->irc, ic->irc->mynick );
     769        else if( g_strcasecmp( who, ic->acc->user ) == 0 )
     770                u = user_find( ic->irc, ic->irc->nick );
     771        else
     772                u = user_findhandle( ic, who );
     773       
     774        if( ( g_strcasecmp( set_getstr( &ic->irc->set, "strip_html" ), "always" ) == 0 ) ||
     775            ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->irc->set, "strip_html" ) ) )
     776                strip_html( topic );
     777       
     778        g_free( c->topic );
     779        c->topic = g_strdup( topic );
     780       
     781        if( c->joined && u )
     782                irc_write( ic->irc, ":%s!%s@%s TOPIC %s :%s", u->nick, u->user, u->host, c->channel, topic );
     783}
     784
    737785struct groupchat *imcb_chat_new( struct im_connection *ic, char *handle )
    738786{
     
    741789        /* This one just creates the conversation structure, user won't see anything yet */
    742790       
    743         if( ic->conversations )
    744         {
    745                 for( c = ic->conversations; c->next; c = c->next );
     791        if( ic->groupchats )
     792        {
     793                for( c = ic->groupchats; c->next; c = c->next );
    746794                c = c->next = g_new0( struct groupchat, 1 );
    747795        }
    748796        else
    749                 ic->conversations = c = g_new0( struct groupchat, 1 );
     797                ic->groupchats = c = g_new0( struct groupchat, 1 );
    750798       
    751799        c->ic = ic;
    752800        c->title = g_strdup( handle );
    753801        c->channel = g_strdup_printf( "&chat_%03d", ic->irc->c_id++ );
     802        c->topic = g_strdup_printf( "%s :BitlBee groupchat: \"%s\". Please keep in mind that root-commands won't work here. Have fun!", c->channel, c->title );
    754803       
    755804        if( set_getbool( &ic->irc->set, "debug" ) )
     
    796845}
    797846
     847/* This function is one BIG hack... :-( EREWRITE */
    798848void imcb_chat_remove_buddy( struct groupchat *b, char *handle, char *reason )
    799849{
     
    807857        if( g_strcasecmp( handle, b->ic->acc->user ) == 0 )
    808858        {
     859                if( b->joined == 0 )
     860                        return;
     861               
    809862                u = user_find( b->ic->irc, b->ic->irc->nick );
    810863                b->joined = 0;
     
    816869        }
    817870       
    818         if( remove_chat_buddy_silent( b, handle ) )
    819                 if( ( b->joined || me ) && u )
    820                         irc_part( b->ic->irc, u, b->channel );
     871        if( me || ( remove_chat_buddy_silent( b, handle ) && b->joined && u ) )
     872                irc_part( b->ic->irc, u, b->channel );
    821873}
    822874
     
    844896
    845897/* Misc. BitlBee stuff which shouldn't really be here */
    846 
    847 struct groupchat *chat_by_channel( char *channel )
    848 {
    849         struct im_connection *ic;
    850         struct groupchat *c;
    851         GSList *l;
    852        
    853         /* This finds the connection which has a conversation which belongs to this channel */
    854         for( l = connections; l; l = l->next )
    855         {
    856                 ic = l->data;
    857                 for( c = ic->conversations; c && g_strcasecmp( c->channel, channel ) != 0; c = c->next );
    858                 if( c )
    859                         return c;
    860         }
    861        
    862         return NULL;
    863 }
    864898
    865899char *set_eval_away_devoice( set_t *set, char *value )
     
    10951129        ic->acc->prpl->rem_deny( ic, handle );
    10961130}
     1131
     1132void imcb_clean_handle( struct im_connection *ic, char *handle )
     1133{
     1134        /* Accepts a handle and does whatever is necessary to make it
     1135           BitlBee-friendly. Currently this means removing everything
     1136           outside 33-127 (ASCII printable excl spaces), @ (only one
     1137           is allowed) and ! and : */
     1138        char out[strlen(handle)+1];
     1139        int s, d;
     1140       
     1141        s = d = 0;
     1142        while( handle[s] )
     1143        {
     1144                if( handle[s] > ' ' && handle[s] != '!' && handle[s] != ':' &&
     1145                    ( handle[s] & 0x80 ) == 0 )
     1146                {
     1147                        if( handle[s] == '@' )
     1148                        {
     1149                                /* See if we got an @ already? */
     1150                                out[d] = 0;
     1151                                if( strchr( out, '@' ) )
     1152                                        continue;
     1153                        }
     1154                       
     1155                        out[d++] = handle[s];
     1156                }
     1157                s ++;
     1158        }
     1159        out[d] = handle[s];
     1160       
     1161        strcpy( handle, out );
     1162}
  • protocols/nogaim.h

    rcd428e4 r256899f  
    6161#define OPT_AWAY        0x00000004
    6262#define OPT_DOES_HTML   0x00000010
     63#define OPT_LOCALBUDDY  0x00000020 /* For nicks local to one groupchat */
    6364#define OPT_TYPING      0x00000100 /* Some pieces of code make assumptions */
    6465#define OPT_THINKING    0x00000200 /* about these values... Stupid me! */
     
    9192        irc_t *irc;
    9293       
    93         struct groupchat *conversations;
     94        struct groupchat *groupchats;
    9495};
    9596
     
    104105        GList *ignored;
    105106       
    106         /* BitlBee */
    107107        struct groupchat *next;
    108108        char *channel;
     
    110110         * chat using imcb_chat_new(). */
    111111        char *title;
     112        /* Use imcb_chat_topic() to change this variable otherwise the user
     113         * won't notice the topic change. */
     114        char *topic;
    112115        char joined;
    113116        /* This is for you, you can add your own structure here to extend this
     
    212215        struct groupchat *
    213216             (* chat_join)      (struct im_connection *, char *room, char *nick, char *password);
     217        /* Change the topic, if supported. Note that BitlBee expects the IM
     218           server to confirm the topic change with a regular topic change
     219           event. If it doesn't do that, you have to fake it to make it
     220           visible to the user. */
     221        void (* chat_topic)     (struct groupchat *, char *topic);
    214222       
    215223        /* You can tell what away states your protocol supports, so that
     
    265273G_MODULE_EXPORT struct buddy *imcb_find_buddy( struct im_connection *ic, char *handle );
    266274G_MODULE_EXPORT void imcb_rename_buddy( struct im_connection *ic, char *handle, char *realname );
     275G_MODULE_EXPORT void imcb_buddy_nick_hint( struct im_connection *ic, char *handle, char *nick );
    267276
    268277/* Buddy activity */
     
    276285G_MODULE_EXPORT void imcb_buddy_msg( struct im_connection *ic, char *handle, char *msg, u_int32_t flags, time_t sent_at );
    277286G_MODULE_EXPORT void imcb_buddy_typing( struct im_connection *ic, char *handle, u_int32_t flags );
     287G_MODULE_EXPORT void imcb_clean_handle( struct im_connection *ic, char *handle );
    278288
    279289/* Groupchats */
     
    291301/* To tell BitlBee 'who' said 'msg' in 'c'. 'flags' and 'sent_at' can be 0. */
    292302G_MODULE_EXPORT void imcb_chat_msg( struct groupchat *c, char *who, char *msg, u_int32_t flags, time_t sent_at );
    293 G_MODULE_EXPORT void imcb_chat_removed( struct groupchat *c );
    294 struct groupchat *chat_by_channel( char *channel );
     303/* To tell BitlBee 'who' changed the topic of 'c' to 'topic'. */
     304G_MODULE_EXPORT void imcb_chat_topic( struct groupchat *c, char *who, char *topic, time_t set_at );
     305G_MODULE_EXPORT void imcb_chat_free( struct groupchat *c );
    295306
    296307/* Actions, or whatever. */
  • protocols/oscar/oscar.c

    rcd428e4 r256899f  
    20312031                                       
    20322032                                        if (realname) {
     2033                                                imcb_buddy_nick_hint(ic, curitem->name, realname);
    20332034                                                imcb_rename_buddy(ic, curitem->name, realname);
    20342035                                                g_free(realname);
     
    25232524
    25242525        /* Notify the conversation window that we've left the chat */
    2525         imcb_chat_removed(cc->cnv);
     2526        imcb_chat_free(cc->cnv);
    25262527
    25272528        /* Destroy the chat_connection */
  • protocols/yahoo/yahoo.c

    rcd428e4 r256899f  
    145145        GSList *l;
    146146       
    147         while( ic->conversations )
    148                 imcb_chat_removed( ic->conversations );
     147        while( ic->groupchats )
     148                imcb_chat_free( ic->groupchats );
    149149       
    150150        for( l = yd->buddygroups; l; l = l->next )
     
    318318       
    319319        yahoo_conference_logoff( yd->y2_id, NULL, c->data, c->title );
    320         imcb_chat_removed( c );
     320        imcb_chat_free( c );
    321321}
    322322
     
    798798{
    799799        yahoo_conference_decline( inv->yid, NULL, inv->members, inv->name, "User rejected groupchat" );
    800         imcb_chat_removed( inv->c );
     800        imcb_chat_free( inv->c );
    801801        g_free( inv->name );
    802802        g_free( inv );
     
    841841        struct groupchat *c;
    842842       
    843         for( c = ic->conversations; c && strcmp( c->title, room ) != 0; c = c->next );
     843        for( c = ic->groupchats; c && strcmp( c->title, room ) != 0; c = c->next );
    844844       
    845845        if( c )
     
    853853        struct groupchat *c;
    854854       
    855         for( c = ic->conversations; c && strcmp( c->title, room ) != 0; c = c->next );
     855        for( c = ic->groupchats; c && strcmp( c->title, room ) != 0; c = c->next );
    856856       
    857857        if( c )
     
    865865        struct groupchat *c;
    866866       
    867         for( c = ic->conversations; c && strcmp( c->title, room ) != 0; c = c->next );
     867        for( c = ic->groupchats; c && strcmp( c->title, room ) != 0; c = c->next );
    868868       
    869869        if( c )
  • root_commands.c

    rcd428e4 r256899f  
    930930        if( cmd[3] )
    931931        {
    932                 channel = g_strdup( cmd[3] );
     932                if( cmd[3][0] != '#' && cmd[3][0] != '&' )
     933                        channel = g_strdup_printf( "&%s", cmd[3] );
     934                else
     935                        channel = g_strdup( cmd[3] );
    933936        }
    934937        else
     
    936939                char *s;
    937940               
    938                 channel = g_strdup( chat );
     941                channel = g_strdup_printf( "&%s", chat );
    939942                if( ( s = strchr( channel, '@' ) ) )
    940943                        *s = 0;
     
    942945        if( cmd[3] && cmd[4] )
    943946                nick = cmd[4];
     947        else
     948                nick = irc->nick;
    944949        if( cmd[3] && cmd[4] && cmd[5] )
    945950                password = cmd[5];
    946951       
    947         c = a->prpl->chat_join( ic, chat, nick, password );
    948        
    949         g_free( channel );
     952        if( !nick_ok( channel + 1 ) )
     953        {
     954                irc_usermsg( irc, "Invalid channel name: %s", channel );
     955                g_free( channel );
     956                return;
     957        }
     958        else if( g_strcasecmp( channel, irc->channel ) == 0 || irc_chat_by_channel( irc, channel ) )
     959        {
     960                irc_usermsg( irc, "Channel already exists: %s", channel );
     961                g_free( channel );
     962                return;
     963        }
     964       
     965        if( ( c = a->prpl->chat_join( ic, chat, nick, password ) ) )
     966        {
     967                g_free( c->channel );
     968                c->channel = channel;
     969        }
     970        else
     971        {
     972                irc_usermsg( irc, "Tried to join chat, not sure if this was successful" );
     973                g_free( channel );
     974        }
    950975}
    951976
  • user.c

    rcd428e4 r256899f  
    161161}
    162162
     163/* DO NOT PASS u->nick FOR oldnick !!! */
    163164void user_rename( irc_t *irc, char *oldnick, char *newnick )
    164165{
Note: See TracChangeset for help on using the changeset viewer.