source: skype/skype.c @ f080961

Last change on this file since f080961 was 5d1b0774, checked in by VMiklos <vmiklos@…>, at 2007-09-03T22:30:15Z

support for edited messages

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