source: skype/skype.c @ 349ee4a

Last change on this file since 349ee4a was 349ee4a, checked in by VMiklos <vmiklos@…>, at 2007-10-06T20:53:32Z

fix agressive groupchat mechanism
since 55f2d43, a groupchat was created even for 2 people. this is wrong. we
should not create a group chat when receiving an ACTIVEMEMBERS, only when the
CHAT's TYPE is MULTICHAT

  • Property mode set to 100644
File size: 15.0 KB
Line 
1/*
2 *  skype.c - Skype plugin for BitlBee
3 *
4 *  Copyright (c) 2007 by Miklos Vajna <vmiklos@frugalware.org>
5 *
6 *  Several ideas are used from the BitlBee Jabber plugin, which is
7 *
8 *  Copyright (c) 2006 by Wilmer van der Gaast <wilmer@gaast.net>
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
21 *  along with this program; if not, write to the Free Software
22 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
23 *  USA.
24 */
25
26#include <stdio.h>
27#include <poll.h>
28#include <bitlbee.h>
29#include <glib.h>
30
31#define SKYPE_PORT_DEFAULT "2727"
32
33/*
34 * Enumerations
35 */
36
37typedef enum
38{
39        SKYPE_CALL_RINGING = 1,
40        SKYPE_CALL_MISSED
41} skype_call_status;
42
43typedef enum
44{
45        SKYPE_FILETRANSFER_NEW = 1,
46        SKYPE_FILETRANSFER_FAILED
47} skype_filetransfer_status;
48
49/*
50 * Structures
51 */
52
53struct skype_data
54{
55        struct im_connection *ic;
56        char *username;
57        /* The effective file descriptor. We store it here so any function can
58         * write() to it. */
59        int fd;
60        /* File descriptor returned by bitlbee. we store it so we know when
61         * we're connected and when we aren't. */
62        int bfd;
63        /* When we receive a new message id, we query the handle, the body and
64         * the chatname. Store the handle and the body here so that we
65         * imcb_buddy_msg() when we got the chatname. */
66        char *handle;
67        char *body;
68        /* This is necessary because we send a notification when we get the
69         * handle. So we store the state here and then we can send a
70         * notification about the handle is in a given status. */
71        skype_call_status call_status;
72        /* Same for file transfers. */
73        skype_filetransfer_status filetransfer_status;
74};
75
76struct skype_away_state
77{
78        char *code;
79        char *full_name;
80};
81
82struct skype_buddy_ask_data
83{
84        struct im_connection *ic;
85        char *handle;
86};
87
88/*
89 * Tables
90 */
91
92const struct skype_away_state skype_away_state_list[] =
93{
94        { "ONLINE",  "Online" },
95        { "SKYPEME",  "Skype Me" },
96        { "AWAY",   "Away" },
97        { "NA",    "Not available" },
98        { "DND",      "Do Not Disturb" },
99        { "INVISIBLE",      "Invisible" },
100        { "OFFLINE",      "Offline" },
101        { NULL, NULL}
102};
103
104/*
105 * Functions
106 */
107
108static void skype_init( account_t *acc )
109{
110        set_t *s;
111
112        s = set_add( &acc->set, "port", SKYPE_PORT_DEFAULT, set_eval_int, acc );
113        s->flags |= ACC_SET_OFFLINE_ONLY;
114
115        s = set_add( &acc->set, "server", NULL, set_eval_account, acc );
116        s->flags |= ACC_SET_NOSAVE | ACC_SET_OFFLINE_ONLY;
117}
118
119int skype_write( struct im_connection *ic, char *buf, int len )
120{
121        struct skype_data *sd = ic->proto_data;
122        struct pollfd pfd[1];
123
124        pfd[0].fd = sd->fd;
125        pfd[0].events = POLLOUT;
126
127        /* This poll is necessary or we'll get a SIGPIPE when we write() to
128         * sd->fd. */
129        poll(pfd, 1, 1000);
130        if(pfd[0].revents & POLLHUP)
131        {
132                imcb_error( ic, "Could not connect to server" );
133                imc_logout( ic, TRUE );
134                return FALSE;
135        }
136        write( sd->fd, buf, len );
137
138        return TRUE;
139}
140
141static void skype_buddy_ask_yes( gpointer w, struct skype_buddy_ask_data *bla )
142{
143        char *buf = g_strdup_printf("SET USER %s ISAUTHORIZED TRUE", bla->handle);
144        skype_write( bla->ic, buf, strlen( buf ) );
145        g_free(buf);
146        g_free(bla->handle);
147        g_free(bla);
148}
149
150static void skype_buddy_ask_no( gpointer w, struct skype_buddy_ask_data *bla )
151{
152        char *buf = g_strdup_printf("SET USER %s ISAUTHORIZED FALSE", bla->handle);
153        skype_write( bla->ic, buf, strlen( buf ) );
154        g_free(buf);
155        g_free(bla->handle);
156        g_free(bla);
157}
158
159void skype_buddy_ask( struct im_connection *ic, char *handle, char *message)
160{
161        struct skype_buddy_ask_data *bla = g_new0( struct skype_buddy_ask_data, 1 );
162        char *buf;
163
164        bla->ic = ic;
165        bla->handle = g_strdup(handle);
166
167        buf = g_strdup_printf( "The user %s wants to add you to his/her buddy list, saying: '%s'.", handle, message);
168        imcb_ask( ic, buf, bla, skype_buddy_ask_yes, skype_buddy_ask_no );
169        g_free( buf );
170}
171
172struct groupchat *skype_chat_by_name( struct im_connection *ic, char *name )
173{
174        struct groupchat *ret;
175
176        for( ret = ic->conversations; ret; ret = ret->next )
177        {
178                if(strcmp(name, ret->title ) == 0 )
179                        break;
180        }
181
182        return ret;
183}
184
185static gboolean skype_read_callback( gpointer data, gint fd, b_input_condition cond )
186{
187        struct im_connection *ic = data;
188        struct skype_data *sd = ic->proto_data;
189        char buf[1024];
190        int st;
191        char **lines, **lineptr, *line, *ptr;
192
193        if( !sd || sd->fd == -1 )
194                return FALSE;
195        /* Read the whole data. */
196        st = read( sd->fd, buf, sizeof( buf ) );
197        if( st > 0 )
198        {
199                buf[st] = '\0';
200                /* Then split it up to lines. */
201                lines = g_strsplit(buf, "\n", 0);
202                lineptr = lines;
203                while((line = *lineptr))
204                {
205                        if(!strlen(line))
206                                break;
207                        if(!strncmp(line, "USERS ", 6))
208                        {
209                                char **i;
210                                char **nicks;
211
212                                nicks = g_strsplit(line + 6, ", ", 0);
213                                i = nicks;
214                                while(*i)
215                                {
216                                        g_snprintf(buf, 1024, "GET USER %s ONLINESTATUS\n", *i);
217                                        skype_write( ic, buf, strlen( buf ) );
218                                        i++;
219                                }
220                                g_strfreev(nicks);
221                        }
222                        else if(!strncmp(line, "USER ", 5))
223                        {
224                                int flags = 0;
225                                char *status = strrchr(line, ' ');
226                                char *user = strchr(line, ' ');
227                                status++;
228                                ptr = strchr(++user, ' ');
229                                *ptr = '\0';
230                                ptr++;
231                                if(!strncmp(ptr, "ONLINESTATUS ", 13) &&
232                                                strcmp(user, sd->username) != 0
233                                                && strcmp(user, "echo123") != 0)
234                                {
235                                        ptr = g_strdup_printf("%s@skype.com", user);
236                                        imcb_add_buddy(ic, ptr, NULL);
237                                        if(strcmp(status, "OFFLINE") != 0)
238                                                flags |= OPT_LOGGED_IN;
239                                        if(strcmp(status, "ONLINE") != 0 && strcmp(status, "SKYPEME") != 0)
240                                                flags |= OPT_AWAY;
241                                        imcb_buddy_status(ic, ptr, flags, NULL, NULL);
242                                        g_free(ptr);
243                                }
244                                else if(!strncmp(ptr, "RECEIVEDAUTHREQUEST ", 20))
245                                {
246                                        char *message = ptr + 20;
247                                        if(strlen(message))
248                                                skype_buddy_ask(ic, user, message);
249                                }
250                                else if(!strncmp(ptr, "BUDDYSTATUS ", 12))
251                                {
252                                        char *st = ptr + 12;
253                                        if(!strcmp(st, "3"))
254                                        {
255                                                char *buf = g_strdup_printf("%s@skype.com", user);
256                                                imcb_add_buddy(ic, buf, NULL);
257                                                g_free(buf);
258                                        }
259                                }
260                        }
261                        else if(!strncmp(line, "CHATMESSAGE ", 12))
262                        {
263                                char *id = strchr(line, ' ');
264                                if(++id)
265                                {
266                                        char *info = strchr(id, ' ');
267                                        *info = '\0';
268                                        info++;
269                                        if(!strcmp(info, "STATUS RECEIVED"))
270                                        {
271                                                /* New message ID:
272                                                 * (1) Request its from field
273                                                 * (2) Request its body
274                                                 * (3) Query chatname
275                                                 */
276                                                g_snprintf(buf, 1024, "GET CHATMESSAGE %s FROM_HANDLE\n", id);
277                                                skype_write( ic, buf, strlen( buf ) );
278                                                g_snprintf(buf, 1024, "GET CHATMESSAGE %s BODY\n", id);
279                                                skype_write( ic, buf, strlen( buf ) );
280                                                g_snprintf(buf, 1024, "GET CHATMESSAGE %s CHATNAME\n", id);
281                                                skype_write( ic, buf, strlen( buf ) );
282                                        }
283                                        else if(!strncmp(info, "FROM_HANDLE ", 12))
284                                        {
285                                                info += 12;
286                                                /* New from field value. Store
287                                                 * it, then we can later use it
288                                                 * when we got the message's
289                                                 * body. */
290                                                g_free(sd->handle);
291                                                sd->handle = g_strdup_printf("%s@skype.com", info);
292                                        }
293                                        else if(!strncmp(info, "EDITED_BY ", 10))
294                                        {
295                                                info += 10;
296                                                /* This is the same as
297                                                 * FROM_HANDLE, except that we
298                                                 * never request these lines
299                                                 * from Skype, we just get
300                                                 * them. */
301                                                g_free(sd->handle);
302                                                sd->handle = g_strdup_printf("%s@skype.com", info);
303                                        }
304                                        else if(!strncmp(info, "BODY ", 5))
305                                        {
306                                                info += 5;
307                                                g_free(sd->body);
308                                                sd->body = g_strdup(info);
309                                        }
310                                        else if(!strncmp(info, "CHATNAME ", 9))
311                                        {
312                                                info += 9;
313                                                if(sd->handle && sd->body && strlen(info))
314                                                {
315                                                        struct groupchat *gc = skype_chat_by_name(ic, info);
316                                                        if(!gc)
317                                                                /* Private message */
318                                                                imcb_buddy_msg(ic, sd->handle, sd->body, 0, 0);
319                                                        else
320                                                                /* Groupchat message */
321                                                                imcb_chat_msg(gc, sd->handle, sd->body, 0, 0);
322                                                }
323                                        }
324                                }
325                        }
326                        else if(!strncmp(line, "CALL ", 5))
327                        {
328                                char *id = strchr(line, ' ');
329                                if(++id)
330                                {
331                                        char *info = strchr(id, ' ');
332                                        *info = '\0';
333                                        info++;
334                                        if(!strcmp(info, "STATUS RINGING"))
335                                        {
336                                                g_snprintf(buf, 1024, "GET CALL %s PARTNER_HANDLE\n", id);
337                                                skype_write( ic, buf, strlen( buf ) );
338                                                sd->call_status = SKYPE_CALL_RINGING;
339                                        }
340                                        else if(!strcmp(info, "STATUS MISSED"))
341                                        {
342                                                g_snprintf(buf, 1024, "GET CALL %s PARTNER_HANDLE\n", id);
343                                                skype_write( ic, buf, strlen( buf ) );
344                                                sd->call_status = SKYPE_CALL_MISSED;
345                                        }
346                                        else if(!strncmp(info, "PARTNER_HANDLE ", 15))
347                                        {
348                                                info += 15;
349                                                if(sd->call_status) {
350                                                        switch(sd->call_status)
351                                                        {
352                                                                case SKYPE_CALL_RINGING:
353                                                                        imcb_log(ic, "The user %s is currently ringing you.", info);
354                                                                        break;
355                                                                case SKYPE_CALL_MISSED:
356                                                                        imcb_log(ic, "You have missed a call from user %s.", info);
357                                                                        break;
358                                                        }
359                                                        sd->call_status = 0;
360                                                }
361                                        }
362                                }
363                        }
364                        else if(!strncmp(line, "FILETRANSFER ", 13))
365                        {
366                                char *id = strchr(line, ' ');
367                                if(++id)
368                                {
369                                        char *info = strchr(id, ' ');
370                                        *info = '\0';
371                                        info++;
372                                        if(!strcmp(info, "STATUS NEW"))
373                                        {
374                                                g_snprintf(buf, 1024, "GET FILETRANSFER %s PARTNER_HANDLE\n", id);
375                                                skype_write( ic, buf, strlen( buf ) );
376                                                sd->filetransfer_status = SKYPE_FILETRANSFER_NEW;
377                                        }
378                                        else if(!strcmp(info, "STATUS FAILED"))
379                                        {
380                                                g_snprintf(buf, 1024, "GET FILETRANSFER %s PARTNER_HANDLE\n", id);
381                                                skype_write( ic, buf, strlen( buf ) );
382                                                sd->filetransfer_status = SKYPE_FILETRANSFER_FAILED;
383                                        }
384                                        else if(!strncmp(info, "PARTNER_HANDLE ", 15))
385                                        {
386                                                info += 15;
387                                                if(sd->filetransfer_status) {
388                                                        switch(sd->filetransfer_status)
389                                                        {
390                                                                case SKYPE_FILETRANSFER_NEW:
391                                                                        imcb_log(ic, "The user %s offered a new file for you.", info);
392                                                                        break;
393                                                                case SKYPE_FILETRANSFER_FAILED:
394                                                                        imcb_log(ic, "Failed to transfer file from user %s.", info);
395                                                                        break;
396                                                        }
397                                                        sd->filetransfer_status = 0;
398                                                }
399                                        }
400                                }
401                        }
402                        else if(!strncmp(line, "CHAT ", 5))
403                        {
404                                char *id = strchr(line, ' ');
405                                if(++id)
406                                {
407                                        char *info = strchr(id, ' ');
408                                        *info = '\0';
409                                        info++;
410                                        if(!strcmp(info, "STATUS MULTI_SUBSCRIBED"))
411                                        {
412                                                struct groupchat *gc;
413                                                gc = imcb_chat_new( ic, id );
414                                        }
415                                        else if(!strncmp(info, "ACTIVEMEMBERS ", 14))
416                                        {
417                                                info += 14;
418                                                struct groupchat *gc = skype_chat_by_name(ic, id);
419                                                if(gc)
420                                                {
421                                                        char **members = g_strsplit(info, " ", 0);
422                                                        int i;
423                                                        for(i=0;members[i];i++)
424                                                        {
425                                                                if(!strcmp(members[i], sd->username))
426                                                                        continue;
427                                                                g_snprintf(buf, 1024, "%s@skype.com", members[i]);
428                                                                imcb_chat_add_buddy(gc, buf);
429                                                        }
430                                                        imcb_chat_add_buddy(gc, sd->username);
431                                                        g_strfreev(members);
432                                                }
433                                        }
434                                }
435                        }
436                        lineptr++;
437                }
438                g_strfreev(lines);
439        }
440        else if( st == 0 || ( st < 0 && !sockerr_again() ) )
441        {
442                closesocket( sd->fd );
443                sd->fd = -1;
444
445                imcb_error( ic, "Error while reading from server" );
446                imc_logout( ic, TRUE );
447                return FALSE;
448        }
449        return TRUE;
450}
451
452gboolean skype_start_stream( struct im_connection *ic )
453{
454        struct skype_data *sd = ic->proto_data;
455        char *buf;
456        int st;
457
458        if(!sd)
459                return FALSE;
460
461        if( sd->bfd <= 0 )
462                sd->bfd = b_input_add( sd->fd, GAIM_INPUT_READ, skype_read_callback, ic );
463
464        /* This will download all buddies. */
465        buf = g_strdup_printf("SEARCH FRIENDS\n");
466        st = skype_write( ic, buf, strlen( buf ) );
467        g_free(buf);
468        buf = g_strdup_printf("SET USERSTATUS ONLINE\n");
469        skype_write( ic, buf, strlen( buf ) );
470        g_free(buf);
471        return st;
472}
473
474gboolean skype_connected( gpointer data, gint source, b_input_condition cond )
475{
476        struct im_connection *ic = data;
477        imcb_connected(ic);
478        return skype_start_stream(ic);
479}
480
481static void skype_login( account_t *acc )
482{
483        struct im_connection *ic = imcb_new( acc );
484        struct skype_data *sd = g_new0( struct skype_data, 1 );
485
486        ic->proto_data = sd;
487
488        imcb_log( ic, "Connecting" );
489        sd->fd = proxy_connect(acc->server, set_getint( &acc->set, "port" ), skype_connected, ic );
490        sd->username = g_strdup( acc->user );
491
492        sd->ic = ic;
493}
494
495static void skype_logout( struct im_connection *ic )
496{
497        struct skype_data *sd = ic->proto_data;
498        char *buf;
499
500        buf = g_strdup_printf("SET USERSTATUS OFFLINE\n");
501        skype_write( ic, buf, strlen( buf ) );
502        g_free(buf);
503
504        g_free(sd->username);
505        g_free(sd->handle);
506        g_free(sd);
507        ic->proto_data = NULL;
508}
509
510static int skype_buddy_msg( struct im_connection *ic, char *who, char *message, int flags )
511{
512        char *buf, *ptr, *nick;
513        int st;
514
515        nick = g_strdup(who);
516        ptr = strchr(nick, '@');
517        if(ptr)
518                *ptr = '\0';
519
520        buf = g_strdup_printf("MESSAGE %s %s\n", nick, message);
521        g_free(nick);
522        st = skype_write( ic, buf, strlen( buf ) );
523        g_free(buf);
524
525        return st;
526}
527
528const struct skype_away_state *skype_away_state_by_name( char *name )
529{
530        int i;
531
532        for( i = 0; skype_away_state_list[i].full_name; i ++ )
533                if( g_strcasecmp( skype_away_state_list[i].full_name, name ) == 0 )
534                        return( skype_away_state_list + i );
535
536        return NULL;
537}
538
539static void skype_set_away( struct im_connection *ic, char *state_txt, char *message )
540{
541        const struct skype_away_state *state;
542        char *buf;
543
544        if( strcmp( state_txt, GAIM_AWAY_CUSTOM ) == 0 )
545                state = skype_away_state_by_name( "Away" );
546        else
547                state = skype_away_state_by_name( state_txt );
548        buf = g_strdup_printf("SET USERSTATUS %s\n", state->code);
549        skype_write( ic, buf, strlen( buf ) );
550        g_free(buf);
551}
552
553static GList *skype_away_states( struct im_connection *ic )
554{
555        GList *l = NULL;
556        int i;
557       
558        for( i = 0; skype_away_state_list[i].full_name; i ++ )
559                l = g_list_append( l, (void*) skype_away_state_list[i].full_name );
560       
561        return l;
562}
563
564static void skype_add_buddy( struct im_connection *ic, char *who, char *group )
565{
566        char *buf, *nick, *ptr;
567
568        nick = g_strdup(who);
569        ptr = strchr(nick, '@');
570        if(ptr)
571                *ptr = '\0';
572        buf = g_strdup_printf("SET USER %s BUDDYSTATUS 2 Please authorize me\n", nick);
573        skype_write( ic, buf, strlen( buf ) );
574        g_free(nick);
575}
576
577static void skype_remove_buddy( struct im_connection *ic, char *who, char *group )
578{
579        char *buf, *nick, *ptr;
580
581        nick = g_strdup(who);
582        ptr = strchr(nick, '@');
583        if(ptr)
584                *ptr = '\0';
585        buf = g_strdup_printf("SET USER %s BUDDYSTATUS 1\n", nick);
586        skype_write( ic, buf, strlen( buf ) );
587        g_free(nick);
588}
589
590void skype_chat_msg( struct groupchat *gc, char *message, int flags )
591{
592        struct im_connection *ic = gc->ic;
593        char *buf;
594        buf = g_strdup_printf("CHATMESSAGE %s %s\n", gc->title, message);
595        skype_write( ic, buf, strlen( buf ) );
596        g_free(buf);
597}
598
599void init_plugin(void)
600{
601        struct prpl *ret = g_new0( struct prpl, 1 );
602
603        ret->name = "skype";
604        ret->login = skype_login;
605        ret->init = skype_init;
606        ret->logout = skype_logout;
607        ret->buddy_msg = skype_buddy_msg;
608        ret->away_states = skype_away_states;
609        ret->set_away = skype_set_away;
610        ret->add_buddy = skype_add_buddy;
611        ret->remove_buddy = skype_remove_buddy;
612        ret->chat_msg = skype_chat_msg;
613        ret->handle_cmp = g_strcasecmp;
614        register_protocol( ret );
615}
Note: See TracBrowser for help on using the repository browser.