source: skype/skype.c @ 2d07803

Last change on this file since 2d07803 was 2d07803, checked in by VMiklos <vmiklos@…>, at 2007-10-06T16:05:44Z

handle topic changes

  • Property mode set to 100644
File size: 13.8 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 * Enumerations
34 */
35
36typedef enum
37{
38        SKYPE_CALL_RINGING = 1,
39        SKYPE_CALL_MISSED
40} skype_call_status;
41
42typedef enum
43{
44        SKYPE_FILETRANSFER_NEW = 1,
45        SKYPE_FILETRANSFER_FAILED
46} skype_filetransfer_status;
47
48/*
49 * Structures
50 */
51
52struct skype_data
53{
54        struct im_connection *ic;
55        char *username;
56        /* The effective file descriptor. We store it here so any function can
57         * write() to it. */
58        int fd;
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;
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. */
65        char *handle;
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;
70        /* Same for file transfers. */
71        skype_filetransfer_status filetransfer_status;
72        /* True if the next message will be a topic */
73        int topic;
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
172static gboolean skype_read_callback( gpointer data, gint fd, b_input_condition cond )
173{
174        struct im_connection *ic = data;
175        struct skype_data *sd = ic->proto_data;
176        char buf[1024];
177        int st;
178        char **lines, **lineptr, *line, *ptr;
179
180        if( !sd || sd->fd == -1 )
181                return FALSE;
182        /* Read the whole data. */
183        st = read( sd->fd, buf, sizeof( buf ) );
184        if( st > 0 )
185        {
186                buf[st] = '\0';
187                /* Then split it up to lines. */
188                lines = g_strsplit(buf, "\n", 0);
189                lineptr = lines;
190                while((line = *lineptr))
191                {
192                        if(!strlen(line))
193                                break;
194                        if(!strncmp(line, "USERS ", 6))
195                        {
196                                char **i;
197                                char **nicks;
198
199                                nicks = g_strsplit(line + 6, ", ", 0);
200                                i = nicks;
201                                while(*i)
202                                {
203                                        g_snprintf(buf, 1024, "GET USER %s ONLINESTATUS\n", *i);
204                                        skype_write( ic, buf, strlen( buf ) );
205                                        i++;
206                                }
207                                g_strfreev(nicks);
208                        }
209                        else if(!strncmp(line, "USER ", 5))
210                        {
211                                int flags = 0;
212                                char *status = strrchr(line, ' ');
213                                char *user = strchr(line, ' ');
214                                status++;
215                                ptr = strchr(++user, ' ');
216                                *ptr = '\0';
217                                ptr++;
218                                if(!strncmp(ptr, "ONLINESTATUS ", 13) &&
219                                                strcmp(user, sd->username) != 0
220                                                && strcmp(user, "echo123") != 0)
221                                {
222                                        ptr = g_strdup_printf("%s@skype.com", user);
223                                        imcb_add_buddy(ic, ptr, NULL);
224                                        if(strcmp(status, "OFFLINE") != 0)
225                                                flags |= OPT_LOGGED_IN;
226                                        if(strcmp(status, "ONLINE") != 0 && strcmp(status, "SKYPEME") != 0)
227                                                flags |= OPT_AWAY;
228                                        imcb_buddy_status(ic, ptr, flags, NULL, NULL);
229                                        g_free(ptr);
230                                }
231                                else if(!strncmp(ptr, "RECEIVEDAUTHREQUEST ", 20))
232                                {
233                                        char *message = ptr + 20;
234                                        if(strlen(message))
235                                                skype_buddy_ask(ic, user, message);
236                                }
237                                else if(!strncmp(ptr, "BUDDYSTATUS ", 12))
238                                {
239                                        char *st = ptr + 12;
240                                        if(!strcmp(st, "3"))
241                                        {
242                                                char *buf = g_strdup_printf("%s@skype.com", user);
243                                                imcb_add_buddy(ic, buf, NULL);
244                                                g_free(buf);
245                                        }
246                                }
247                        }
248                        else if(!strncmp(line, "CHATMESSAGE ", 12))
249                        {
250                                char *id = strchr(line, ' ');
251                                if(++id)
252                                {
253                                        char *info = strchr(id, ' ');
254                                        *info = '\0';
255                                        info++;
256                                        if(!strcmp(info, "STATUS RECEIVED"))
257                                        {
258                                                /* New message ID:
259                                                 * (1) Request its from field
260                                                 * (2) Request its body
261                                                 * (3) Mark it as seen
262                                                 */
263                                                g_snprintf(buf, 1024, "GET CHATMESSAGE %s FROM_HANDLE\n", id);
264                                                skype_write( ic, buf, strlen( buf ) );
265                                                g_snprintf(buf, 1024, "GET CHATMESSAGE %s BODY\n", id);
266                                                skype_write( ic, buf, strlen( buf ) );
267                                                g_snprintf(buf, 1024, "SET CHATMESSAGE %s SEEN\n", id);
268                                                skype_write( ic, buf, strlen( buf ) );
269                                        }
270                                        else if(!strncmp(info, "FROM_HANDLE ", 12))
271                                        {
272                                                info += 12;
273                                                /* New from field value. Store
274                                                 * it, then we can later use it
275                                                 * when we got the message's
276                                                 * body. */
277                                                g_free(sd->handle);
278                                                sd->handle = g_strdup_printf("%s@skype.com", info);
279                                        }
280                                        else if(!strncmp(info, "EDITED_BY ", 10))
281                                        {
282                                                info += 10;
283                                                /* This is the same as
284                                                 * FROM_HANDLE, except that we
285                                                 * never request these lines
286                                                 * from Skype, we just get
287                                                 * them. */
288                                                g_free(sd->handle);
289                                                sd->handle = g_strdup_printf("%s@skype.com", info);
290                                        }
291                                        else if(!strncmp(info, "BODY ", 5))
292                                        {
293                                                info += 5;
294                                                if(sd->handle && strlen(info))
295                                                {
296                                                        /* New body, we have everything to use imcb_buddy_msg() now! */
297                                                        if(sd->topic)
298                                                        {
299                                                                imcb_log(ic, "%s has changed the chat topic to \"%s\"", sd->handle, info);
300                                                                sd->topic = 0;
301                                                        }
302                                                        else
303                                                                imcb_buddy_msg(ic, sd->handle, info, 0, 0);
304                                                }
305                                        }
306                                }
307                        }
308                        else if(!strncmp(line, "CALL ", 5))
309                        {
310                                char *id = strchr(line, ' ');
311                                if(++id)
312                                {
313                                        char *info = strchr(id, ' ');
314                                        *info = '\0';
315                                        info++;
316                                        if(!strcmp(info, "STATUS RINGING"))
317                                        {
318                                                g_snprintf(buf, 1024, "GET CALL %s PARTNER_HANDLE\n", id);
319                                                skype_write( ic, buf, strlen( buf ) );
320                                                sd->call_status = SKYPE_CALL_RINGING;
321                                        }
322                                        else if(!strcmp(info, "STATUS MISSED"))
323                                        {
324                                                g_snprintf(buf, 1024, "GET CALL %s PARTNER_HANDLE\n", id);
325                                                skype_write( ic, buf, strlen( buf ) );
326                                                sd->call_status = SKYPE_CALL_MISSED;
327                                        }
328                                        else if(!strncmp(info, "PARTNER_HANDLE ", 15))
329                                        {
330                                                info += 15;
331                                                if(sd->call_status) {
332                                                        switch(sd->call_status)
333                                                        {
334                                                                case SKYPE_CALL_RINGING:
335                                                                        imcb_log(ic, "The user %s is currently ringing you.", info);
336                                                                        break;
337                                                                case SKYPE_CALL_MISSED:
338                                                                        imcb_log(ic, "You have missed a call from user %s.", info);
339                                                                        break;
340                                                        }
341                                                        sd->call_status = 0;
342                                                }
343                                        }
344                                }
345                        }
346                        else if(!strncmp(line, "FILETRANSFER ", 13))
347                        {
348                                char *id = strchr(line, ' ');
349                                if(++id)
350                                {
351                                        char *info = strchr(id, ' ');
352                                        *info = '\0';
353                                        info++;
354                                        if(!strcmp(info, "STATUS NEW"))
355                                        {
356                                                g_snprintf(buf, 1024, "GET FILETRANSFER %s PARTNER_HANDLE\n", id);
357                                                skype_write( ic, buf, strlen( buf ) );
358                                                sd->filetransfer_status = SKYPE_FILETRANSFER_NEW;
359                                        }
360                                        else if(!strcmp(info, "STATUS FAILED"))
361                                        {
362                                                g_snprintf(buf, 1024, "GET FILETRANSFER %s PARTNER_HANDLE\n", id);
363                                                skype_write( ic, buf, strlen( buf ) );
364                                                sd->filetransfer_status = SKYPE_FILETRANSFER_FAILED;
365                                        }
366                                        else if(!strncmp(info, "PARTNER_HANDLE ", 15))
367                                        {
368                                                info += 15;
369                                                if(sd->filetransfer_status) {
370                                                        switch(sd->filetransfer_status)
371                                                        {
372                                                                case SKYPE_FILETRANSFER_NEW:
373                                                                        imcb_log(ic, "The user %s offered a new file for you.", info);
374                                                                        break;
375                                                                case SKYPE_FILETRANSFER_FAILED:
376                                                                        imcb_log(ic, "Failed to transfer file from user %s.", info);
377                                                                        break;
378                                                        }
379                                                        sd->filetransfer_status = 0;
380                                                }
381                                        }
382                                }
383                        }
384                        else if(!strncmp(line, "CHAT ", 5))
385                        {
386                                char *id = strchr(line, ' ');
387                                if(++id)
388                                {
389                                        char *info = strchr(id, ' ');
390                                        *info = '\0';
391                                        info++;
392                                        if(!strncmp(info, "TOPIC ", 6))
393                                                sd->topic = 1;
394                                }
395                        }
396                        lineptr++;
397                }
398                g_strfreev(lines);
399        }
400        else if( st == 0 || ( st < 0 && !sockerr_again() ) )
401        {
402                closesocket( sd->fd );
403                sd->fd = -1;
404
405                imcb_error( ic, "Error while reading from server" );
406                imc_logout( ic, TRUE );
407                return FALSE;
408        }
409        return TRUE;
410}
411
412gboolean skype_start_stream( struct im_connection *ic )
413{
414        struct skype_data *sd = ic->proto_data;
415        char *buf;
416        int st;
417
418        if(!sd)
419                return FALSE;
420
421        if( sd->bfd <= 0 )
422                sd->bfd = b_input_add( sd->fd, GAIM_INPUT_READ, skype_read_callback, ic );
423
424        /* This will download all buddies. */
425        buf = g_strdup_printf("SEARCH FRIENDS\n");
426        st = skype_write( ic, buf, strlen( buf ) );
427        g_free(buf);
428        buf = g_strdup_printf("SET USERSTATUS ONLINE\n");
429        skype_write( ic, buf, strlen( buf ) );
430        g_free(buf);
431        return st;
432}
433
434gboolean skype_connected( gpointer data, gint source, b_input_condition cond )
435{
436        struct im_connection *ic = data;
437        imcb_connected(ic);
438        return skype_start_stream(ic);
439}
440
441static void skype_login( account_t *acc )
442{
443        struct im_connection *ic = imcb_new( acc );
444        struct skype_data *sd = g_new0( struct skype_data, 1 );
445
446        ic->proto_data = sd;
447
448        imcb_log( ic, "Connecting" );
449        sd->fd = proxy_connect(acc->server, set_getint( &acc->set, "port" ), skype_connected, ic );
450        sd->username = g_strdup( acc->user );
451
452        sd->ic = ic;
453}
454
455static void skype_logout( struct im_connection *ic )
456{
457        struct skype_data *sd = ic->proto_data;
458        char *buf;
459
460        buf = g_strdup_printf("SET USERSTATUS OFFLINE\n");
461        skype_write( ic, buf, strlen( buf ) );
462        g_free(buf);
463
464        g_free(sd->username);
465        g_free(sd->handle);
466        g_free(sd);
467        ic->proto_data = NULL;
468}
469
470static int skype_buddy_msg( struct im_connection *ic, char *who, char *message, int flags )
471{
472        char *buf, *ptr, *nick;
473        int st;
474
475        nick = g_strdup(who);
476        ptr = strchr(nick, '@');
477        if(ptr)
478                *ptr = '\0';
479
480        buf = g_strdup_printf("MESSAGE %s %s\n", nick, message);
481        g_free(nick);
482        st = skype_write( ic, buf, strlen( buf ) );
483        g_free(buf);
484
485        return st;
486}
487
488const struct skype_away_state *skype_away_state_by_name( char *name )
489{
490        int i;
491
492        for( i = 0; skype_away_state_list[i].full_name; i ++ )
493                if( g_strcasecmp( skype_away_state_list[i].full_name, name ) == 0 )
494                        return( skype_away_state_list + i );
495
496        return NULL;
497}
498
499static void skype_set_away( struct im_connection *ic, char *state_txt, char *message )
500{
501        const struct skype_away_state *state;
502        char *buf;
503
504        if( strcmp( state_txt, GAIM_AWAY_CUSTOM ) == 0 )
505                state = skype_away_state_by_name( "Away" );
506        else
507                state = skype_away_state_by_name( state_txt );
508        buf = g_strdup_printf("SET USERSTATUS %s\n", state->code);
509        skype_write( ic, buf, strlen( buf ) );
510        g_free(buf);
511}
512
513static GList *skype_away_states( struct im_connection *ic )
514{
515        GList *l = NULL;
516        int i;
517       
518        for( i = 0; skype_away_state_list[i].full_name; i ++ )
519                l = g_list_append( l, (void*) skype_away_state_list[i].full_name );
520       
521        return l;
522}
523
524static void skype_add_buddy( struct im_connection *ic, char *who, char *group )
525{
526        char *buf, *nick, *ptr;
527
528        nick = g_strdup(who);
529        ptr = strchr(nick, '@');
530        if(ptr)
531                *ptr = '\0';
532        buf = g_strdup_printf("SET USER %s BUDDYSTATUS 2 Please authorize me\n", nick);
533        skype_write( ic, buf, strlen( buf ) );
534        g_free(nick);
535}
536
537static void skype_remove_buddy( struct im_connection *ic, char *who, char *group )
538{
539        char *buf, *nick, *ptr;
540
541        nick = g_strdup(who);
542        ptr = strchr(nick, '@');
543        if(ptr)
544                *ptr = '\0';
545        buf = g_strdup_printf("SET USER %s BUDDYSTATUS 1\n", nick);
546        skype_write( ic, buf, strlen( buf ) );
547        g_free(nick);
548}
549
550void init_plugin(void)
551{
552        struct prpl *ret = g_new0( struct prpl, 1 );
553
554        ret->name = "skype";
555        ret->login = skype_login;
556        ret->init = skype_init;
557        ret->logout = skype_logout;
558        ret->buddy_msg = skype_buddy_msg;
559        ret->away_states = skype_away_states;
560        ret->set_away = skype_set_away;
561        ret->add_buddy = skype_add_buddy;
562        ret->remove_buddy = skype_remove_buddy;
563        ret->handle_cmp = g_strcasecmp;
564        register_protocol( ret );
565}
Note: See TracBrowser for help on using the repository browser.