source: protocols/msn/sb.c @ e7f8838

Last change on this file since e7f8838 was 46dca11, checked in by Wilmer van der Gaast <wilmer@…>, at 2008-01-06T12:32:27Z

Changed warning message about unsent MSN messages. It should show the actual
message(s) now.

  • Property mode set to 100644
File size: 15.1 KB
Line 
1  /********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
4  * Copyright 2002-2005 Wilmer van der Gaast and others                *
5  \********************************************************************/
6
7/* MSN module - Switchboard server callbacks and utilities              */
8
9/*
10  This program is free software; you can redistribute it and/or modify
11  it under the terms of the GNU General Public License as published by
12  the Free Software Foundation; either version 2 of the License, or
13  (at your option) any later version.
14
15  This program is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  GNU General Public License for more details.
19
20  You should have received a copy of the GNU General Public License with
21  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
22  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
23  Suite 330, Boston, MA  02111-1307  USA
24*/
25
26#include <ctype.h>
27#include "nogaim.h"
28#include "msn.h"
29#include "passport.h"
30#include "md5.h"
31
32static gboolean msn_sb_callback( gpointer data, gint source, b_input_condition cond );
33static int msn_sb_command( gpointer data, char **cmd, int num_parts );
34static int msn_sb_message( gpointer data, char *msg, int msglen, char **cmd, int num_parts );
35
36int msn_sb_write( struct msn_switchboard *sb, char *s, int len )
37{
38        int st;
39       
40        st = write( sb->fd, s, len );
41        if( st != len )
42        {
43                msn_sb_destroy( sb );
44                return( 0 );
45        }
46       
47        return( 1 );
48}
49
50struct msn_switchboard *msn_sb_create( struct im_connection *ic, char *host, int port, char *key, int session )
51{
52        struct msn_data *md = ic->proto_data;
53        struct msn_switchboard *sb = g_new0( struct msn_switchboard, 1 );
54       
55        sb->fd = proxy_connect( host, port, msn_sb_connected, sb );
56        if( sb->fd < 0 )
57        {
58                g_free( sb );
59                return( NULL );
60        }
61       
62        sb->ic = ic;
63        sb->key = g_strdup( key );
64        sb->session = session;
65       
66        msn_switchboards = g_slist_append( msn_switchboards, sb );
67        md->switchboards = g_slist_append( md->switchboards, sb );
68       
69        return( sb );
70}
71
72struct msn_switchboard *msn_sb_by_handle( struct im_connection *ic, char *handle )
73{
74        struct msn_data *md = ic->proto_data;
75        struct msn_switchboard *sb;
76        GSList *l;
77       
78        for( l = md->switchboards; l; l = l->next )
79        {
80                sb = l->data;
81                if( sb->who && strcmp( sb->who, handle ) == 0 )
82                        return( sb );
83        }
84       
85        return( NULL );
86}
87
88struct msn_switchboard *msn_sb_by_chat( struct groupchat *c )
89{
90        struct msn_data *md = c->ic->proto_data;
91        struct msn_switchboard *sb;
92        GSList *l;
93       
94        for( l = md->switchboards; l; l = l->next )
95        {
96                sb = l->data;
97                if( sb->chat == c )
98                        return( sb );
99        }
100       
101        return( NULL );
102}
103
104struct msn_switchboard *msn_sb_spare( struct im_connection *ic )
105{
106        struct msn_data *md = ic->proto_data;
107        struct msn_switchboard *sb;
108        GSList *l;
109       
110        for( l = md->switchboards; l; l = l->next )
111        {
112                sb = l->data;
113                if( !sb->who && !sb->chat )
114                        return( sb );
115        }
116       
117        return( NULL );
118}
119
120int msn_sb_sendmessage( struct msn_switchboard *sb, char *text )
121{
122        if( sb->ready )
123        {
124                char *packet, *buf;
125                int i, j;
126               
127                /* Build the message. Convert LF to CR-LF for normal messages. */
128                if( strcmp( text, TYPING_NOTIFICATION_MESSAGE ) != 0 )
129                {
130                        buf = g_new0( char, sizeof( MSN_MESSAGE_HEADERS ) + strlen( text ) * 2 + 1 );
131                        i = strlen( MSN_MESSAGE_HEADERS );
132                       
133                        strcpy( buf, MSN_MESSAGE_HEADERS );
134                        for( j = 0; text[j]; j ++ )
135                        {
136                                if( text[j] == '\n' )
137                                        buf[i++] = '\r';
138                               
139                                buf[i++] = text[j];
140                        }
141                }
142                else
143                {
144                        i = strlen( MSN_TYPING_HEADERS ) + strlen( sb->ic->acc->user );
145                        buf = g_new0( char, i );
146                        i = g_snprintf( buf, i, MSN_TYPING_HEADERS, sb->ic->acc->user );
147                }
148               
149                /* Build the final packet (MSG command + the message). */
150                packet = g_strdup_printf( "MSG %d N %d\r\n%s", ++sb->trId, i, buf );
151                g_free( buf );
152                if( msn_sb_write( sb, packet, strlen( packet ) ) )
153                {
154                        g_free( packet );
155                        return( 1 );
156                }
157                else
158                {
159                        g_free( packet );
160                        return( 0 );
161                }
162        }
163        else if( sb->who )
164        {
165                struct msn_message *m = g_new0( struct msn_message, 1 );
166               
167                m->who = g_strdup( "" );
168                m->text = g_strdup( text );
169                sb->msgq = g_slist_append( sb->msgq, m );
170               
171                return( 1 );
172        }
173        else
174        {
175                return( 0 );
176        }
177}
178
179struct groupchat *msn_sb_to_chat( struct msn_switchboard *sb )
180{
181        struct im_connection *ic = sb->ic;
182        char buf[1024];
183       
184        /* Create the groupchat structure. */
185        g_snprintf( buf, sizeof( buf ), "MSN groupchat session %d", sb->session );
186        sb->chat = imcb_chat_new( ic, buf );
187       
188        /* Populate the channel. */
189        if( sb->who ) imcb_chat_add_buddy( sb->chat, sb->who );
190        imcb_chat_add_buddy( sb->chat, ic->acc->user );
191       
192        /* And make sure the switchboard doesn't look like a regular chat anymore. */
193        if( sb->who )
194        {
195                g_free( sb->who );
196                sb->who = NULL;
197        }
198       
199        return sb->chat;
200}
201
202void msn_sb_destroy( struct msn_switchboard *sb )
203{
204        struct im_connection *ic = sb->ic;
205        struct msn_data *md = ic->proto_data;
206       
207        debug( "Destroying switchboard: %s", sb->who ? sb->who : sb->key ? sb->key : "" );
208       
209        msn_msgq_purge( ic, &sb->msgq );
210       
211        if( sb->key ) g_free( sb->key );
212        if( sb->who ) g_free( sb->who );
213       
214        if( sb->chat )
215        {
216                imcb_chat_free( sb->chat );
217        }
218       
219        if( sb->handler )
220        {
221                if( sb->handler->rxq ) g_free( sb->handler->rxq );
222                if( sb->handler->cmd_text ) g_free( sb->handler->cmd_text );
223                g_free( sb->handler );
224        }
225       
226        if( sb->inp ) b_event_remove( sb->inp );
227        closesocket( sb->fd );
228       
229        msn_switchboards = g_slist_remove( msn_switchboards, sb );
230        md->switchboards = g_slist_remove( md->switchboards, sb );
231        g_free( sb );
232}
233
234gboolean msn_sb_connected( gpointer data, gint source, b_input_condition cond )
235{
236        struct msn_switchboard *sb = data;
237        struct im_connection *ic;
238        struct msn_data *md;
239        char buf[1024];
240       
241        /* Are we still alive? */
242        if( !g_slist_find( msn_switchboards, sb ) )
243                return FALSE;
244       
245        ic = sb->ic;
246        md = ic->proto_data;
247       
248        if( source != sb->fd )
249        {
250                debug( "Error %d while connecting to switchboard server", 1 );
251                msn_sb_destroy( sb );
252                return FALSE;
253        }
254       
255        /* Prepare the callback */
256        sb->handler = g_new0( struct msn_handler_data, 1 );
257        sb->handler->fd = sb->fd;
258        sb->handler->rxq = g_new0( char, 1 );
259        sb->handler->data = sb;
260        sb->handler->exec_command = msn_sb_command;
261        sb->handler->exec_message = msn_sb_message;
262       
263        if( sb->session == MSN_SB_NEW )
264                g_snprintf( buf, sizeof( buf ), "USR %d %s %s\r\n", ++sb->trId, ic->acc->user, sb->key );
265        else
266                g_snprintf( buf, sizeof( buf ), "ANS %d %s %s %d\r\n", ++sb->trId, ic->acc->user, sb->key, sb->session );
267       
268        if( msn_sb_write( sb, buf, strlen( buf ) ) )
269                sb->inp = b_input_add( sb->fd, GAIM_INPUT_READ, msn_sb_callback, sb );
270        else
271                debug( "Error %d while connecting to switchboard server", 2 );
272       
273        return FALSE;
274}
275
276static gboolean msn_sb_callback( gpointer data, gint source, b_input_condition cond )
277{
278        struct msn_switchboard *sb = data;
279       
280        if( msn_handler( sb->handler ) == -1 )
281        {
282                debug( "Error: Switchboard died" );
283                msn_sb_destroy( sb );
284               
285                return FALSE;
286        }
287        else
288                return TRUE;
289}
290
291static int msn_sb_command( gpointer data, char **cmd, int num_parts )
292{
293        struct msn_switchboard *sb = data;
294        struct im_connection *ic = sb->ic;
295        char buf[1024];
296       
297        if( !num_parts )
298        {
299                /* Hrrm... Empty command...? Ignore? */
300                return( 1 );
301        }
302       
303        if( strcmp( cmd[0], "XFR" ) == 0 )
304        {
305                imcb_error( ic, "Received an XFR from a switchboard server, unable to comply! This is likely to be a bug, please report it!" );
306                imc_logout( ic, TRUE );
307                return( 0 );
308        }
309        else if( strcmp( cmd[0], "USR" ) == 0 )
310        {
311                if( num_parts != 5 )
312                {
313                        msn_sb_destroy( sb );
314                        return( 0 );
315                }
316               
317                if( strcmp( cmd[2], "OK" ) != 0 )
318                {
319                        msn_sb_destroy( sb );
320                        return( 0 );
321                }
322               
323                if( sb->who )
324                {
325                        g_snprintf( buf, sizeof( buf ), "CAL %d %s\r\n", ++sb->trId, sb->who );
326                        return( msn_sb_write( sb, buf, strlen( buf ) ) );
327                }
328                else
329                {
330                        debug( "Just created a switchboard, but I don't know what to do with it." );
331                }
332        }
333        else if( strcmp( cmd[0], "IRO" ) == 0 )
334        {
335                int num, tot;
336               
337                if( num_parts != 6 )
338                {
339                        msn_sb_destroy( sb );
340                        return( 0 );
341                }
342               
343                num = atoi( cmd[2] );
344                tot = atoi( cmd[3] );
345               
346                if( tot <= 0 )
347                {
348                        msn_sb_destroy( sb );
349                        return( 0 );
350                }
351                else if( tot > 1 )
352                {
353                        char buf[1024];
354                       
355                        if( num == 1 )
356                        {
357                                g_snprintf( buf, sizeof( buf ), "MSN groupchat session %d", sb->session );
358                                sb->chat = imcb_chat_new( ic, buf );
359                               
360                                g_free( sb->who );
361                                sb->who = NULL;
362                        }
363                       
364                        imcb_chat_add_buddy( sb->chat, cmd[4] );
365                       
366                        if( num == tot )
367                        {
368                                imcb_chat_add_buddy( sb->chat, ic->acc->user );
369                        }
370                }
371        }
372        else if( strcmp( cmd[0], "ANS" ) == 0 )
373        {
374                if( num_parts != 3 )
375                {
376                        msn_sb_destroy( sb );
377                        return( 0 );
378                }
379               
380                if( strcmp( cmd[2], "OK" ) != 0 )
381                {
382                        debug( "Switchboard server sent a negative ANS reply" );
383                        msn_sb_destroy( sb );
384                        return( 0 );
385                }
386               
387                sb->ready = 1;
388        }
389        else if( strcmp( cmd[0], "CAL" ) == 0 )
390        {
391                if( num_parts != 4 || !isdigit( cmd[3][0] ) )
392                {
393                        msn_sb_destroy( sb );
394                        return( 0 );
395                }
396               
397                sb->session = atoi( cmd[3] );
398        }
399        else if( strcmp( cmd[0], "JOI" ) == 0 )
400        {
401                if( num_parts != 3 )
402                {
403                        msn_sb_destroy( sb );
404                        return( 0 );
405                }
406               
407                if( sb->who && g_strcasecmp( cmd[1], sb->who ) == 0 )
408                {
409                        /* The user we wanted to talk to is finally there, let's send the queued messages then. */
410                        struct msn_message *m;
411                        GSList *l;
412                        int st = 1;
413                       
414                        debug( "%s arrived in the switchboard session, now sending queued message(s)", cmd[1] );
415                       
416                        /* Without this, sendmessage() will put everything back on the queue... */
417                        sb->ready = 1;
418                       
419                        while( ( l = sb->msgq ) )
420                        {
421                                m = l->data;
422                                if( st )
423                                {
424                                        /* This hack is meant to convert a regular new chat into a groupchat */
425                                        if( strcmp( m->text, GROUPCHAT_SWITCHBOARD_MESSAGE ) == 0 )
426                                                msn_sb_to_chat( sb );
427                                        else
428                                                st = msn_sb_sendmessage( sb, m->text );
429                                }
430                                g_free( m->text );
431                                g_free( m->who );
432                                g_free( m );
433                               
434                                sb->msgq = g_slist_remove( sb->msgq, m );
435                        }
436                       
437                        return( st );
438                }
439                else if( sb->who )
440                {
441                        debug( "Converting chat with %s to a groupchat because %s joined the session.", sb->who, cmd[1] );
442                       
443                        /* This SB is a one-to-one chat right now, but someone else is joining. */
444                        msn_sb_to_chat( sb );
445                       
446                        imcb_chat_add_buddy( sb->chat, cmd[1] );
447                }
448                else if( sb->chat )
449                {
450                        imcb_chat_add_buddy( sb->chat, cmd[1] );
451                        sb->ready = 1;
452                }
453                else
454                {
455                        /* PANIC! */
456                }
457        }
458        else if( strcmp( cmd[0], "MSG" ) == 0 )
459        {
460                if( num_parts != 4 )
461                {
462                        msn_sb_destroy( sb );
463                        return( 0 );
464                }
465               
466                sb->handler->msglen = atoi( cmd[3] );
467               
468                if( sb->handler->msglen <= 0 )
469                {
470                        debug( "Received a corrupted message on the switchboard, the switchboard will be closed" );
471                        msn_sb_destroy( sb );
472                        return( 0 );
473                }
474        }
475        else if( strcmp( cmd[0], "NAK" ) == 0 )
476        {
477                if( sb->who )
478                {
479                        imcb_log( ic, "The MSN servers could not deliver one of your messages to %s.", sb->who );
480                }
481                else
482                {
483                        imcb_log( ic, "The MSN servers could not deliver one of your groupchat messages to all participants." );
484                }
485        }
486        else if( strcmp( cmd[0], "BYE" ) == 0 )
487        {
488                if( num_parts < 2 )
489                {
490                        msn_sb_destroy( sb );
491                        return( 0 );
492                }
493               
494                /* if( cmd[2] && *cmd[2] == '1' ) -=> Chat is being cleaned up because of idleness */
495               
496                if( sb->who )
497                {
498                        /* This is a single-person chat, and the other person is leaving. */
499                        g_free( sb->who );
500                        sb->who = NULL;
501                        sb->ready = 0;
502                       
503                        debug( "Person %s left the one-to-one switchboard connection. Keeping it around as a spare...", cmd[1] );
504                       
505                        /* We could clean up the switchboard now, but keeping it around
506                           as a spare for a next conversation sounds more sane to me.
507                           The server will clean it up when it's idle for too long. */
508                }
509                else if( sb->chat )
510                {
511                        imcb_chat_remove_buddy( sb->chat, cmd[1], "" );
512                }
513                else
514                {
515                        /* PANIC! */
516                }
517        }
518        else if( isdigit( cmd[0][0] ) )
519        {
520                int num = atoi( cmd[0] );
521                const struct msn_status_code *err = msn_status_by_number( num );
522               
523                imcb_error( ic, "Error reported by switchboard server: %s", err->text );
524               
525                if( err->flags & STATUS_SB_FATAL )
526                {
527                        msn_sb_destroy( sb );
528                        return 0;
529                }
530                else if( err->flags & STATUS_FATAL )
531                {
532                        imc_logout( ic, TRUE );
533                        return 0;
534                }
535                else if( err->flags & STATUS_SB_IM_SPARE )
536                {
537                        if( sb->who )
538                        {
539                                /* Apparently some invitation failed. We might want to use this
540                                   board later, so keep it as a spare. */
541                                g_free( sb->who );
542                                sb->who = NULL;
543                               
544                                /* Also clear the msgq, otherwise someone else might get them. */
545                                msn_msgq_purge( ic, &sb->msgq );
546                        }
547                       
548                        /* Do NOT return 0 here, we want to keep this sb. */
549                }
550        }
551        else
552        {
553                debug( "Received unknown command from switchboard server: %s", cmd[0] );
554        }
555       
556        return( 1 );
557}
558
559static int msn_sb_message( gpointer data, char *msg, int msglen, char **cmd, int num_parts )
560{
561        struct msn_switchboard *sb = data;
562        struct im_connection *ic = sb->ic;
563        char *body;
564        int blen = 0;
565       
566        if( !num_parts )
567                return( 1 );
568       
569        if( ( body = strstr( msg, "\r\n\r\n" ) ) )
570        {
571                body += 4;
572                blen = msglen - ( body - msg );
573        }
574       
575        if( strcmp( cmd[0], "MSG" ) == 0 )
576        {
577                char *ct = msn_findheader( msg, "Content-Type:", msglen );
578               
579                if( !ct )
580                        return( 1 );
581               
582                if( g_strncasecmp( ct, "text/plain", 10 ) == 0 )
583                {
584                        g_free( ct );
585                       
586                        if( !body )
587                                return( 1 );
588                       
589                        if( sb->who )
590                        {
591                                imcb_buddy_msg( ic, cmd[1], body, 0, 0 );
592                        }
593                        else if( sb->chat )
594                        {
595                                imcb_chat_msg( sb->chat, cmd[1], body, 0, 0 );
596                        }
597                        else
598                        {
599                                /* PANIC! */
600                        }
601                }
602                else if( g_strncasecmp( ct, "text/x-msmsgsinvite", 19 ) == 0 )
603                {
604                        char *itype = msn_findheader( body, "Application-GUID:", blen );
605                        char buf[1024];
606                       
607                        g_free( ct );
608                       
609                        *buf = 0;
610                       
611                        if( !itype )
612                                return( 1 );
613                       
614                        /* File transfer. */
615                        if( strcmp( itype, "{5D3E02AB-6190-11d3-BBBB-00C04F795683}" ) == 0 )
616                        {
617                                char *name = msn_findheader( body, "Application-File:", blen );
618                                char *size = msn_findheader( body, "Application-FileSize:", blen );
619                               
620                                if( name && size )
621                                {
622                                        g_snprintf( buf, sizeof( buf ), "<< \x02""BitlBee\x02"" - Filetransfer: `%s', %s bytes >>\n"
623                                                    "Filetransfers are not supported by BitlBee for now...", name, size );
624                                }
625                                else
626                                {
627                                        strcpy( buf, "<< \x02""BitlBee\x02"" - Corrupted MSN filetransfer invitation message >>" );
628                                }
629                               
630                                if( name ) g_free( name );
631                                if( size ) g_free( size );
632                        }
633                        else
634                        {
635                                char *iname = msn_findheader( body, "Application-Name:", blen );
636                               
637                                g_snprintf( buf, sizeof( buf ), "<< \x02""BitlBee\x02"" - Unknown MSN invitation - %s (%s) >>",
638                                                                itype, iname ? iname : "no name" );
639                               
640                                if( iname ) g_free( iname );
641                        }
642                       
643                        g_free( itype );
644                       
645                        if( !*buf )
646                                return( 1 );
647                       
648                        if( sb->who )
649                        {
650                                imcb_buddy_msg( ic, cmd[1], buf, 0, 0 );
651                        }
652                        else if( sb->chat )
653                        {
654                                imcb_chat_msg( sb->chat, cmd[1], buf, 0, 0 );
655                        }
656                        else
657                        {
658                                /* PANIC! */
659                        }
660                }
661                else if( g_strncasecmp( ct, "text/x-msmsgscontrol", 20 ) == 0 )
662                {
663                        char *who = msn_findheader( msg, "TypingUser:", msglen );
664                       
665                        if( who )
666                        {
667                                imcb_buddy_typing( ic, who, OPT_TYPING );
668                                g_free( who );
669                        }
670                       
671                        g_free( ct );
672                }
673                else
674                {
675                        g_free( ct );
676                }
677        }
678       
679        return( 1 );
680}
Note: See TracBrowser for help on using the repository browser.