source: skype/skype.c @ 56b56478

Last change on this file since 56b56478 was 7daec06, checked in by VMiklos <vmiklos@…>, at 2007-08-21T17:02:11Z

cosmetics

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