source: protocols/msn/msn_util.c @ 4452e69

Last change on this file since 4452e69 was 4452e69, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-08-14T13:06:11Z

Allow changing the display_name, now permanently!

  • Property mode set to 100644
File size: 12.2 KB
Line 
1  /********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
4  * Copyright 2002-2010 Wilmer van der Gaast and others                *
5  \********************************************************************/
6
7/* MSN module - Miscellaneous 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 "nogaim.h"
27#include "msn.h"
28#include "md5.h"
29#include <ctype.h>
30
31int msn_write( struct im_connection *ic, char *s, int len )
32{
33        struct msn_data *md = ic->proto_data;
34        int st;
35       
36        if( getenv( "BITLBEE_DEBUG" ) )
37        {
38                write( 2, "->NS:", 5 );
39                write( 2, s, len );
40        }
41       
42        st = write( md->fd, s, len );
43        if( st != len )
44        {
45                imcb_error( ic, "Short write() to main server" );
46                imc_logout( ic, TRUE );
47                return 0;
48        }
49       
50        return 1;
51}
52
53int msn_logged_in( struct im_connection *ic )
54{
55        imcb_connected( ic );
56       
57        return( 0 );
58}
59
60int msn_buddy_list_add( struct im_connection *ic, const char *list, const char *who, const char *realname_, const char *group )
61{
62        struct msn_data *md = ic->proto_data;
63        char buf[1024], realname[strlen(realname_)*3+1], groupid[8];
64       
65        *groupid = '\0';
66        if( group )
67        {
68                int i;
69                for( i = 0; i < md->groupcount; i ++ )
70                        if( g_strcasecmp( md->grouplist[i], group ) == 0 )
71                        {
72                                g_snprintf( groupid, sizeof( groupid ), " %d", i );
73                                break;
74                        }
75               
76                if( *groupid == '\0' )
77                {
78                        /* Have to create this group, it doesn't exist yet. */
79                        struct msn_groupadd *ga;
80                        GSList *l;
81                       
82                        for( l = md->grpq; l; l = l->next )
83                        {
84                                ga = l->data;
85                                if( g_strcasecmp( ga->group, group ) == 0 )
86                                        break;
87                        }
88                       
89                        ga = g_new0( struct msn_groupadd, 1 );
90                        ga->who = g_strdup( who );
91                        ga->group = g_strdup( group );
92                        md->grpq = g_slist_prepend( md->grpq, ga );
93                       
94                        if( l == NULL )
95                        {
96                                char groupname[strlen(group)+1];
97                                strcpy( groupname, group );
98                                http_encode( groupname );
99                                g_snprintf( buf, sizeof( buf ), "ADG %d %s %d\r\n", ++md->trId, groupname, 0 );
100                                return msn_write( ic, buf, strlen( buf ) );
101                        }
102                        else
103                        {
104                                /* This can happen if the user's doing lots of adds to a
105                                   new group at once; we're still waiting for the server
106                                   to confirm group creation. */
107                                return 1;
108                        }
109                }
110        }
111       
112        strcpy( realname, realname_ );
113        http_encode( realname );
114        g_snprintf( buf, sizeof( buf ), "ADD %d %s %s %s%s\r\n", ++md->trId, list, who, realname, groupid );
115       
116        return msn_write( ic, buf, strlen( buf ) );
117}
118
119int msn_buddy_list_remove( struct im_connection *ic, char *list, const char *who, const char *group )
120{
121        struct msn_data *md = ic->proto_data;
122        char buf[1024], groupid[8];
123       
124        *groupid = '\0';
125        if( group )
126        {
127                int i;
128                for( i = 0; i < md->groupcount; i ++ )
129                        if( g_strcasecmp( md->grouplist[i], group ) == 0 )
130                        {
131                                g_snprintf( groupid, sizeof( groupid ), " %d", i );
132                                break;
133                        }
134        }
135       
136        g_snprintf( buf, sizeof( buf ), "REM %d %s %s%s\r\n", ++md->trId, list, who, groupid );
137        if( msn_write( ic, buf, strlen( buf ) ) )
138                return( 1 );
139       
140        return( 0 );
141}
142
143struct msn_buddy_ask_data
144{
145        struct im_connection *ic;
146        char *handle;
147        char *realname;
148};
149
150static void msn_buddy_ask_yes( void *data )
151{
152        struct msn_buddy_ask_data *bla = data;
153       
154        msn_buddy_list_add( bla->ic, "AL", bla->handle, bla->realname, NULL );
155       
156        imcb_ask_add( bla->ic, bla->handle, NULL );
157       
158        g_free( bla->handle );
159        g_free( bla->realname );
160        g_free( bla );
161}
162
163static void msn_buddy_ask_no( void *data )
164{
165        struct msn_buddy_ask_data *bla = data;
166       
167        msn_buddy_list_add( bla->ic, "BL", bla->handle, bla->realname, NULL );
168       
169        g_free( bla->handle );
170        g_free( bla->realname );
171        g_free( bla );
172}
173
174void msn_buddy_ask( struct im_connection *ic, char *handle, char *realname )
175{
176        struct msn_buddy_ask_data *bla = g_new0( struct msn_buddy_ask_data, 1 );
177        char buf[1024];
178       
179        bla->ic = ic;
180        bla->handle = g_strdup( handle );
181        bla->realname = g_strdup( realname );
182       
183        g_snprintf( buf, sizeof( buf ),
184                    "The user %s (%s) wants to add you to his/her buddy list.",
185                    handle, realname );
186        imcb_ask( ic, buf, bla, msn_buddy_ask_yes, msn_buddy_ask_no );
187}
188
189char *msn_findheader( char *text, char *header, int len )
190{
191        int hlen = strlen( header ), i;
192        char *ret;
193       
194        if( len == 0 )
195                len = strlen( text );
196       
197        i = 0;
198        while( ( i + hlen ) < len )
199        {
200                /* Maybe this is a bit over-commented, but I just hate this part... */
201                if( g_strncasecmp( text + i, header, hlen ) == 0 )
202                {
203                        /* Skip to the (probable) end of the header */
204                        i += hlen;
205                       
206                        /* Find the first non-[: \t] character */
207                        while( i < len && ( text[i] == ':' || text[i] == ' ' || text[i] == '\t' ) ) i ++;
208                       
209                        /* Make sure we're still inside the string */
210                        if( i >= len ) return( NULL );
211                       
212                        /* Save the position */
213                        ret = text + i;
214                       
215                        /* Search for the end of this line */
216                        while( i < len && text[i] != '\r' && text[i] != '\n' ) i ++;
217                       
218                        /* Make sure we're still inside the string */
219                        if( i >= len ) return( NULL );
220                       
221                        /* Copy the found data */
222                        return( g_strndup( ret, text + i - ret ) );
223                }
224               
225                /* This wasn't the header we were looking for, skip to the next line. */
226                while( i < len && ( text[i] != '\r' && text[i] != '\n' ) ) i ++;
227                while( i < len && ( text[i] == '\r' || text[i] == '\n' ) ) i ++;
228               
229                /* End of headers? */
230                if( ( i >= 4 && strncmp( text + i - 4, "\r\n\r\n", 4 ) == 0 ) ||
231                    ( i >= 2 && ( strncmp( text + i - 2, "\n\n", 2 ) == 0 ||   
232                                  strncmp( text + i - 2, "\r\r", 2 ) == 0 ) ) )
233                {
234                        break;
235                }
236        }
237       
238        return( NULL );
239}
240
241/* *NOT* thread-safe, but that's not a problem for now... */
242char **msn_linesplit( char *line )
243{
244        static char **ret = NULL;
245        static int size = 3;
246        int i, n = 0;
247       
248        if( ret == NULL )
249                ret = g_new0( char*, size );
250       
251        for( i = 0; line[i] && line[i] == ' '; i ++ );
252        if( line[i] )
253        {
254                ret[n++] = line + i;
255                for( i ++; line[i]; i ++ )
256                {
257                        if( line[i] == ' ' )
258                                line[i] = 0;
259                        else if( line[i] != ' ' && !line[i-1] )
260                                ret[n++] = line + i;
261                       
262                        if( n >= size )
263                                ret = g_renew( char*, ret, size += 2 );
264                }
265        }
266        ret[n] = NULL;
267       
268        return( ret );
269}
270
271/* This one handles input from a MSN Messenger server. Both the NS and SB servers usually give
272   commands, but sometimes they give additional data (payload). This function tries to handle
273   this all in a nice way and send all data to the right places. */
274
275/* Return values: -1: Read error, abort connection.
276                   0: Command reported error; Abort *immediately*. (The connection does not exist anymore)
277                   1: OK */
278
279int msn_handler( struct msn_handler_data *h )
280{
281        int st;
282       
283        h->rxq = g_renew( char, h->rxq, h->rxlen + 1024 );
284        st = read( h->fd, h->rxq + h->rxlen, 1024 );
285        h->rxlen += st;
286       
287        if( st <= 0 )
288                return( -1 );
289       
290        if( getenv( "BITLBEE_DEBUG" ) )
291        {
292                write( 2, "->C:", 4 );
293                write( 2, h->rxq + h->rxlen - st, st );
294        }
295       
296        while( st )
297        {
298                int i;
299               
300                if( h->msglen == 0 )
301                {
302                        for( i = 0; i < h->rxlen; i ++ )
303                        {
304                                if( h->rxq[i] == '\r' || h->rxq[i] == '\n' )
305                                {
306                                        char *cmd_text, **cmd;
307                                        int count;
308                                       
309                                        cmd_text = g_strndup( h->rxq, i );
310                                        cmd = msn_linesplit( cmd_text );
311                                        for( count = 0; cmd[count]; count ++ );
312                                        st = h->exec_command( h->data, cmd, count );
313                                        g_free( cmd_text );
314                                       
315                                        /* If the connection broke, don't continue. We don't even exist anymore. */
316                                        if( !st )
317                                                return( 0 );
318                                       
319                                        if( h->msglen )
320                                                h->cmd_text = g_strndup( h->rxq, i );
321                                       
322                                        /* Skip to the next non-emptyline */
323                                        while( i < h->rxlen && ( h->rxq[i] == '\r' || h->rxq[i] == '\n' ) ) i ++;
324                                       
325                                        break;
326                                }
327                        }
328                       
329                        /* If we reached the end of the buffer, there's still an incomplete command there.
330                           Return and wait for more data. */
331                        if( i == h->rxlen && h->rxq[i-1] != '\r' && h->rxq[i-1] != '\n' )
332                                break;
333                }
334                else
335                {
336                        char *msg, **cmd;
337                        int count;
338                       
339                        /* Do we have the complete message already? */
340                        if( h->msglen > h->rxlen )
341                                break;
342                       
343                        msg = g_strndup( h->rxq, h->msglen );
344                        cmd = msn_linesplit( h->cmd_text );
345                        for( count = 0; cmd[count]; count ++ );
346                       
347                        st = h->exec_message( h->data, msg, h->msglen, cmd, count );
348                        g_free( msg );
349                        g_free( h->cmd_text );
350                        h->cmd_text = NULL;
351                       
352                        if( !st )
353                                return( 0 );
354                       
355                        i = h->msglen;
356                        h->msglen = 0;
357                }
358               
359                /* More data after this block? */
360                if( i < h->rxlen )
361                {
362                        char *tmp;
363                       
364                        tmp = g_memdup( h->rxq + i, h->rxlen - i );
365                        g_free( h->rxq );
366                        h->rxq = tmp;
367                        h->rxlen -= i;
368                        i = 0;
369                }
370                else
371                /* If not, reset the rx queue and get lost. */
372                {
373                        g_free( h->rxq );
374                        h->rxq = g_new0( char, 1 );
375                        h->rxlen = 0;
376                        return( 1 );
377                }
378        }
379       
380        return( 1 );
381}
382
383void msn_msgq_purge( struct im_connection *ic, GSList **list )
384{
385        struct msn_message *m;
386        GString *ret;
387        GSList *l;
388        int n = 0;
389       
390        l = *list;
391        if( l == NULL )
392                return;
393       
394        m = l->data;
395        ret = g_string_sized_new( 1024 );
396        g_string_printf( ret, "Warning: Cleaning up MSN (switchboard) connection with unsent "
397                              "messages to %s:", m->who ? m->who : "unknown recipient" );
398       
399        while( l )
400        {
401                m = l->data;
402               
403                if( strncmp( m->text, "\r\r\r", 3 ) != 0 )
404                {
405                        g_string_append_printf( ret, "\n%s", m->text );
406                        n ++;
407                }
408               
409                g_free( m->who );
410                g_free( m->text );
411                g_free( m );
412               
413                l = l->next;
414        }
415        g_slist_free( *list );
416        *list = NULL;
417       
418        if( n > 0 )
419                imcb_log( ic, "%s", ret->str );
420        g_string_free( ret, TRUE );
421}
422
423/* Copied and heavily modified from http://tmsnc.sourceforge.net/chl.c */
424char *msn_p11_challenge( char *challenge )
425{
426        char *output, buf[256];
427        md5_state_t md5c;
428        unsigned char md5Hash[16], *newHash;
429        unsigned int *md5Parts, *chlStringParts, newHashParts[5];
430        long long nHigh = 0, nLow = 0;
431        int i, n;
432
433        /* Create the MD5 hash */
434        md5_init(&md5c);
435        md5_append(&md5c, (unsigned char*) challenge, strlen(challenge));
436        md5_append(&md5c, (unsigned char*) MSNP11_PROD_KEY, strlen(MSNP11_PROD_KEY));
437        md5_finish(&md5c, md5Hash);
438
439        /* Split it into four integers */
440        md5Parts = (unsigned int *)md5Hash;
441        for (i = 0; i < 4; i ++)
442        { 
443                md5Parts[i] = GUINT32_TO_LE(md5Parts[i]);
444               
445                /* & each integer with 0x7FFFFFFF */
446                /* and save one unmodified array for later */
447                newHashParts[i] = md5Parts[i];
448                md5Parts[i] &= 0x7FFFFFFF;
449        }
450       
451        /* make a new string and pad with '0' */
452        n = g_snprintf(buf, sizeof(buf)-5, "%s%s00000000", challenge, MSNP11_PROD_ID);
453        /* truncate at an 8-byte boundary */
454        buf[n&=~7] = '\0';
455       
456        /* split into integers */
457        chlStringParts = (unsigned int *)buf;
458       
459        /* this is magic */
460        for (i = 0; i < (n / 4) - 1; i += 2)
461        {
462                long long temp;
463
464                chlStringParts[i]   = GUINT32_TO_LE(chlStringParts[i]);
465                chlStringParts[i+1] = GUINT32_TO_LE(chlStringParts[i+1]);
466
467                temp  = (md5Parts[0] * (((0x0E79A9C1 * (long long)chlStringParts[i]) % 0x7FFFFFFF)+nHigh) + md5Parts[1])%0x7FFFFFFF;
468                nHigh = (md5Parts[2] * (((long long)chlStringParts[i+1]+temp) % 0x7FFFFFFF) + md5Parts[3]) % 0x7FFFFFFF;
469                nLow  = nLow + nHigh + temp;
470        }
471        nHigh = (nHigh+md5Parts[1]) % 0x7FFFFFFF;
472        nLow = (nLow+md5Parts[3]) % 0x7FFFFFFF;
473       
474        newHashParts[0] ^= nHigh;
475        newHashParts[1] ^= nLow;
476        newHashParts[2] ^= nHigh;
477        newHashParts[3] ^= nLow;
478       
479        /* swap more bytes if big endian */
480        for (i = 0; i < 4; i ++)
481                newHashParts[i] = GUINT32_TO_LE(newHashParts[i]); 
482       
483        /* make a string of the parts */
484        newHash = (unsigned char *)newHashParts;
485       
486        /* convert to hexadecimal */
487        output = g_new(char, 33);
488        for (i = 0; i < 16; i ++)
489                sprintf(output + i * 2, "%02x", newHash[i]);
490       
491        return output;
492}
493
494gint msn_domaintree_cmp( gconstpointer a_, gconstpointer b_ )
495{
496        const char *a = a_, *b = b_;
497        gint ret;
498       
499        if( !( a = strchr( a, '@' ) ) || !( b = strchr( b, '@' ) ) ||
500            ( ret = strcmp( a, b ) ) == 0 )
501                ret = strcmp( a_, b_ );
502       
503        return ret;
504}
Note: See TracBrowser for help on using the repository browser.