source: protocols/msn/msn.c @ 1ad104a

Last change on this file since 1ad104a was 1053836, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-01-30T13:30:59Z

When closing down MSN (sb) connections with unsent messages, the warning
message now mentions the people those messages were meant for.

  • Property mode set to 100644
File size: 10.3 KB
Line 
1  /********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
4  * Copyright 2002-2004 Wilmer van der Gaast and others                *
5  \********************************************************************/
6
7/* MSN module - Main file; functions to be called from BitlBee          */
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 "nogaim.h"
27#include "msn.h"
28
29static void msn_login( struct aim_user *acct )
30{
31        struct gaim_connection *gc = new_gaim_conn( acct );
32        struct msn_data *md = g_new0( struct msn_data, 1 );
33       
34        set_login_progress( gc, 1, "Connecting" );
35       
36        gc->proto_data = md;
37        md->fd = -1;
38       
39        if( strchr( acct->username, '@' ) == NULL )
40        {
41                hide_login_progress( gc, "Invalid account name" );
42                signoff( gc );
43                return;
44        }
45       
46        md->fd = proxy_connect( "messenger.hotmail.com", 1863, msn_ns_connected, gc );
47        if( md->fd < 0 )
48        {
49                hide_login_progress( gc, "Could not connect to server" );
50                signoff( gc );
51        }
52        else
53        {
54                md->gc = gc;
55                md->away_state = msn_away_state_list;
56               
57                msn_connections = g_slist_append( msn_connections, gc );
58        }
59}
60
61static void msn_close( struct gaim_connection *gc )
62{
63        struct msn_data *md = gc->proto_data;
64        GSList *l;
65       
66        if( md->fd >= 0 )
67                closesocket( md->fd );
68       
69        if( md->handler )
70        {
71                if( md->handler->rxq ) g_free( md->handler->rxq );
72                if( md->handler->cmd_text ) g_free( md->handler->cmd_text );
73                g_free( md->handler );
74        }
75       
76        while( md->switchboards )
77                msn_sb_destroy( md->switchboards->data );
78       
79        if( md->msgq )
80        {
81                struct msn_message *m;
82               
83                for( l = md->msgq; l; l = l->next )
84                {
85                        m = l->data;
86               
87                        serv_got_crap( gc, "Warning: Closing down MSN connection with unsent message to %s, you'll have to resend it.", m->who );
88                        g_free( m->who );
89                        g_free( m->text );
90                        g_free( m );
91                }
92                g_slist_free( md->msgq );
93        }
94       
95        for( l = gc->permit; l; l = l->next )
96                g_free( l->data );
97        g_slist_free( gc->permit );
98       
99        for( l = gc->deny; l; l = l->next )
100                g_free( l->data );
101        g_slist_free( gc->deny );
102       
103        g_free( md );
104       
105        msn_connections = g_slist_remove( msn_connections, gc );
106}
107
108static int msn_send_im( struct gaim_connection *gc, char *who, char *message, int len, int away )
109{
110        struct msn_switchboard *sb;
111        struct msn_data *md = gc->proto_data;
112       
113        if( ( sb = msn_sb_by_handle( gc, who ) ) )
114        {
115                return( msn_sb_sendmessage( sb, message ) );
116        }
117        else
118        {
119                struct msn_message *m;
120                char buf[1024];
121               
122                /* Create a message. We have to arrange a usable switchboard, and send the message later. */
123                m = g_new0( struct msn_message, 1 );
124                m->who = g_strdup( who );
125                m->text = g_strdup( message );
126               
127                /* FIXME: *CHECK* the reliability of using spare sb's! */
128                if( ( sb = msn_sb_spare( gc ) ) )
129                {
130                        debug( "Trying to use a spare switchboard to message %s", who );
131                       
132                        sb->who = g_strdup( who );
133                        g_snprintf( buf, sizeof( buf ), "CAL %d %s\r\n", ++sb->trId, who );
134                        if( msn_sb_write( sb, buf, strlen( buf ) ) )
135                        {
136                                /* He/She should join the switchboard soon, let's queue the message. */
137                                sb->msgq = g_slist_append( sb->msgq, m );
138                                return( 1 );
139                        }
140                }
141               
142                debug( "Creating a new switchboard to message %s", who );
143               
144                /* If we reach this line, there was no spare switchboard, so let's make one. */
145                g_snprintf( buf, sizeof( buf ), "XFR %d SB\r\n", ++md->trId );
146                if( !msn_write( gc, buf, strlen( buf ) ) )
147                {
148                        g_free( m->who );
149                        g_free( m->text );
150                        g_free( m );
151                       
152                        return( 0 );
153                }
154               
155                /* And queue the message to md. We'll pick it up when the switchboard comes up. */
156                md->msgq = g_slist_append( md->msgq, m );
157               
158                /* FIXME: If the switchboard creation fails, the message will not be sent. */
159               
160                return( 1 );
161        }
162       
163        return( 0 );
164}
165
166static GList *msn_away_states( struct gaim_connection *gc )
167{
168        GList *l = NULL;
169        int i;
170       
171        for( i = 0; msn_away_state_list[i].number > -1; i ++ )
172                l = g_list_append( l, (void*) msn_away_state_list[i].name );
173       
174        return( l );
175}
176
177static char *msn_get_status_string( struct gaim_connection *gc, int number )
178{
179        const struct msn_away_state *st = msn_away_state_by_number( number );
180       
181        if( st )
182                return( (char*) st->name );
183        else
184                return( "" );
185}
186
187static void msn_set_away( struct gaim_connection *gc, char *state, char *message )
188{
189        char buf[1024];
190        struct msn_data *md = gc->proto_data;
191        const struct msn_away_state *st;
192       
193        if( strcmp( state, GAIM_AWAY_CUSTOM ) == 0 )
194                st = msn_away_state_by_name( "Away" );
195        else
196                st = msn_away_state_by_name( state );
197       
198        if( !st ) st = msn_away_state_list;
199        md->away_state = st;
200       
201        g_snprintf( buf, sizeof( buf ), "CHG %d %s\r\n", ++md->trId, st->code );
202        msn_write( gc, buf, strlen( buf ) );
203}
204
205static void msn_set_info( struct gaim_connection *gc, char *info )
206{
207        int i;
208        char buf[1024], *fn, *s;
209        struct msn_data *md = gc->proto_data;
210       
211        if( strlen( info ) > 129 )
212        {
213                do_error_dialog( gc, "Maximum name length exceeded", "MSN" );
214                return;
215        }
216       
217        /* Of course we could use http_encode() here, but when we encode
218           every character, the server is less likely to complain about the
219           chosen name. However, the MSN server doesn't seem to like escaped
220           non-ASCII chars, so we keep those unescaped. */
221        s = fn = g_new0( char, strlen( info ) * 3 + 1 );
222        for( i = 0; info[i]; i ++ )
223                if( info[i] & 128 )
224                {
225                        *s = info[i];
226                        s ++;
227                }
228                else
229                {
230                        g_snprintf( s, 4, "%%%02X", info[i] );
231                        s += 3;
232                }
233       
234        g_snprintf( buf, sizeof( buf ), "REA %d %s %s\r\n", ++md->trId, gc->username, fn );
235        msn_write( gc, buf, strlen( buf ) );
236        g_free( fn );
237}
238
239static void msn_get_info(struct gaim_connection *gc, char *who) 
240{
241        /* Just make an URL and let the user fetch the info */
242        serv_got_crap( gc, "%s\n%s: %s%s", _("User Info"), _("For now, fetch yourself"), PROFILE_URL, who );
243}
244
245static void msn_add_buddy( struct gaim_connection *gc, char *who )
246{
247        msn_buddy_list_add( gc, "FL", who, who );
248}
249
250static void msn_remove_buddy( struct gaim_connection *gc, char *who, char *group )
251{
252        msn_buddy_list_remove( gc, "FL", who );
253}
254
255static int msn_chat_send( struct gaim_connection *gc, int id, char *message )
256{
257        struct msn_switchboard *sb = msn_sb_by_id( gc, id );
258       
259        if( sb )
260                return( msn_sb_sendmessage( sb, message ) );
261        else
262                return( 0 );
263}
264
265static void msn_chat_invite( struct gaim_connection *gc, int id, char *msg, char *who )
266{
267        struct msn_switchboard *sb = msn_sb_by_id( gc, id );
268        char buf[1024];
269       
270        if( sb )
271        {
272                g_snprintf( buf, sizeof( buf ), "CAL %d %s\r\n", ++sb->trId, who );
273                msn_sb_write( sb, buf, strlen( buf ) );
274        }
275}
276
277static void msn_chat_leave( struct gaim_connection *gc, int id )
278{
279        struct msn_switchboard *sb = msn_sb_by_id( gc, id );
280       
281        if( sb )
282                msn_sb_write( sb, "OUT\r\n", 5 );
283}
284
285static int msn_chat_open( struct gaim_connection *gc, char *who )
286{
287        struct msn_switchboard *sb;
288        struct msn_data *md = gc->proto_data;
289        char buf[1024];
290       
291        if( ( sb = msn_sb_by_handle( gc, who ) ) )
292        {
293                debug( "Converting existing switchboard to %s to a groupchat", who );
294                msn_sb_to_chat( sb );
295                return( 1 );
296        }
297        else
298        {
299                struct msn_message *m;
300               
301                if( ( sb = msn_sb_spare( gc ) ) )
302                {
303                        debug( "Trying to reuse an existing switchboard as a groupchat with %s", who );
304                        g_snprintf( buf, sizeof( buf ), "CAL %d %s\r\n", ++sb->trId, who );
305                        if( msn_sb_write( sb, buf, strlen( buf ) ) )
306                        {
307                                msn_sb_to_chat( sb );
308                                return( 1 );
309                        }
310                }
311               
312                /* If the stuff above failed for some reason: */
313                debug( "Creating a new switchboard to groupchat with %s", who );
314               
315                /* Request a new switchboard. */
316                g_snprintf( buf, sizeof( buf ), "XFR %d SB\r\n", ++md->trId );
317                if( !msn_write( gc, buf, strlen( buf ) ) )
318                        return( 0 );
319               
320                /* Create a magic message. This is quite hackish, but who cares? :-P */
321                m = g_new0( struct msn_message, 1 );
322                m->who = g_strdup( who );
323                m->text = g_strdup( GROUPCHAT_SWITCHBOARD_MESSAGE );
324               
325                /* Queue the magic message and cross your fingers. */
326                md->msgq = g_slist_append( md->msgq, m );
327               
328                return( 1 );
329        }
330       
331        return( 0 );
332}
333
334static void msn_keepalive( struct gaim_connection *gc )
335{
336        msn_write( gc, "PNG\r\n", strlen( "PNG\r\n" ) );
337}
338
339static void msn_add_permit( struct gaim_connection *gc, char *who )
340{
341        msn_buddy_list_add( gc, "AL", who, who );
342}
343
344static void msn_rem_permit( struct gaim_connection *gc, char *who )
345{
346        msn_buddy_list_remove( gc, "AL", who );
347}
348
349static void msn_add_deny( struct gaim_connection *gc, char *who )
350{
351        struct msn_switchboard *sb;
352       
353        msn_buddy_list_add( gc, "BL", who, who );
354       
355        /* If there's still a conversation with this person, close it. */
356        if( ( sb = msn_sb_by_handle( gc, who ) ) )
357        {
358                msn_sb_destroy( sb );
359        }
360}
361
362static void msn_rem_deny( struct gaim_connection *gc, char *who )
363{
364        msn_buddy_list_remove( gc, "BL", who );
365}
366
367static int msn_send_typing( struct gaim_connection *gc, char *who, int typing )
368{
369        if( typing )
370                return( msn_send_im( gc, who, TYPING_NOTIFICATION_MESSAGE, strlen( TYPING_NOTIFICATION_MESSAGE ), 0 ) );
371        else
372                return( 1 );
373}
374
375void msn_init()
376{
377        struct prpl *ret = g_new0(struct prpl, 1);
378        ret->name = "msn";
379        ret->login = msn_login;
380        ret->close = msn_close;
381        ret->send_im = msn_send_im;
382        ret->away_states = msn_away_states;
383        ret->get_status_string = msn_get_status_string;
384        ret->set_away = msn_set_away;
385        ret->set_info = msn_set_info;
386        ret->get_info = msn_get_info;
387        ret->add_buddy = msn_add_buddy;
388        ret->remove_buddy = msn_remove_buddy;
389        ret->chat_send = msn_chat_send;
390        ret->chat_invite = msn_chat_invite;
391        ret->chat_leave = msn_chat_leave;
392        ret->chat_open = msn_chat_open;
393        ret->keepalive = msn_keepalive;
394        ret->add_permit = msn_add_permit;
395        ret->rem_permit = msn_rem_permit;
396        ret->add_deny = msn_add_deny;
397        ret->rem_deny = msn_rem_deny;
398        ret->send_typing = msn_send_typing;
399        ret->cmp_buddynames = g_strcasecmp;
400
401        register_protocol(ret);
402}
Note: See TracBrowser for help on using the repository browser.