source: skype/skype.c @ 368861e

Last change on this file since 368861e was 368861e, checked in by VMiklos <vmiklos@…>, at 2007-09-21T14:16:48Z

skype_data struct: some more comments
also renmae r_inpa to bfd

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