source: protocols/msn/msn.c @ 0147ca9

Last change on this file since 0147ca9 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.3 KB
Line 
1  /********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
4  * Copyright 2002-2013 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 "soap.h"
28#include "msn.h"
29
30int msn_chat_id;
31GSList *msn_connections;
32GSList *msn_switchboards;
33
34static char *set_eval_display_name( set_t *set, char *value );
35
36static void msn_init( account_t *acc )
37{
38        set_t *s;
39       
40        s = set_add( &acc->set, "display_name", NULL, set_eval_display_name, acc );
41        s->flags |= ACC_SET_NOSAVE | ACC_SET_ONLINE_ONLY;
42       
43        set_add( &acc->set, "mail_notifications", "false", set_eval_bool, acc );
44        set_add( &acc->set, "switchboard_keepalives", "false", set_eval_bool, acc );
45       
46        acc->flags |= ACC_FLAG_AWAY_MESSAGE | ACC_FLAG_STATUS_MESSAGE |
47                      ACC_FLAG_HANDLE_DOMAINS;
48}
49
50static void msn_login( account_t *acc )
51{
52        struct im_connection *ic = imcb_new( acc );
53        struct msn_data *md = g_new0( struct msn_data, 1 );
54       
55        ic->proto_data = md;
56        ic->flags |= OPT_PONGS | OPT_PONGED;
57       
58        if( strchr( acc->user, '@' ) == NULL )
59        {
60                imcb_error( ic, "Invalid account name" );
61                imc_logout( ic, FALSE );
62                return;
63        }
64       
65        md->ic = ic;
66        md->away_state = msn_away_state_list;
67        md->domaintree = g_tree_new( msn_domaintree_cmp );
68        md->ns->fd = -1;
69       
70        msn_connections = g_slist_prepend( msn_connections, ic );
71       
72        imcb_log( ic, "Connecting" );
73        msn_ns_connect( ic, md->ns, MSN_NS_HOST, MSN_NS_PORT );
74}
75
76static void msn_logout( struct im_connection *ic )
77{
78        struct msn_data *md = ic->proto_data;
79        GSList *l;
80        int i;
81       
82        if( md )
83        {
84                /** Disabling MSN ft support for now.
85                while( md->filetransfers ) {
86                        imcb_file_canceled( md->filetransfers->data, "Closing connection" );
87                }
88                */
89               
90                msn_ns_close( md->ns );
91               
92                while( md->switchboards )
93                        msn_sb_destroy( md->switchboards->data );
94               
95                msn_msgq_purge( ic, &md->msgq );
96                msn_soapq_flush( ic, FALSE );
97               
98                for( i = 0; i < sizeof( md->tokens ) / sizeof( md->tokens[0] ); i ++ )
99                        g_free( md->tokens[i] );
100                g_free( md->lock_key );
101                g_free( md->pp_policy );
102                g_free( md->uuid );
103               
104                while( md->groups )
105                {
106                        struct msn_group *mg = md->groups->data;
107                        g_free( mg->id );
108                        g_free( mg->name );
109                        g_free( mg );
110                        md->groups = g_slist_remove( md->groups, mg );
111                }
112               
113                g_free( md->profile_rid );
114               
115                if( md->domaintree )
116                        g_tree_destroy( md->domaintree );
117                md->domaintree = NULL;
118               
119                while( md->grpq )
120                {
121                        struct msn_groupadd *ga = md->grpq->data;
122                        g_free( ga->group );
123                        g_free( ga->who );
124                        g_free( ga );
125                        md->grpq = g_slist_remove( md->grpq, ga );
126                }
127               
128                g_free( md );
129        }
130       
131        for( l = ic->permit; l; l = l->next )
132                g_free( l->data );
133        g_slist_free( ic->permit );
134       
135        for( l = ic->deny; l; l = l->next )
136                g_free( l->data );
137        g_slist_free( ic->deny );
138       
139        msn_connections = g_slist_remove( msn_connections, ic );
140}
141
142static int msn_buddy_msg( struct im_connection *ic, char *who, char *message, int away )
143{
144        struct bee_user *bu = bee_user_by_handle( ic->bee, ic, who );
145        struct msn_buddy_data *bd = bu ? bu->data : NULL;
146        struct msn_switchboard *sb;
147       
148#ifdef DEBUG
149        if( strcmp( who, "raw" ) == 0 )
150        {
151                msn_ns_write( ic, -1, "%s\r\n", message );
152        }
153        else
154#endif
155        if( bd && bd->flags & MSN_BUDDY_FED )
156        {
157                msn_ns_sendmessage( ic, bu, message );
158        }
159        else if( ( sb = msn_sb_by_handle( ic, who ) ) )
160        {
161                return( msn_sb_sendmessage( sb, message ) );
162        }
163        else
164        {
165                struct msn_message *m;
166               
167                /* Create a message. We have to arrange a usable switchboard, and send the message later. */
168                m = g_new0( struct msn_message, 1 );
169                m->who = g_strdup( who );
170                m->text = g_strdup( message );
171               
172                return msn_sb_write_msg( ic, m );
173        }
174       
175        return( 0 );
176}
177
178static GList *msn_away_states( struct im_connection *ic )
179{
180        static GList *l = NULL;
181        int i;
182       
183        if( l == NULL )
184                for( i = 0; *msn_away_state_list[i].code; i ++ )
185                        if( *msn_away_state_list[i].name )
186                                l = g_list_append( l, (void*) msn_away_state_list[i].name );
187       
188        return l;
189}
190
191static void msn_set_away( struct im_connection *ic, char *state, char *message )
192{
193        char *uux;
194        struct msn_data *md = ic->proto_data;
195       
196        if( state == NULL )
197                md->away_state = msn_away_state_list;
198        else if( ( md->away_state = msn_away_state_by_name( state ) ) == NULL )
199                md->away_state = msn_away_state_list + 1;
200       
201        if( !msn_ns_write( ic, -1, "CHG %d %s %d:%02d\r\n", ++md->trId, md->away_state->code, MSN_CAP1, MSN_CAP2 ) )
202                return;
203       
204        uux = g_markup_printf_escaped( "<EndpointData><Capabilities>%d:%02d"
205                                       "</Capabilities></EndpointData>",
206                                       MSN_CAP1, MSN_CAP2 );
207        msn_ns_write( ic, -1, "UUX %d %zd\r\n%s", ++md->trId, strlen( uux ), uux );
208        g_free( uux );
209       
210        uux = g_markup_printf_escaped( "<PrivateEndpointData><EpName>%s</EpName>"
211                                       "<Idle>%s</Idle><ClientType>%d</ClientType>"
212                                       "<State>%s</State></PrivateEndpointData>",
213                                       md->uuid,
214                                       strcmp( md->away_state->code, "IDL" ) ? "false" : "true",
215                                       1, /* ? */
216                                       md->away_state->code );
217        msn_ns_write( ic, -1, "UUX %d %zd\r\n%s", ++md->trId, strlen( uux ), uux );
218        g_free( uux );
219       
220        uux = g_markup_printf_escaped( "<Data><DDP></DDP><PSM>%s</PSM>"
221                                       "<CurrentMedia></CurrentMedia>"
222                                       "<MachineGuid>%s</MachineGuid></Data>",
223                                       message ? message : "", md->uuid );
224        msn_ns_write( ic, -1, "UUX %d %zd\r\n%s", ++md->trId, strlen( uux ), uux );
225        g_free( uux );
226}
227
228static void msn_get_info(struct im_connection *ic, char *who) 
229{
230        /* Just make an URL and let the user fetch the info */
231        imcb_log( ic, "%s\n%s: %s%s", _("User Info"), _("For now, fetch yourself"), PROFILE_URL, who );
232}
233
234static void msn_add_buddy( struct im_connection *ic, char *who, char *group )
235{
236        struct bee_user *bu = bee_user_by_handle( ic->bee, ic, who );
237       
238        msn_buddy_list_add( ic, MSN_BUDDY_FL, who, who, group );
239        if( bu && bu->group )
240                msn_buddy_list_remove( ic, MSN_BUDDY_FL, who, bu->group->name );
241}
242
243static void msn_remove_buddy( struct im_connection *ic, char *who, char *group )
244{
245        msn_buddy_list_remove( ic, MSN_BUDDY_FL, who, NULL );
246}
247
248static void msn_chat_msg( struct groupchat *c, char *message, int flags )
249{
250        struct msn_switchboard *sb = msn_sb_by_chat( c );
251       
252        if( sb )
253                msn_sb_sendmessage( sb, message );
254        /* FIXME: Error handling (although this can't happen unless something's
255           already severely broken) disappeared here! */
256}
257
258static void msn_chat_invite( struct groupchat *c, char *who, char *message )
259{
260        struct msn_switchboard *sb = msn_sb_by_chat( c );
261       
262        if( sb )
263                msn_sb_write( sb, "CAL %d %s\r\n", ++sb->trId, who );
264}
265
266static void msn_chat_leave( struct groupchat *c )
267{
268        struct msn_switchboard *sb = msn_sb_by_chat( c );
269       
270        if( sb )
271                msn_sb_write( sb, "OUT\r\n" );
272}
273
274static struct groupchat *msn_chat_with( struct im_connection *ic, char *who )
275{
276        struct msn_switchboard *sb;
277        struct groupchat *c = imcb_chat_new( ic, who );
278       
279        if( ( sb = msn_sb_by_handle( ic, who ) ) )
280        {
281                debug( "Converting existing switchboard to %s to a groupchat", who );
282                return msn_sb_to_chat( sb );
283        }
284        else
285        {
286                struct msn_message *m;
287               
288                /* Create a magic message. This is quite hackish, but who cares? :-P */
289                m = g_new0( struct msn_message, 1 );
290                m->who = g_strdup( who );
291                m->text = g_strdup( GROUPCHAT_SWITCHBOARD_MESSAGE );
292               
293                msn_sb_write_msg( ic, m );
294
295                return c;
296        }
297}
298
299static void msn_keepalive( struct im_connection *ic )
300{
301        msn_ns_write( ic, -1, "PNG\r\n" );
302}
303
304static void msn_add_permit( struct im_connection *ic, char *who )
305{
306        msn_buddy_list_add( ic, MSN_BUDDY_AL, who, who, NULL );
307}
308
309static void msn_rem_permit( struct im_connection *ic, char *who )
310{
311        msn_buddy_list_remove( ic, MSN_BUDDY_AL, who, NULL );
312}
313
314static void msn_add_deny( struct im_connection *ic, char *who )
315{
316        struct msn_switchboard *sb;
317       
318        msn_buddy_list_add( ic, MSN_BUDDY_BL, who, who, NULL );
319       
320        /* If there's still a conversation with this person, close it. */
321        if( ( sb = msn_sb_by_handle( ic, who ) ) )
322        {
323                msn_sb_destroy( sb );
324        }
325}
326
327static void msn_rem_deny( struct im_connection *ic, char *who )
328{
329        msn_buddy_list_remove( ic, MSN_BUDDY_BL, who, NULL );
330}
331
332static int msn_send_typing( struct im_connection *ic, char *who, int typing )
333{
334        struct bee_user *bu = bee_user_by_handle( ic->bee, ic, who );
335       
336        if( !( bu->flags & BEE_USER_ONLINE ) )
337                return 0;
338        else if( typing & OPT_TYPING )
339                return( msn_buddy_msg( ic, who, TYPING_NOTIFICATION_MESSAGE, 0 ) );
340        else
341                return 1;
342}
343
344static char *set_eval_display_name( set_t *set, char *value )
345{
346        account_t *acc = set->data;
347        struct im_connection *ic = acc->ic;
348        struct msn_data *md = ic->proto_data;
349       
350        if( md->flags & MSN_EMAIL_UNVERIFIED )
351                imcb_log( ic, "Warning: Your e-mail address is unverified. MSN doesn't allow "
352                              "changing your display name until your e-mail address is verified." );
353       
354        if( md->flags & MSN_GOT_PROFILE_DN )
355                msn_soap_profile_set_dn( ic, value );
356        else
357                msn_soap_addressbook_set_display_name( ic, value );
358       
359        return msn_ns_set_display_name( ic, value ) ? value : NULL;
360}
361
362static void msn_buddy_data_add( bee_user_t *bu )
363{
364        struct msn_data *md = bu->ic->proto_data;
365        struct msn_buddy_data *bd;
366        char *handle;
367       
368        bd = bu->data = g_new0( struct msn_buddy_data, 1 );
369        g_tree_insert( md->domaintree, bu->handle, bu );
370       
371        for( handle = bu->handle; isdigit( *handle ); handle ++ );
372        if( *handle == ':' )
373        {
374                /* Pass a nick hint so hopefully the stupid numeric prefix
375                   won't show up to the user.  */
376                char *s = strchr( ++handle, '@' );
377                if( s )
378                {
379                        handle = g_strndup( handle, s - handle );
380                        imcb_buddy_nick_hint( bu->ic, bu->handle, handle );
381                        g_free( handle );
382                }
383               
384                bd->flags |= MSN_BUDDY_FED;
385        }
386}
387
388static void msn_buddy_data_free( bee_user_t *bu )
389{
390        struct msn_data *md = bu->ic->proto_data;
391        struct msn_buddy_data *bd = bu->data;
392       
393        g_free( bd->cid );
394        g_free( bd );
395       
396        g_tree_remove( md->domaintree, bu->handle );
397}
398
399GList *msn_buddy_action_list( bee_user_t *bu )
400{
401        static GList *ret = NULL;
402       
403        if( ret == NULL )
404        {
405                static const struct buddy_action ba[2] = {
406                        { "NUDGE", "Draw attention" },
407                };
408               
409                ret = g_list_prepend( ret, (void*) ba + 0 );
410        }
411       
412        return ret;
413}
414
415void *msn_buddy_action( struct bee_user *bu, const char *action, char * const args[], void *data )
416{
417        if( g_strcasecmp( action, "NUDGE" ) == 0 )
418                msn_buddy_msg( bu->ic, bu->handle, NUDGE_MESSAGE, 0 );
419       
420        return NULL;
421}
422
423void msn_initmodule()
424{
425        struct prpl *ret = g_new0(struct prpl, 1);
426       
427        ret->name = "msn";
428        ret->mms = 1409;         /* this guess taken from libotr UPGRADING file */
429        ret->login = msn_login;
430        ret->init = msn_init;
431        ret->logout = msn_logout;
432        ret->buddy_msg = msn_buddy_msg;
433        ret->away_states = msn_away_states;
434        ret->set_away = msn_set_away;
435        ret->get_info = msn_get_info;
436        ret->add_buddy = msn_add_buddy;
437        ret->remove_buddy = msn_remove_buddy;
438        ret->chat_msg = msn_chat_msg;
439        ret->chat_invite = msn_chat_invite;
440        ret->chat_leave = msn_chat_leave;
441        ret->chat_with = msn_chat_with;
442        ret->keepalive = msn_keepalive;
443        ret->add_permit = msn_add_permit;
444        ret->rem_permit = msn_rem_permit;
445        ret->add_deny = msn_add_deny;
446        ret->rem_deny = msn_rem_deny;
447        ret->send_typing = msn_send_typing;
448        ret->handle_cmp = g_strcasecmp;
449        ret->buddy_data_add = msn_buddy_data_add;
450        ret->buddy_data_free = msn_buddy_data_free;
451        ret->buddy_action_list = msn_buddy_action_list;
452        ret->buddy_action = msn_buddy_action;
453       
454        //ret->transfer_request = msn_ftp_transfer_request;
455
456        register_protocol(ret);
457}
Note: See TracBrowser for help on using the repository browser.