source: skype/skype.c @ 5a61e43f

Last change on this file since 5a61e43f was 5a61e43f, checked in by VMiklos <vmiklos@…>, at 2007-10-06T20:03:49Z

revert "handle topic changes"

SETTOPIC, then do the same

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