source: protocols/jabber/conference.c @ 1fa5109

Last change on this file since 1fa5109 was 0e788f5, checked in by Wilmer van der Gaast <wilmer@…>, at 2013-02-21T19:15:59Z

I'm still bored on a long flight. Wrote a script to automatically update
my copyright mentions since some were getting pretty stale. Left files not
touched since before 2012 alone so that this change doesn't touch almost
EVERY source file.

  • Property mode set to 100644
File size: 12.4 KB
Line 
1/***************************************************************************\
2*                                                                           *
3*  BitlBee - An IRC to IM gateway                                           *
4*  Jabber module - Conference rooms                                         *
5*                                                                           *
6*  Copyright 2007-2012 Wilmer van der Gaast <wilmer@gaast.net>              *
7*                                                                           *
8*  This program is free software; you can redistribute it and/or modify     *
9*  it under the terms of the GNU General Public License as published by     *
10*  the Free Software Foundation; either version 2 of the License, or        *
11*  (at your option) any later version.                                      *
12*                                                                           *
13*  This program is distributed in the hope that it will be useful,          *
14*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
15*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            *
16*  GNU General Public License for more details.                             *
17*                                                                           *
18*  You should have received a copy of the GNU General Public License along  *
19*  with this program; if not, write to the Free Software Foundation, Inc.,  *
20*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
21*                                                                           *
22\***************************************************************************/
23
24#include "jabber.h"
25#include "sha1.h"
26
27static xt_status jabber_chat_join_failed( struct im_connection *ic, struct xt_node *node, struct xt_node *orig );
28
29struct groupchat *jabber_chat_join( struct im_connection *ic, const char *room, const char *nick, const char *password )
30{
31        struct jabber_chat *jc;
32        struct xt_node *node;
33        struct groupchat *c;
34        char *roomjid;
35       
36        roomjid = g_strdup_printf( "%s/%s", room, nick );
37        node = xt_new_node( "x", NULL, NULL );
38        xt_add_attr( node, "xmlns", XMLNS_MUC );
39        if( password )
40                xt_add_child( node, xt_new_node( "password", password, NULL ) );
41        node = jabber_make_packet( "presence", NULL, roomjid, node );
42        jabber_cache_add( ic, node, jabber_chat_join_failed );
43       
44        if( !jabber_write_packet( ic, node ) )
45        {
46                g_free( roomjid );
47                return NULL;
48        }
49       
50        jc = g_new0( struct jabber_chat, 1 );
51        jc->name = jabber_normalize( room );
52       
53        if( ( jc->me = jabber_buddy_add( ic, roomjid ) ) == NULL )
54        {
55                g_free( roomjid );
56                g_free( jc->name );
57                g_free( jc );
58                return NULL;
59        }
60       
61        /* roomjid isn't normalized yet, and we need an original version
62           of the nick to send a proper presence update. */
63        jc->my_full_jid = roomjid;
64       
65        c = imcb_chat_new( ic, room );
66        c->data = jc;
67       
68        return c;
69}
70
71struct groupchat *jabber_chat_with( struct im_connection *ic, char *who )
72{
73        struct jabber_data *jd = ic->proto_data;
74        struct jabber_chat *jc;
75        struct groupchat *c;
76        sha1_state_t sum;
77        double now = gettime();
78        char *uuid, *rjid, *cserv;
79       
80        sha1_init( &sum );
81        sha1_append( &sum, (uint8_t*) ic->acc->user, strlen( ic->acc->user ) );
82        sha1_append( &sum, (uint8_t*) &now, sizeof( now ) );
83        sha1_append( &sum, (uint8_t*) who, strlen( who ) );
84        uuid = sha1_random_uuid( &sum );
85       
86        if( jd->flags & JFLAG_GTALK )
87                cserv = g_strdup( "groupchat.google.com" );
88        else
89                /* Guess... */
90                cserv = g_strdup_printf( "conference.%s", jd->server );
91       
92        rjid = g_strdup_printf( "private-chat-%s@%s", uuid, cserv );
93        g_free( uuid );
94        g_free( cserv );
95       
96        c = jabber_chat_join( ic, rjid, jd->username, NULL );
97        g_free( rjid );
98        if( c == NULL )
99                return NULL;
100       
101        jc = c->data;
102        jc->invite = g_strdup( who );
103       
104        return c;
105}
106
107static xt_status jabber_chat_join_failed( struct im_connection *ic, struct xt_node *node, struct xt_node *orig )
108{
109        struct jabber_error *err;
110        struct jabber_buddy *bud;
111        char *room;
112       
113        room = xt_find_attr( orig, "to" );
114        bud = jabber_buddy_by_jid( ic, room, 0 );
115        err = jabber_error_parse( xt_find_node( node->children, "error" ), XMLNS_STANZA_ERROR );
116        if( err )
117        {
118                imcb_error( ic, "Error joining groupchat %s: %s%s%s", room, err->code,
119                            err->text ? ": " : "", err->text ? err->text : "" );
120                jabber_error_free( err );
121        }
122        if( bud )
123                jabber_chat_free( jabber_chat_by_jid( ic, bud->bare_jid ) );
124       
125        return XT_HANDLED;
126}
127
128struct groupchat *jabber_chat_by_jid( struct im_connection *ic, const char *name )
129{
130        char *normalized = jabber_normalize( name );
131        GSList *l;
132        struct groupchat *ret;
133        struct jabber_chat *jc;
134       
135        for( l = ic->groupchats; l; l = l->next )
136        {
137                ret = l->data;
138                jc = ret->data;
139                if( strcmp( normalized, jc->name ) == 0 )
140                        break;
141        }
142        g_free( normalized );
143       
144        return l ? ret : NULL;
145}
146
147void jabber_chat_free( struct groupchat *c )
148{
149        struct jabber_chat *jc = c->data;
150       
151        jabber_buddy_remove_bare( c->ic, jc->name );
152       
153        g_free( jc->my_full_jid );
154        g_free( jc->name );
155        g_free( jc->invite );
156        g_free( jc );
157       
158        imcb_chat_free( c );
159}
160
161int jabber_chat_msg( struct groupchat *c, char *message, int flags )
162{
163        struct im_connection *ic = c->ic;
164        struct jabber_chat *jc = c->data;
165        struct xt_node *node;
166       
167        jc->flags |= JCFLAG_MESSAGE_SENT;
168       
169        node = xt_new_node( "body", message, NULL );
170        node = jabber_make_packet( "message", "groupchat", jc->name, node );
171       
172        if( !jabber_write_packet( ic, node ) )
173        {
174                xt_free_node( node );
175                return 0;
176        }
177        xt_free_node( node );
178       
179        return 1;
180}
181
182int jabber_chat_topic( struct groupchat *c, char *topic )
183{
184        struct im_connection *ic = c->ic;
185        struct jabber_chat *jc = c->data;
186        struct xt_node *node;
187       
188        node = xt_new_node( "subject", topic, NULL );
189        node = jabber_make_packet( "message", "groupchat", jc->name, node );
190       
191        if( !jabber_write_packet( ic, node ) )
192        {
193                xt_free_node( node );
194                return 0;
195        }
196        xt_free_node( node );
197       
198        return 1;
199}
200
201int jabber_chat_leave( struct groupchat *c, const char *reason )
202{
203        struct im_connection *ic = c->ic;
204        struct jabber_chat *jc = c->data;
205        struct xt_node *node;
206       
207        node = xt_new_node( "x", NULL, NULL );
208        xt_add_attr( node, "xmlns", XMLNS_MUC );
209        node = jabber_make_packet( "presence", "unavailable", jc->my_full_jid, node );
210       
211        if( !jabber_write_packet( ic, node ) )
212        {
213                xt_free_node( node );
214                return 0;
215        }
216        xt_free_node( node );
217       
218        return 1;
219}
220
221void jabber_chat_invite( struct groupchat *c, char *who, char *message )
222{
223        struct xt_node *node;
224        struct im_connection *ic = c->ic;
225        struct jabber_chat *jc = c->data;
226
227        node = xt_new_node( "reason", message, NULL ); 
228
229        node = xt_new_node( "invite", NULL, node );
230        xt_add_attr( node, "to", who ); 
231
232        node = xt_new_node( "x", NULL, node ); 
233        xt_add_attr( node, "xmlns", XMLNS_MUC_USER ); 
234       
235        node = jabber_make_packet( "message", NULL, jc->name, node ); 
236
237        jabber_write_packet( ic, node ); 
238
239        xt_free_node( node );
240}
241
242/* Not really the same syntax as the normal pkt_ functions, but this isn't
243   called by the xmltree parser directly and this way I can add some extra
244   parameters so we won't have to repeat too many things done by the caller
245   already. */
246void jabber_chat_pkt_presence( struct im_connection *ic, struct jabber_buddy *bud, struct xt_node *node )
247{
248        struct groupchat *chat;
249        struct xt_node *c;
250        char *type = xt_find_attr( node, "type" );
251        struct jabber_data *jd = ic->proto_data;
252        struct jabber_chat *jc;
253        char *s;
254       
255        if( ( chat = jabber_chat_by_jid( ic, bud->bare_jid ) ) == NULL )
256        {
257                /* How could this happen?? We could do kill( self, 11 )
258                   now or just wait for the OS to do it. :-) */
259                return;
260        }
261       
262        jc = chat->data;
263       
264        if( type == NULL && !( bud->flags & JBFLAG_IS_CHATROOM ) )
265        {
266                bud->flags |= JBFLAG_IS_CHATROOM;
267                /* If this one wasn't set yet, this buddy just joined the chat.
268                   Slightly hackish way of finding out eh? ;-) */
269               
270                /* This is pretty messy... Here it sets ext_jid to the real
271                   JID of the participant. Works for non-anonymized channels.
272                   Might break if someone joins a chat twice, though. */
273                for( c = node->children; ( c = xt_find_node( c, "x" ) ); c = c->next )
274                        if( ( s = xt_find_attr( c, "xmlns" ) ) &&
275                            ( strcmp( s, XMLNS_MUC_USER ) == 0 ) )
276                        {
277                                struct xt_node *item;
278                               
279                                item = xt_find_node( c->children, "item" );
280                                if( ( s = xt_find_attr( item, "jid" ) ) )
281                                {
282                                        /* Yay, found what we need. :-) */
283                                        bud->ext_jid = jabber_normalize( s );
284                                        break;
285                                }
286                        }
287               
288                /* Make up some other handle, if necessary. */
289                if( bud->ext_jid == NULL )
290                {
291                        if( bud == jc->me )
292                        {
293                                bud->ext_jid = g_strdup( jd->me );
294                        }
295                        else
296                        {
297                                int i;
298                               
299                                /* Don't want the nick to be at the end, so let's
300                                   think of some slightly different notation to use
301                                   for anonymous groupchat participants in BitlBee. */
302                                bud->ext_jid = g_strdup_printf( "%s=%s", bud->resource, bud->bare_jid );
303                               
304                                /* And strip any unwanted characters. */
305                                for( i = 0; bud->resource[i]; i ++ )
306                                        if( bud->ext_jid[i] == '=' || bud->ext_jid[i] == '@' )
307                                                bud->ext_jid[i] = '_';
308                               
309                                /* Some program-specific restrictions. */
310                                imcb_clean_handle( ic, bud->ext_jid );
311                        }
312                        bud->flags |= JBFLAG_IS_ANONYMOUS;
313                }
314               
315                if( bud != jc->me && bud->flags & JBFLAG_IS_ANONYMOUS )
316                {
317                        /* If JIDs are anonymized, add them to the local
318                           list for the duration of this chat. */
319                        imcb_add_buddy( ic, bud->ext_jid, NULL );
320                        imcb_buddy_nick_hint( ic, bud->ext_jid, bud->resource );
321                }
322               
323                if( bud == jc->me && jc->invite != NULL )
324                {
325                        char *msg = g_strdup_printf( "Please join me in room %s", jc->name );
326                        jabber_chat_invite( chat, jc->invite, msg );
327                        g_free( jc->invite );
328                        g_free( msg );
329                        jc->invite = NULL;
330                }
331               
332                s = strchr( bud->ext_jid, '/' );
333                if( s ) *s = 0; /* Should NEVER be NULL, but who knows... */
334                imcb_chat_add_buddy( chat, bud->ext_jid );
335                if( s ) *s = '/';
336        }
337        else if( type ) /* type can only be NULL or "unavailable" in this function */
338        {
339                if( ( bud->flags & JBFLAG_IS_CHATROOM ) && bud->ext_jid )
340                {
341                        s = strchr( bud->ext_jid, '/' );
342                        if( s ) *s = 0;
343                        imcb_chat_remove_buddy( chat, bud->ext_jid, NULL );
344                        if( bud != jc->me && bud->flags & JBFLAG_IS_ANONYMOUS )
345                                imcb_remove_buddy( ic, bud->ext_jid, NULL );
346                        if( s ) *s = '/';
347                }
348               
349                if( bud == jc->me )
350                        jabber_chat_free( chat );
351        }
352}
353
354void jabber_chat_pkt_message( struct im_connection *ic, struct jabber_buddy *bud, struct xt_node *node )
355{
356        struct xt_node *subject = xt_find_node( node->children, "subject" );
357        struct xt_node *body = xt_find_node( node->children, "body" );
358        struct groupchat *chat = bud ? jabber_chat_by_jid( ic, bud->bare_jid ) : NULL;
359        struct jabber_chat *jc = chat ? chat->data : NULL;
360        char *s;
361       
362        if( subject && chat )
363        {
364                s = bud ? strchr( bud->ext_jid, '/' ) : NULL;
365                if( s ) *s = 0;
366                imcb_chat_topic( chat, bud ? bud->ext_jid : NULL, subject->text_len > 0 ?
367                                 subject->text : NULL, jabber_get_timestamp( node ) );
368                if( s ) *s = '/';
369        }
370       
371        if( bud == NULL || ( jc && ~jc->flags & JCFLAG_MESSAGE_SENT && bud == jc->me ) )
372        {
373                char *nick;
374               
375                if( body == NULL || body->text_len == 0 )
376                        /* Meh. Empty messages aren't very interesting, no matter
377                           how much some servers love to send them. */
378                        return;
379               
380                s = xt_find_attr( node, "from" ); /* pkt_message() already NULL-checked this one. */
381                nick = strchr( s, '/' );
382                if( nick )
383                {
384                        /* If this message included a resource/nick we don't know,
385                           we might still know the groupchat itself. */
386                        *nick = 0;
387                        chat = jabber_chat_by_jid( ic, s );
388                        *nick = '/';
389                       
390                        nick ++;
391                }
392                else
393                {
394                        /* message.c uses the EXACT_JID option, so bud should
395                           always be NULL here for bare JIDs. */
396                        chat = jabber_chat_by_jid( ic, s );
397                }
398               
399                if( nick == NULL )
400                {
401                        /* This is fine, the groupchat itself isn't in jd->buddies. */
402                        if( chat )
403                                imcb_chat_log( chat, "From conference server: %s", body->text );
404                        else
405                                imcb_log( ic, "System message from unknown groupchat %s: %s", s, body->text );
406                }
407                else
408                {
409                        /* This can happen too, at least when receiving a backlog when
410                           just joining a channel. */
411                        if( chat )
412                                imcb_chat_log( chat, "Message from unknown participant %s: %s", nick, body->text );
413                        else
414                                imcb_log( ic, "Groupchat message from unknown JID %s: %s", s, body->text );
415                }
416               
417                return;
418        }
419        else if( chat == NULL )
420        {
421                /* How could this happen?? We could do kill( self, 11 )
422                   now or just wait for the OS to do it. :-) */
423                return;
424        }
425        if( body && body->text_len > 0 )
426        {
427                s = strchr( bud->ext_jid, '/' );
428                if( s ) *s = 0;
429                imcb_chat_msg( chat, bud->ext_jid, body->text, 0, jabber_get_timestamp( node ) );
430                if( s ) *s = '/';
431        }
432}
Note: See TracBrowser for help on using the repository browser.