source: skype/skype.c @ a75f2a7

Last change on this file since a75f2a7 was df9255d, checked in by VMiklos <vmiklos@…>, at 2007-10-06T15:16:06Z

notification when somebody wants to transfer a file

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